DCC++ Ethernet & Wifi Shield

Jean-Eric Jan 25, 2016

  1. Diego Pamio

    Diego Pamio TrainBoard Member

    10
    11
    2
    @UK Steve do you have any of the experimental code in GitHub so we can take a sneak peek?
    I've purchased an ESP8266 that, with just 4 wires can be connected to my Arduino Mega Base Station and have it working now.

    Now I will need to implement the WebSocket server on it so I can connect from an HTML/Javascript page.

    @Dago : one small thing to consider when using the arduino as a mean to connect to the serial of the ESP is that, if you use the arduino serial monitor, you need to set it to use both CR-LF otherwise you'll only see an echo of your AT commands. It took me almost the entire afternoon to resolve it.
     
    Scott Eric Catalano and Dago like this.
  2. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    This code sets up an ESP8266 as a TCP to Serial and Websocket to Serial interface, you can run 1 TCP and up to 3 Websocket connections simultaneously.
    The project is well documented in the relative DCC++ threads, mainly the Throttle thread.
    OTA flash implementation code is included in this version.

    Code:
    #include <ESP8266WiFi.h>
    #include <ESP8266mDNS.h>
    #include <WiFiUdp.h>
    #include <ArduinoOTA.h>
    #include <FS.h>
    #include <Hash.h>
    #include <ESPAsyncTCP.h>
    #include <ESPAsyncWebServer.h>
    
    // SKETCH BEGIN
    AsyncWebServer server(80);
    WiFiServer TcpServer(23);
    WiFiClient Client;
    
    AsyncWebSocket webSocket("/ws");
    
    void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) {
      if (type == WS_EVT_CONNECT) {
        //os_printf("ws[%s][%u] connect\n", server->url(), client->id());
        client->printf("Welcome. Server Ready %u", client->id());
    
      } else if (type == WS_EVT_DATA) {
        for (size_t i = 0; i < len; i++) {
          Serial.write(data[i]);
        }
      }
    }
    //IPAddress ip(101,101,10,10); //Change to suit your config for a Static IP
    //IPAddress gateway(101,101,10,1);
    //IPAddress subnet(255,255,255,0);
    
    const char* ssid = "YOUR SSID";
    const char* password = "Password";
    
    String reply = "";
    
    extern "C" void system_set_os_print(uint8 onoff);
    extern "C" void ets_install_putc1(void* routine);
    
    //Use the internal hardware buffer
    static void _u0_putc(char c) {
      while (((U0S >> USTXC) & 0x7F) == 0x7F);
      U0F = c;
    }
    
    void initSerial() {
      Serial.begin(115200);
      ets_install_putc1((void *) &_u0_putc);
      system_set_os_print(1);
    }
    
    void setup() {
      initSerial();
      WiFi.mode(WIFI_STA);
      //WiFi.config(ip, gateway, subnet);//Uncomment to initiate a static IP
      WiFi.begin(ssid, password);
      if (WiFi.waitForConnectResult() != WL_CONNECTED) {
        Serial.printf("STA: Failed!\n");
        WiFi.disconnect(false);
        delay(1000);
        WiFi.begin(ssid, password);
      }
      // Port defaults to 8266
      ArduinoOTA.setPort(8266);
    
      // Hostname defaults to esp8266-[ChipID]
      ArduinoOTA.setHostname("myesp8266");
    
      // No authentication by default
      //ArduinoOTA.setPassword((const char *)"123");
    
      ArduinoOTA.onStart([]() {
        Serial.println("Start");
      });
      ArduinoOTA.onEnd([]() {
        Serial.println("\nEnd");
      });
      ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
        Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
      });
      ArduinoOTA.onError([](ota_error_t error) {
        Serial.printf("Error[%u]: ", error);
        if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
        else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
        else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
        else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
        else if (error == OTA_END_ERROR) Serial.println("End Failed");
      });
      ArduinoOTA.begin();
    
      reply.reserve(80);
    
      webSocket.onEvent(onEvent);
      server.addHandler(&webSocket);
    
      server.begin();
      TcpServer.begin();
    }
    
    void loop() {
    
      if (TcpServer.hasClient()) {
        if (!Client || !Client.connected()) {
          if (Client) Client.stop();
          Client = TcpServer.available();
        } else {
          TcpServer.available().stop();
        }
      }
      if (Client && Client.connected() && Client.available()) {
        while (Client.available())
          Serial.write(Client.read());
      }
    
    
      while (Serial.available()) {
        char inChar = (char)Serial.read();
        reply += inChar;
    
        if (inChar == '>') {
          webSocket.textAll(reply);
          Client.print(reply);
          reply = "";
        }
      }
      ArduinoOTA.handle();
    }
    
     
    Scott Eric Catalano likes this.
  3. Diego Pamio

    Diego Pamio TrainBoard Member

    10
    11
    2
    Yahooooo!!! Look mom, no wires! I was able to setup the ESP8266 with the WebSockets firmware, then used the Web Throttle: http://trainelectronics.com/WiFi-esp8266/Setup2/indexDCC++/indexDCC.html, worked like charm.

    I used the http://trainelectronics.com/WiFi-esp8266/Setup/ procedure with the code posted there. Did I do it right? I reviewed the code for the ESP and looks like it is very "trottle-oriented", and doesn't provide an interface for accessories, sensors or actuators. Is that so?

    As I'm a (kind of) seasoned Web Developer, I'll probably make a pure web app DCC controller (porting the one done in Processing), if no one has done it already.

    @UK Steve I still don't understand how to mix OTA Flash (the code you provided in the previous comment) with the actual code that translates Web Socket messages to serial commands in the ESP.
     
    Scott Eric Catalano likes this.
  4. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Diego,

    OTA (Over the Air) update is the process of loading the firmware to ESP module using Wi-Fi connection rather that a serial port.
    Such functionality became extremely useful in case of limited or no physical access to the module.

    Read the rest here http://esp8266.github.io/Arduino/versions/2.0.0/doc/ota_updates/ota_updates.html

    It's a great time saving method you might like to use while developing ESP8266 code. You can disable it by commenting out the line...... ArduinoOTA.begin();

    If you have managed to load the Server code successfully and use the WebThrottle as you describe then everything is working as I designed.

    I'm not sure what you mean by "throttle-oriented", and doesn't provide an interface for accessories...............

    It provides precisely that interface, you could connect JMRI or Gregg's DCC++ Controller apps with an ordinary TCP connection, without any further modification to the code.
    It is a Server providing 2 types of connection protocols, the rest is in the software you connect to it..

    Have fun.

    Steve.
     
    Scott Eric Catalano likes this.
  5. Diego Pamio

    Diego Pamio TrainBoard Member

    10
    11
    2
    Ouch, sorry, I didn't see where it handles "<a" commands, for instance.

    So, which messages should I send from my WebSocket client to the wifi socket server to activate accesories?
     
    Scott Eric Catalano likes this.
  6. Diego Pamio

    Diego Pamio TrainBoard Member

    10
    11
    2
    I see now that our confusion is because I'm talking about the esp program described here by Dave Bodnar: http://trainelectronics.com/WiFi-esp8266/Setup/

    There, specific parsing for "stop" "T1" etc. is done, which is different from the code you posted in this thread, which looks more generic.
     
    Scott Eric Catalano likes this.
  7. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12

    OK, now I'm with you.

    The code over on Dave's site is somewhat depreciated. That was some of my first attempts at coding the ESP and was a mix of server and command decoder in one.

    As I've only been coding since February this year, I knew nothing of how to approach such a project.

    I moved on to writing the Html/Javascript pages that sent the DCC++ commands directly. The Server just passes those commands through to the Serial line,

    But importantly also handles the Websocket connections.

    So you would need to write an app or webpage that does what you'd want for switching turnouts etc.

    The list of commands and structure is here https://github.com/DccPlusPlus/BaseStation/wiki/Commands-for-DCCpp-BaseStation

    Steve.
     
    Scott Eric Catalano likes this.
  8. Diego Pamio

    Diego Pamio TrainBoard Member

    10
    11
    2
    Scott Eric Catalano likes this.
  9. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    @Diego,

    There are examples of my later work post#1275 here http://www.trainboard.com/highball/...ource-dcc-station-and-interface.84800/page-64

    That page will look something like this, which runs on the latest server code.

    Please don't ask for support, I've moved on.

    Are you just another copy and paste developer ???

    Publish some of YOUR work and you might grab my attention.

    Follow some of the current stuff from here http://www.trainboard.com/highball/...ource-dcc-station-and-interface.84800/page-71

    Screenshot_2016-07-21-15-46-50.png
     
    Last edited: Sep 16, 2016
    Scott Eric Catalano likes this.
  10. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
  11. EFA Train Guy

    EFA Train Guy TrainBoard Member

    12
    0
    4
    I got a MEGA+WiFi R3 ATmega2560+ESP8266, flash 32Mb, USB-TTL CH340G, Micro-USB like this and am hoping to get it to run my layout and connect to my phone & tab without any computer.

    I have not gotten a chance to access WiFi to see if I can program but I was able to get the mega and motor shield working and connecting to JMRI.

    I will keep you posted.

    EFA Train Guy
     
  12. Atani

    Atani TrainBoard Member

    1,460
    1,698
    36
    you should be able to flash my forked base station on this and have it available on WiFi...

    https://github.com/atanisoft/BaseStation
     
  13. LMSFan72

    LMSFan72 TrainBoard Member

    118
    18
    9
  14. TexasRailroader

    TexasRailroader TrainBoard Member

    90
    26
    7
  15. Atani

    Atani TrainBoard Member

    1,460
    1,698
    36
    In theory it should work, there are a few libraries to enable the WiFi side of things but it should work the same as the Ethernet support I believe.
     
  16. TexasRailroader

    TexasRailroader TrainBoard Member

    90
    26
    7
    While attempting to upload the DCC++ sketch to the Uno wifi, the sketch indicated it will only work with the Uno or Mega.
    I suppose until some additional code is written, I may not be able to use this.
     
  17. Atani

    Atani TrainBoard Member

    1,460
    1,698
    36
    You can likely fix this by editing DCCpp.ino to rename ARDUINO_AVR_UNO to ARDUINO_AVR_UNO_WIFI_REV2 and the same update in any other files that might have the ARDUINO_AVR_UNO checks.
     
    Jimbo20 likes this.
  18. Jimbo20

    Jimbo20 TrainBoard Member

    274
    178
    11
    ^ Agreed! I had to do a similar edit in order to load DCC++ onto the Nano.
     
  19. TexasRailroader

    TexasRailroader TrainBoard Member

    90
    26
    7
    Changed ARDUINO_AVR_UNO to ARDUINO_AVR_UNO_WIFI_REV2 then ran the upload.
    Got this error message that started like this:

    Arduino: 1.8.9 (Windows 10), Board: "Arduino Uno WiFi Rev2, ATMEGA328"
    In file included from C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/ArduinoAPI.h:52:0,
    from C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/Arduino.h:23,
    from sketch\DCCpp_Uno.ino.cpp:1:
    C:\DCC++2\BaseStation-master\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino: In function 'void setup()':
    DCCpp_Uno:289:10: error: 'TCCR1A' was not declared in this scope
    bitSet(TCCR1A,WGM10); // set Timer 1 to FAST PWM, with TOP=OCR1A
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:30: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~~~
    C:\DCC++2\BaseStation-master\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino:289:10: note: suggested alternative: 'TCB1'
    bitSet(TCCR1A,WGM10); // set Timer 1 to FAST PWM, with TOP=OCR1A
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:30: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~~~
    DCCpp_Uno:289:17: error: 'WGM10' was not declared in this scope
    bitSet(TCCR1A,WGM10); // set Timer 1 to FAST PWM, with TOP=OCR1A
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:49: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~
    DCCpp_Uno:290:17: error: 'WGM11' was not declared in this scope
    bitSet(TCCR1A,WGM11);
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:49: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~
    DCCpp_Uno:291:10: error: 'TCCR1B' was not declared in this scope
    bitSet(TCCR1B,WGM12);
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:30: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~~~
    C:\DCC++2\BaseStation-master\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino:291:10: note: suggested alternative: 'TCB1'
    bitSet(TCCR1B,WGM12);
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:30: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~~~
    DCCpp_Uno:291:17: error: 'WGM12' was not declared in this scope
    bitSet(TCCR1B,WGM12);
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:49: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~
    DCCpp_Uno:292:17: error: 'WGM13' was not declared in this scope
    bitSet(TCCR1B,WGM13);
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:49: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~
    DCCpp_Uno:294:17: error: 'COM1B1' was not declared in this scope
    bitSet(TCCR1A,COM1B1); // set Timer 1, OC1B (pin 10/UNO, pin 12/MEGA) to inverting toggle (actual direction is arbitrary)
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:49: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~
    DCCpp_Uno:295:17: error: 'COM1B0' was not declared in this scope
    bitSet(TCCR1A,COM1B0);
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:49: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~
    DCCpp_Uno:297:19: error: 'CS12' was not declared in this scope
    bitClear(TCCR1B,CS12); // set Timer 1 prescale=1
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:78:52: note: in definition of macro 'bitClear'
    #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
    ^~~
    DCCpp_Uno:298:19: error: 'CS11' was not declared in this scope
    bitClear(TCCR1B,CS11);
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:78:52: note: in definition of macro 'bitClear'
    #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
    ^~~
    C:\DCC++2\BaseStation-master\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino:298:19: note: suggested alternative: 'B011'
    bitClear(TCCR1B,CS11);
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:78:52: note: in definition of macro 'bitClear'
    #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
    ^~~
    DCCpp_Uno:299:17: error: 'CS10' was not declared in this scope
    bitSet(TCCR1B,CS10);
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:49: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~
    C:\DCC++2\BaseStation-master\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino:299:17: note: suggested alternative: 'B010'
    bitSet(TCCR1B,CS10);
    ^
    C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/Common.h:77:49: note: in definition of macro 'bitSet'
    #define bitSet(value, bit) ((value) |= (1UL << (bit)))
    ^~~
    DCCpp_Uno:301:3: error: 'OCR1A' was not declared in this scope
    OCR1A=DCC_ONE_BIT_TOTAL_DURATION_TIMER1;
    ^~~~~
    DCCpp_Uno:302:3: error: 'OCR1B' was not declared in this scope
    OCR1B=DCC_ONE_BIT_PULSE_DURATION_TIMER1;
    ^~~~~
    In file included from C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/api/ArduinoAPI.h:52:0,
    from C:\Users\dkbow\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\cores\arduino/Arduino.h:23,
    from sketch\DCCpp_Uno.ino.cpp:1:
    DCCpp_Uno:308:10: error: 'TIMSK1' was not declared in this scope
    bitSet(TIMSK1,OCIE1B); // enable interrupt vector for Timer 1 Output Compare B Match (OCR1B)
    ^

    exit status 1
    'TCCR1A' was not declared in this scope
    This report would have more information with
    "Show verbose output during compilation"
    option enabled in File -> Preferences.
     
  20. Atani

    Atani TrainBoard Member

    1,460
    1,698
    36
    Unfortunately this means that someone will need to rewrite the entire DCC signal generation macro and timer setup code from scratch. From a very quick read of the datasheet for the megaAVR it looks like TCCR1A should be TCA1 but I don't know for sure and I don't have one of these new Uno devices to work with.
     

Share This Page