Introducing DCC++ ---a complete open-source DCC station and interface

Gregg Aug 25, 2015

  1. David Bodnar

    David Bodnar TrainBoard Member

    264
    481
    13
    Steve - I've been away from the computer --- early morning bike ride with the local club.
    The routine that allows me to store a number larger than 255 (up to 2^16 = 65536) involves dividing the large value by 256, storing that value (the high byte) then storing the remainder (also less than 256) as the low byte.
    I hope that helps with your code, too
    dave
     
    Scott Eric Catalano likes this.
  2. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Dave,

    I tested this up to 65535 and fingers crossed it's stable.

    Thanks for your patience and testing (had a few Saturday thing to do)

    Thanks for the solution !

    Code:
    //*** IMPORTANT *** Most of the libraries used are included in the ESP8266 Core package which you must install to the Arduino IDE for this sketch to compile.
    //Please use the Git method here https://github.com/esp8266/Arduino and NOT the File > Preferences > Additional Boards Manager URL.
    //WebSocketsServer.h appears to be broken using the later. And DO include the Python requirement (correct at Feb 15 2016) The author is aware of this issue.
    //You will need to install Git to simplify that operation.
    //If the sketch compiles OK but won't upload properly, then go Sketch > Export compiled Binary. A .bin file will be created in this sketch's folder.
    //Then upload the binary with your favorite flashing tool.
    
    //Please remember to fill in your home network SSID and PASSWORD. In this version when you reboot your esp8266 it will automatically log on to your WLAN.
    //Make a note of the IP address assigned by your router by logging on to its home page. Your PC or Mobile also need to be on this same network.
    //With the "Webpage" open on your chosen device, type the IP and Port in the following format eg. 192.168.11.55:81 (port == @line 28) press Enter(desktop) or tap the screen outside the box (mobile).
    //With luck you are now connected enjoy. A message will confirm in later versions.
    //Console Debugging is available in Chrome windows desktop for the html wireless side. And at the other end (Arduino) a serial monitor will let you know whats happening there.
    //You can also see serial out from esp8266 if you connect by usb and use a serial monitor there. (Debug from this sketch shows up there)
    
    //In tests I hooked up 2 seperate clients at the same time so multi-client is confirmed. However as certain values are fixed assignment things start to go wrong quickly. We can work that out ;)
    //There is room for many more functions on the page, this is just Proof of Concept.
    
    //Special thanks to Rahul 27 who created the original sketch on which this sketch is based. And also Markus Sattler for the WebSocketsServer library.
    
    //This software is Free. Enjoy. Steve Lowry(indev2)2016.
    
    #include <Arduino.h>
    #include <ESP8266WiFi.h>
    #include <WebSocketsServer.h>
    #include <Hash.h>
    #include <EEPROM.h>
    WebSocketsServer webSocket = WebSocketsServer(81);
    int xxx;
    String c;
    String d;
    int cInt;
    int dInt;
    const char* ssid     = "YOUR SSID";
    const char* password = "YOUR PASSWORD";
    const int redPin = 15;  //Debug LED function not needed ***All debug lines can be deleted once you understand the code and you have it working, we like tidy code ;)***
    const int greenPin = 12; //Debug LED function not needed
    void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) {
    
      switch (type) {
        case WStype_DISCONNECTED:
    
          break;
        case WStype_CONNECTED:
          {
            IPAddress ip = webSocket.remoteIP(num);
    
    
          }
          break;
        case WStype_TEXT:
          {
    
            String text = String((char *) &payload[0]);
            if (text == "PON") {
    
              digitalWrite(12, HIGH);//Visual Debug - Green LED will come on at Power ON
              digitalWrite(15, LOW);//Debug - not needed
              Serial.println("<1>");
    
            }
    
            if (text.startsWith("CAB1")) {
    
              String c = (text.substring(text.indexOf("CAB1") + 4, text.length()));
              int cInt = c.toInt();
              xxx = cInt /256;
              EEPROM.write (8, xxx);
              xxx = cInt - (xxx * 256);
              EEPROM.write (9, xxx);
              EEPROM.commit();
              delay (20);
              Serial.print("Cab# Entered t1 ");//Debug
              Serial.println(cInt);//Debug
    
            }
    
            if (text.startsWith("CAB2")) {
    
              String c = (text.substring(text.indexOf("CAB2") + 4, text.length()));
              int cInt = c.toInt();
              xxx = cInt /256;
              EEPROM.write (12, xxx);
              xxx = cInt - (xxx * 256);
              EEPROM.write (13, xxx);
              EEPROM.commit();
              delay (20);
              Serial.print("Cab# Entered t2 ");//Debug
              Serial.println(cInt);//Debug
    
            }
    
            if (text == "FOR") {
    
              dInt = (1);
              EEPROM.write (0, dInt);
              EEPROM.commit();
              int Cab1 = EEPROM.read(8)*256;
              Cab1 = Cab1 + EEPROM.read(9);
              Serial.print("Forward Entered t1 ");//Debug
              Serial.println(dInt);//Debug
              Serial.print("<t1 ");
              Serial.print(Cab1);
              Serial.println(" 0 1>");//Stops train (if not stopped) on direction change
              delay (20);
    
            }
    
            if (text == "FOR2") {
    
              dInt = (1);
              EEPROM.write (4, dInt);
              EEPROM.commit();
              int Cab2 = EEPROM.read(12)*256;
              Cab2 = Cab2 + EEPROM.read(13);
              Serial.print("Forward Entered t2 ");//Debug
              Serial.println(dInt);//Debug
              Serial.print("<t2 ");
              Serial.print(Cab2);
              Serial.println(" 0 1>");
              delay (20);
    
            }
    
            if (text == "REV") {
    
              dInt = (0);
              EEPROM.write (0, dInt);
              EEPROM.commit();
              int Cab1 = EEPROM.read(8)*256;
              Cab1 = Cab1 + EEPROM.read(9);
              Serial.print("Reverse Entered t1 ");//Debug
              Serial.println(dInt);//Debug
              Serial.print("<t1 ");
              Serial.print(Cab1);
              Serial.println(" 0 0>");
              delay (20);
    
    
            }
    
            if (text == "REV2") {
    
              dInt = (0);
              EEPROM.write (4, dInt);
              EEPROM.commit();
              int Cab2 = EEPROM.read(12)*256;
              Cab2 = Cab2 + EEPROM.read(13);
              Serial.print("Reverse Entered t2 ");//Debug
              Serial.println(dInt);//Debug
              Serial.print("<t2 ");
              Serial.print(Cab2);
              Serial.println(" 0 0>");
              delay (20);
    
    
            }
    
            if (text.startsWith("x")) {
    
              String xVal = (text.substring(text.indexOf("x") + 1, text.length()));
              int xInt = xVal.toInt();
              analogWrite(greenPin, xInt); //Debug - not needed, Green "ON" LED will brighten/dim according to throttle up/down
              int dvalue = EEPROM.read(0);
              int cvalue = EEPROM.read(8)*256;
              cvalue = cvalue + EEPROM.read(9);
              Serial.print("<t1 ");
              Serial.print(cvalue);
              Serial.print(" ");
              Serial.print(xInt);
              Serial.print(" ");
              Serial.print(dvalue);
              Serial.println(">");
    
            }
    
            if (text.startsWith("y")) {
    
              String yVal = (text.substring(text.indexOf("y") + 1, text.length()));
              int yInt = yVal.toInt();
              int dvalue = EEPROM.read(4);
              int cvalue = EEPROM.read(12)*256;
              cvalue = cvalue + EEPROM.read(13);
              Serial.print("<t2 ");
              Serial.print(cvalue);
              Serial.print(" ");
              Serial.print(yInt);
              Serial.print(" ");
              Serial.print(dvalue);
              Serial.println(">");
    
            }
    
            if (text == "RESET") {
    
              EEPROM.write (0, 0);
              EEPROM.write (4, 0);
              EEPROM.write (8, 0);          
              EEPROM.write (9, 0);
              EEPROM.write (12, 0);
               EEPROM.write (13, 0);
              EEPROM.commit (); //All stored values set zero
              delay (20);
              digitalWrite(15, HIGH);//Visual Debug - not needed - Red LED will come on at Power OFF
              analogWrite(greenPin, 0); //LED function - not needed
              Serial.println("<0>");
    
            }
            break;
    
          }
    
          webSocket.sendTXT(num, payload, lenght);
          webSocket.broadcastTXT(payload, lenght);
          break;
    
        case WStype_BIN:
    
          hexdump(payload, lenght);
    
          // echo data back to browser
          webSocket.sendBIN(num, payload, lenght);
          break;
      }
    
    }
    
    
    void setup() {
    
      Serial.begin(115200);
      pinMode(15, OUTPUT);
      pinMode(12, OUTPUT);
      EEPROM.begin(512);
      EEPROM.write(0,1);
      EEPROM.write(4,1);
      EEPROM.commit();
      delay (50);
    
      WiFi.begin(ssid, password);
    
      while (WiFi.status() != WL_CONNECTED) {
        delay(100);
      }
      Serial.println(WiFi.localIP());
      webSocket.begin();
      webSocket.onEvent(webSocketEvent);
    }
    
    void loop() {
      webSocket.loop();
    }
     
    Scott Eric Catalano likes this.
  3. David Bodnar

    David Bodnar TrainBoard Member

    264
    481
    13
    Steve - Good work! It is passing the 4 digit loco numbers now!!

    Two issues - you left (what I assume is) your SSID and Password in the code you posted - may want to remove that!
    Also, when you change direction the software always sends a command with a speed of zero - I have to increase the throttle manually to get the loco to start up again,

    Also, I got the ESP8266 connected directly to the Uno with DCC++ on it by just inserting a small silicon diode in series with the data line - I did not use a zener. It works and everything seems happy. I still have the USB programmer connected to the ESP8266 so that I can see commands on the terminal - nice!

    Thanks for your work on this!

    dave
     
    Scott Eric Catalano likes this.
  4. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    The Stop is intentional, kinda thinking of novice uses on the system, hitting reverse by mistake. Especially if the train is moving some.
    You can easily disable that behaviour.

    Thanks again

    Steve
     
    Scott Eric Catalano likes this.
  5. David Bodnar

    David Bodnar TrainBoard Member

    264
    481
    13
    I'll have a look at the code and see what I can find - thanks, Steve
    Now outside for some yard work!
    dave
     
    Scott Eric Catalano likes this.
  6. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Dave,

    Without debug, LEDs and the forward/reverse stop the 'if' statements can be pared down to this,

    Code:
    String text = String((char *) &payload[0]);
            if (text == "PON") {
    
              Serial.println("<1>");
    
            }
    
            if (text.startsWith("CAB1")) {
    
              String c = (text.substring(text.indexOf("CAB1") + 4, text.length()));
              int cInt = c.toInt();
              xxx = cInt /256;
              EEPROM.write (8, xxx);
              xxx = cInt - (xxx * 256);
              EEPROM.write (9, xxx);
              EEPROM.commit();
              delay (20);
    
            }
    
            if (text.startsWith("CAB2")) {
    
              String c = (text.substring(text.indexOf("CAB2") + 4, text.length()));
              int cInt = c.toInt();
              xxx = cInt /256;
              EEPROM.write (12, xxx);
              xxx = cInt - (xxx * 256);
              EEPROM.write (13, xxx);
              EEPROM.commit();
              delay (20);
    
            }
    
            if (text == "FOR") {
    
              EEPROM.write (0,1);
              EEPROM.commit();
              delay (20);
    
            }
    
            if (text == "FOR2") {
    
              EEPROM.write (4, 1);
              EEPROM.commit();
              delay (20);
    
            }
    
            if (text == "REV") {
    
              EEPROM.write (0, 0);
              EEPROM.commit();
              delay (20);
    
    
            }
    
            if (text == "REV2") {
    
              EEPROM.write (4, 0);
              EEPROM.commit();
              delay (20);
    
    
            }
    
            if (text.startsWith("x")) {
    
              String xVal = (text.substring(text.indexOf("x") + 1, text.length()));
              int xInt = xVal.toInt();
              int dvalue = EEPROM.read(0);
              int cvalue = EEPROM.read(8)*256;
              cvalue = cvalue + EEPROM.read(9);
              Serial.print("<t1 ");
              Serial.print(cvalue);
              Serial.print(" ");
              Serial.print(xInt);
              Serial.print(" ");
              Serial.print(dvalue);
              Serial.println(">");
    
            }
    
            if (text.startsWith("y")) {
    
              String yVal = (text.substring(text.indexOf("y") + 1, text.length()));
              int yInt = yVal.toInt();
              int dvalue = EEPROM.read(4);
              int cvalue = EEPROM.read(12)*256;
              cvalue = cvalue + EEPROM.read(13);
              Serial.print("<t2 ");
              Serial.print(cvalue);
              Serial.print(" ");
              Serial.print(yInt);
              Serial.print(" ");
              Serial.print(dvalue);
              Serial.println(">");
    
            }
    
            if (text == "RESET") {
    
              EEPROM.write (0, 0);
              EEPROM.write (4, 0);
              EEPROM.write (8, 0);        
              EEPROM.write (9, 0);
              EEPROM.write (12, 0);
              EEPROM.write (13, 0);
              EEPROM.commit (); //All stored values set zero
              Serial.println("<0>");
              delay (20);
            
            
    
            }
            break;
    
     
    Scott Eric Catalano likes this.
  7. David Bodnar

    David Bodnar TrainBoard Member

    264
    481
    13
    Good day, Steve - I think I found another bug - if you set a loco address in excess of 255 (say 257) it works properly at first. If you turn the power off then back on (with the on-screen power button) the address changes to the low byte (in this case from 257 to 2) the next time you use the throttle

    See if you can get it to display the same behavior

    thanks
    dave
     
    Scott Eric Catalano likes this.
  8. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Good Morning Dave,

    The expected behaviour would be to clear the session at least from an Eeprom point of view, as you've noted that's not working correctly.
    Ideally the input fields would be emptied too.

    We can easily comment out or delete the eeprom.write 0 entries in the program and the session will be saved. What do think might be best practice?

    Steve
     
    Scott Eric Catalano likes this.
  9. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Dave,

    In other news, I've made excellent progress on moving the whole project onto the ESP8266 and it's SPIFFS memory (an easy to use feature recently implemented in Arduino IDE) There are some very smart folks out there !

    There is an intermittent issue in getting the page to render properly when you call it to your browser. If I can sort that out things will take an entirely different dimension.

    Great party piece, do you still run your little seminars? Audience participation?

    Steve.
     
    Last edited: Mar 13, 2016
    Scott Eric Catalano likes this.
  10. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Dave,

    Address's 9 and 13 are probably not in the sketch you are running. Alternatively comment out the EEPROM lines to retain session in memory.


    }

    if (text == "RESET") {

    EEPROM.write (0, 0);
    EEPROM.write (4, 0);
    EEPROM.write (8, 0);
    EEPROM.write (9, 0);
    EEPROM.write (12, 0);
    EEPROM.write (13, 0);
    EEPROM.commit (); //All stored values set zero
    Serial.println("<0>");
    delay (20);
     
    Scott Eric Catalano likes this.
  11. David Bodnar

    David Bodnar TrainBoard Member

    264
    481
    13
    Steve - when I did my throttle I just sent <0> when I did a power off and send a <1> then <t1 xxx xxx xxx> to restore the prior speed when done - my reasoning is that you might want to briefly stop a train to clear some issue but want it back on at the same speed when power is restored - I cleared speed when the STOP button was hit, not the power.

    dave
     
    Scott Eric Catalano likes this.
  12. David Bodnar

    David Bodnar TrainBoard Member

    264
    481
    13
    Very cool - so I gather that the objective is to more the web page to the ESP8266? That would make things much simpler!
    dave
     
  13. David Bodnar

    David Bodnar TrainBoard Member

    264
    481
    13
    I'll have a look at that - I should think that you would have to resend the speed command to restore that, at least I had to when I did it.
    thanks
    dave
     
    Scott Eric Catalano likes this.
  14. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    If we get rid of the Eeprom 'reset' then most of the session is still in memory. Then somehow we need to resend the last throttle string at power up.

    If we add a separate STOP button, then the functions can be separated.

    Shouldn't take too long to do. And thank you Dave, this is the kind of input needed to develop the page as we move along.

    On the SPIFFS thing, yes the page would be served from the esp, just point ANY browser enabled device at the server, and up pops the page.


    Steve.
     
    Scott Eric Catalano likes this.
  15. David Bodnar

    David Bodnar TrainBoard Member

    264
    481
    13
    lnxlnx, Scott Eric Catalano and HVT like this.
  16. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Dave,

    Yeah, that's neat. Another viable option ! Good work.

    My aim is just to try cover all bases for all users, like when your not able to connect to the internet.

    I got a couple of the 12 series esp's. They have 3M.

    There is no difficulty hosting the page from the ESP, I got that done in a few hours last night, including a working sketch.

    Just to debug a few things and I'll post the code.

    Steve.

    Edit: Just tried the link on my Kindle Fire 7HD and the page displays perfectly. Nice. Function OK once websocket established !
     
    Last edited: Mar 13, 2016
    Scott Eric Catalano likes this.
  17. David Bodnar

    David Bodnar TrainBoard Member

    264
    481
    13
    Excellent = will the code fit on the basic ESP -01 with 512K memory?
    dave
     
    Scott Eric Catalano likes this.
  18. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    Dave

    Yes, with room to spare. And the SPIFFS bit is just a wrapped up version of the html folder, a few hundred kb into the seperate 1M area.

    Steve

    Another option for the page is to host it anywhere on your home network. Just type its location into your browser. Create a bookmark done.
     
    Last edited: Mar 13, 2016
    HVT likes this.
  19. UK Steve

    UK Steve TrainBoard Member

    453
    683
    12
    All,

    I could use some input from other readers on how the page renders on different mobile devices so I can at least know the formatting is sound.

    As Dave Bodnar has kindly hosted the page on his website, this should be simple for anyone to do. No more files to install, just follow the links in his post #995 above.

    Thanks

    Steve.
     
    Scott Eric Catalano likes this.
  20. HVT

    HVT TrainBoard Member

    74
    93
    15
    Steve,
    Just tried it with Win10 Chrome, Galaxy S5 phone and Galaxy Note 10.1 from the link on Dave B's site at the same time, they all open and work very well. Way coooooool!!!!

    So if IIUC if the HTML page on Dave B's site was connected to his layout via a DCC++ Base Station I would actually be running the trains on his layout? Or I guess I should ask from where is what I am seeing on the screens coming?

    Dave Merrill
     
    Scott Eric Catalano likes this.

Share This Page