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

Gregg Aug 25, 2015

  1. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    Fair enough. If you want to use "Y", that will work for me. I just need to know how to define the constant :)
     
  2. Scott Eric Catalano

    Scott Eric Catalano TrainBoard Member

    205
    57
    6
    Someone asked me the other day will this Based Station support other manufacturers throttles? It should....just need to know how....any thoughts? Wireless, wired loconet etc.
     
  3. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    Well, for example, if you wire up a LocoNet and connect it to the same JMRI PC that is connected to the BaseStation, then throttles connected to the LocoNet will have their commands forwarded through JMRI to the base station. Other systems should work similarly with their cab buses, as long as the cab bus itself can be connected to JMRI. I know this is so for Digitrax and Lenz, a bit less certain for the others.

    Same is true for WiThrottle. As long as the WiThrottle server is running on the JMRI computer, those commands will get forwarded to the Base Station as well. To a certain extent, it "just works"
     
  4. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    I think I've pretty much got the WiThrottle code fully functional, for a single-client connection. Apparently getting multiple simultaneous clients connected via an Ethernet Shield is relatively tricky, and as far as I can tell the WiThrottle expects a continuously "up" client connection. So at least for now, single client may have to be the way to go. If multiple WiThrottles need to be supported, then JMRI can be used as an intermediary WiThrottle server.

    I have tested it fairly thoroughly over Serial, but my WiFi Shield just shipped, so it'll be a few more days before I can test the Ethernet side.

    I had to make a fairly significant (but functionally transparent, I hope) change to SerialCommand::process() in order to get it to handle both DCC++ and WiThrottle format commands simultaneously, but it does work. But only for one port. So you can send both DCC++ and WiThrottle commands over Serial, or both types of commands over Ethernet (untested), but you can't (yet) send DCC++ over serial while sending WiThrottle over Ethernet. I think fixing this would be a good thing, and should be relatively minor, but I can't really do it until I have the hardware.

    The key problem is that DCC++ treats < and > as command delimiters, but WiThrottle uses < and > as part of the command... so I had to make sure that whichever type of command got started, I didn't try to parse it with the wrong parser. Hence the state machine. I only start collecting up a DCC++ command if I see a < while NOT already collecting up a WiThrottle command. And I only start collecting a WiThrottle command when I see a character while NOT already collecting up a DCC++ command.

    Anyway, the code is up on my GitHub account: htttps://github.com/msunderwd/BaseStation-Uno/tree/WiThrottle

    I don't want to issue a Pull Request until I have tested it on the hardware. But if you will, I DO want y'all to review the changes to SerialCommand and see if you are OK with them. The sketch as is is taking 42K of flash and 2K of RAM (plus dynamic memory).
     
  5. Gregg

    Gregg TrainBoard Member

    237
    311
    18
    Good news - I just uploaded the new functionality discussed above to create/edit/delete/control OUTPUT pins for custom use. Pins can be set to either ACTIVE or INACTIVE state, where the default behavior is ACTIVE=HIGH and INACTIVE=LOW. This default behavior can reversed on a pin-by-pin basis that ACTIVE=LOW and INACTIVE=HIGH. Also, the STATE of each pin is retained in EEPROM and the default behavior is to restore upon power-up. However, this behavior can also be changed on a pin-by-pin basis to always set STATE to either ACTIVE or INACTIVE upon power-up.

    Create/edit/delete/control commands use different variations of <Z>. Responses back from the Base Station are in the form <Y ID STATE>. Full details are in the new file Outputs.cpp.

    Note that since this new functionality extends the use of the EEPROM, any turnout and sensor definitions currently stored will be corrupted and need to be re-entered. May want to start with the <e> command to ensure EEPROM settings are reset after new code is use for the first time. There is another way I can store the meta-info for the EEPROM (at the end of the EEPROM instead of the start) that would allow for more graceful updates that change the EEPROM without corrupting existing data. If I find we keep making new features that use more of the EEPROM I'll make this change.

    -Gregg
     
  6. Gregg

    Gregg TrainBoard Member

    237
    311
    18
    TD, how do you set the WiThrottle to output the correct DCC++ commands without JMRI (which would perform the translations)? Or are you changing the DCC++ code to accept WiThrottle commands in its native format?
     
  7. TwinDad

    TwinDad TrainBoard Member

    1,844
    551
    34
    I've added a translation layer (WiThrottle.cpp) to the DCC++ code and tweaked the code in SerialCommand:: process() to accept native WiThrottle commands. Mostly it translates the WiThrottle commands into equivalent DCC++ commands and then feeds them directly into SerialCommand:: parse() but in a few places (turnouts in particular, I think), it calls internal functions directly. For WiThrottle commands that don't have DCC++ equivalents, the WiThrottle.cpp code handles those directly. For example. WiThrottle has a "heartbeat" function where if the throttle doesn't send some kind of message every 16 seconds, the server sends an emergency stop to the locomotives.

    The code still accepts native DCC++ commands over the selected port (Ethernet or serial), so there should be no change to the existing functionality. I'm going to work on setting it up so that you can have WiThrottle coming in over Ethernet and simultaneously accept DCC++ commands over Serial, but I need to get my hardware in before I can do that.

    Assuming it works, you will be able to connect a smartphone with either WiThrottle or EngineDriver directly to a WiFi-enable DCC++ base station without JMRI. You'll only need JMRI to support multiple clients (and only until I figure out how to handle multiple simultaneous network connections in the Arduino).
     
  8. Gregg

    Gregg TrainBoard Member

    237
    311
    18
    Ross, sorry for the mix-up --- I did not realize the 1280 was an option in the Arduino IDE. I double-checked the specs and the Mega 1280 and Mega 2560 are identical except for the Flash Memory (256K versus 128K). Since the Base Station code takes up much less than 128K, the Mega 1280 should work just fine. I just updated the code to now allow for compilation under both the Mega 2560 and the Mega 1280 (basically, if the code detects a definition for the 1280 it also defines the 2560 condition and then the logic can remain the same throughout the rest of the program).

    Please give it a try and let me know how it goes!

    -Gregg
     
  9. RossNZ

    RossNZ TrainBoard Member

    12
    12
    5
    Hi Gregg,
    Your response is much appreciated. I downloaded the latest Zip file from Github and unzipped it.
    Using Arduino IDE 1.6.4 I set up the board type as "Arduino Mega or Mega 2560" and Processor as ATmega1280.
    I connected my Mega1280 to the PC USB and found it on com 3.
    An attempt to compile the sketch hung up at 2/3 of the indicator bar at the bottom left of the screen with no error message to give any clue .
    I then tried setting the board type to UNO. disconnected my mega1280 from the PC USB and attempted to compile the sketch
    as per the One Step at a Time section of the accompanying PDF file, with the same result.
    As you can see, it didn't go for some reason.

    Ross
    NZ
     
  10. Gregg

    Gregg TrainBoard Member

    237
    311
    18
    Hmm. I'm at a loss as to why it won't compile. Even without an actual board, you still should be able to "verify" the compile without uploading. Perhaps this is an issue with version 1.6.4 of the Arduino IDE (I'm using version 1.6.5, though I noticed there is now a version 1.6.6 just released). One possibility is that it's getting confused about the #include <EthernetV2_0.h>. Try changing this in line 177 of DCCpp_Uno.ino as well as line 39 of Config.h to just #include <Ethernet.h> (i.e. removing the V2_0 which is only applicable for the Seeed Studio version of the Ethernet Library). Unfortunately, the IDE cannot be set to optionally include this file when only the Ethernet is needed. Unless you have that library, the IDE should have thrown an explicit error message during compile time.
     
  11. esfeld

    esfeld TrainBoard Member

    443
    382
    17
    Deleted post, sorry
    Steve
     
    Last edited: Dec 6, 2015
  12. w8one

    w8one TrainBoard Member

    89
    109
    5
    I'm just learning C++ but maybe this will work

    in Config.h
    /////////////////////////////////////////////////////////////////////////////////////
    //
    // DEFINE COMMUNICATIONS INTERFACE TYPE
    //
    // 0 = Built-in Serial Port
    // 1 = Arduino Ethernet/SD Card Shield Seeed Studio version
    // 2 = Generic Ethernet Sheild

    #define COMM_TYPE 0

    /////////////////////////////////////////////////////////////////////////////////////
    //
    // DEFINE NAME OF ETHERNET LIBRARY TO INCLUDE (DIFFERENT SHIELDS MAY USE THEIR OWN LIBRARIES)
    // *** ALSO MUST ADD THIS AS AN EXPLICIT INCLUDE FILE TO "DCCpp_Uno" ***

    #if COMM_TYPE == 1
    #define ETHERNET_LIBRARY <EthernetV2_0.h>
    #elif COMM_TYPE == 2 // change #elif to #else if one of the two has to be there
    #define ETHERNET_LIBRARY <Ethernet.h>
    #endif // define libary by comm_type

    in DCCpp_Uno.ino in the #include area

    // #include <EthernetV2_0.h> // Moved bellow to select witch one

    // SET UP COMMUNICATIONS INTERFACE - FOR STANDARD SERIAL, NOTHING NEEDS TO BE DONE

    #if COMM_TYPE == 1 // Seeed Studio version
    #include <EthernetV2_0.h>
    byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF }; // define your own 6-byte MAC address (to be used for DHCP when initializing server)
    EthernetServer INTERFACE(ETHERNET_PORT); // Create and instance of an EnternetServer
    #endif

    #if COMM_TYPE == 2 // Generic version
    #include <Ethernet.h>
    byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF }; // define your own 6-byte MAC address (to be used for DHCP when initializing server)
    EthernetServer INTERFACE(ETHERNET_PORT); // Create and instance of an EnternetServer
    #endif

    and in DCCpp_Uno.ino in the void setup area


    #if COMM_TYPE != 0 // Changed to not equal 0 so works for 1 or 2
    Ethernet.begin(mac); // Start networking using DHCP to get an IP Address
    INTERFACE.begin();
    #endif

    I seen this in another sketch and thought it might apply here.
     
  13. w8one

    w8one TrainBoard Member

    89
    109
    5
    And because i was bored waiting for parts, maybe the mac address should be 0x44, 0x43, 0x43, 0x2B, 0x2B, 0x00 = 44:43:43:2b:2b:00 = d:c:c:+:+:0 just killing time.
     
  14. Gregg

    Gregg TrainBoard Member

    237
    311
    18
    All, I've just uploaded a small but hopefully helpful change to the Base Station Code. Last month Arduino released version 1.6.6 of it's IDE. This version corrects a long-standing problem with the way #include is handled, especially with regard to the inclusion of libraries. With this new update, #include can take a #define as an argument, which allows us to specify the name of the Ethernet Library just once in the Config.h file, without the need to duplicate it in the main sketch file (DCCpp_Uno.h). But more importantly, the new Arduino IDE correctly allows #include to be part of a compiler conditional #if ... #endif. This is what I had tried to do last week but it did not work in the older 1.6.5 IDE. What this means is that if in the Config.h file you select the Serial Port for communication, then the #include for the Ethernet Library is completely ignored --- you don't have to worry about having the library installed (which of course makes sense if you don't want to use it).

    This framework will also make it a lot easier for us to incorporate other optional libraries and pieces of code and have them all conditionally compiled based on the settings in the Config.h file.

    HOWEVER, this version will NOT compile with the Arduino 1.6.5 IDE. I think that version will throw an error (or behave in an unexpected way). Please make sure to download the latest 1.6.6 IDE before using this latest update of the Base Station code.

    -Gregg
     
  15. Gregg

    Gregg TrainBoard Member

    237
    311
    18

    w8one, I think you have the right idea --- I tried to do something like this last week but the IDE I was using (version 1.6.5) was not behaving as expected. As discussed in my previous post, the latest Arduino IDE seems to work correctly (and according to the web, this has been a very long-standing "bug" in the IDE). Please check out the latest Base Station update to see how I implemented the conditional compile.

    Also, though not technically required, in C and C++ it's common to include only definitions in *.h header files. Including byte mac[]... and EthernetServer... actually declares these variables. This usually gets done only in a *.c or *.cpp file. The reason is that *.h files are included in many other *.cpp files, and that would lead to a duplicate declaration of the same variables, which either will throw an error at compile time, or behave in an unexpected fashion during run time. The scope of variables declared outside of a function are typically global, but only within the *.cpp or *.c file in which they are declared. To use a variable declared in a single *.cpp file across other *.cpp files, you create a definition (instead of a declaration) of that variable using the keyword extern. This is usually put in a *.h file that is included in any *.cpp file that needs access to variables declared in a different file (for example, see the Base Station file Comm.h). Most of this complication has to do with the multi-step way compilers first pre-process each file, then compile the *.c and *.cpp files, and then finally link the resulting object codes for each *.cpp and *.c file (often in the form of *.o files) into a single executable. If you turn on the verbose flag within the IDE you'll notice that it only compiles *.c and *.cpp files that have changed (otherwise it re-uses previously compiled object code to speed up the process). Very large C++ programs can take many minutes or even a full hour to compile, so you only want to compile what's changed (of course this is not really an issue for the Arduino :))
     
  16. RossNZ

    RossNZ TrainBoard Member

    12
    12
    5
    Hi Gregg,
    This is good for a laugh..................
    I have just got the sketch to upload to my Mega1280.

    I tried your suggestion of removing the V2_o but that was not a success story.
    So I down down loaded Arduino IDE 1.6.6. Downloaded a new zip file from github and tried compiling it.
    I got so many pages of errors it was obvious something was significantly mismatching.
    So I downloaded Arduino IDE 1.6.5 and tried compiling the sketch.
    This time i got error messages telling me of duplicate files, so I tracked them down, deleted them and all is well.
    I now get <iDCC++ BASE STATION FOR ARDUINO MEGA / ARDUINO MOTOR SHIELD: BUILD Dec 7 2015 15:41:00><N0: SERIAL>
    in the serial monitor. Step one is now ticked of:).

    This was about 2 hours ago so would have been an hour or more before your latest upload.
    Now I am tempted to try IDE 1.6.6 with your latest upload and see what happens.

    Ross
    NZ
     
  17. NigelTwo

    NigelTwo New Member

    2
    1
    1
    Could it be that the apparent "oh" in <No:SERIAL> is actually a "zero"? The code that emits this is in the setup part of DCCpp_Uno.ino. It is trying to report what COMM_TYPE option you have chosen, viz
    Serial.print("<N");
    Serial.print(COMM_TYPE);
    Serial.print(": ");

    #if COMM_TYPE == 0
    Serial.print("SERIAL>");
    #elif COMM_TYPE == 1
    Serial.print(Ethernet.localIP());
    Serial.print(">");
    #endif


    So you could be running on the serial port!
     
  18. RossNZ

    RossNZ TrainBoard Member

    12
    12
    5
    HI Gregg,
    Step two, the jumpers on the Arduino motor shield for the Mega.

    The paragraph in the BRIEF NOTES ON THE THEORY AND OPERATION OF DCC++ BASE STATION:
    (lines 129-131) reads.......
    For the Mega, the OC1B output is produced directly on pin 12, so no jumper is needed to connect to the
    Motor Shield's DIRECTION A input. However, one small jumper wire is needed to connect the Mega's OC3B output (pin 2)
    to the Motor Shield's DIRECTION A input (pin 12), and another small jumper wire to connect
    to the Motor Shield's DIRECTION B input (pin 13).

    ......
    I am presuming that
    The OC1B output is produced directly on pin 12.
    Only one jumper wire is needed to connect the Mega's OC3B output (pin 2) to the Motor Shield's DIRECTION B input (pin 13).


    And now I have Arduino IDE 1.6.6, The latest github zip file. The compile worked ok and completed but did give the following warnings.....

    C:\DCC++\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino:415:152: warning: backslash and newline separated by space [enabled by default]

    R.currentBit=0; /* reset current bit pointer and determine which Register and Packet to process next--- */ \

    ^
    C:\DCC++\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino:421:100: warning: backslash and newline separated by space [enabled by default]

    R.tempPacket=R.currentReg->activePacket; /* flip active and update Packets */ \

    ^

    C:\DCC++\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino:437:103: warning: backslash and newline separated by space [enabled by default]

    } /* END-ELSE */ \

    ^

    C:\DCC++\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino:438:88: warning: backslash and newline separated by space [enabled by default]

    \

    ^

    C:\DCC++\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino:195:59: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]

    CurrentMonitor mainMonitor(CURRENT_MONITOR_PIN_MAIN,"<p2>"); // create monitor for current on Main Track

    ^

    C:\DCC++\BaseStation-master\DCCpp_Uno\DCCpp_Uno.ino:196:59: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]

    CurrentMonitor progMonitor(CURRENT_MONITOR_PIN_PROG,"<p3>"); // create monitor for current on Program Track

    ^

    It did compile and upload to my Mega1280 so now its onward to connecting the motor shield to a power supply and some track and see what sort of signal I can detect on the track.
    Then work out how to get a throttle to talk to my DCC train which is on default address 3.

    Ross
    NZ
     
    Last edited: Dec 7, 2015
  19. dpharris

    dpharris New Member

    5
    2
    4
    I have DCC++ running -- sweet. You have to edit the controllerConfig file to change the cabs. Search for cab2004. You can edit the attached controls there, too.
     
  20. Gregg

    Gregg TrainBoard Member

    237
    311
    18
    Yes - that a zero. It's echoing back the COMM_TYPE (either N0 or N1). Sorry for any confusion. If you type <s> into the serial monitor window of the IDE it should reveal the overall status of the system, verifying that things are working.
     
    Last edited: Dec 7, 2015

Share This Page