Modifying DCC++ for multiple power districts

crusader27529 Aug 19, 2016

  1. chi.sp

    chi.sp New Member

    7
    12
    7
    I've been playing DCC++ with Pro Mini and Nano boards and everything seems to be fine, both for main as well as programming track tasks. My 'arduino' boards are the cheap compatible chinese boards sold in ebay. All I've done was comment out all conditional lines ('UNO or MEGA' decisions) so the sketch compiles as it were a UNO board.

    These are the guidelines I used to comment out final version 1.2.1:

    ON DCCpp_UNO.h

    #ifdef ARDUINO_AVR_MEGA...
    REM Lines 25 until 27

    #if defined ARDUINO_AVR_UNO...
    REM Line 29
    REM Lines 42 until 53


    ON DCCpp_UNO.ino

    #ifdef ARDUINO_AVR_UNO...
    REM Line 312
    REM Lines 350 until 389

    #ifdef ARDUINO_AVR_UNO...
    REM Line 461
    REM Lines 467 until 473


    ON SerialCommand.cpp

    #ifdef ARDUINO_AVR_UNO...
    REM Line 461
    REM Lines 467 until 473

    Hope it can be helpfull.
     
    Scott Eric Catalano likes this.
  2. crusader27529

    crusader27529 TrainBoard Member

    247
    167
    11
    I did similar stuff, but tried to force the detection logic from the IDE to select the UNO, but it still selected the MEGA in some places, so I commented out essentially the same stuff you did......I didn't get a chance to test.....thanks for the results.
     
    Scott Eric Catalano likes this.
  3. brendanf

    brendanf TrainBoard Member

    62
    54
    8
    Hello, new member here.. I would have posted sooner but I have been busy reading the original 75 page thread on how DCC++ got started..

    Along with that, testing it out on my own layout..

    Anyway, about adding more power districts, I have a few ideas about doing this:

    1. Basically assemble a "TTL to RS485 convertor" for each Mega/UNO/Nano (lets call them NODES) and connect it to the TX+RX pins on Arduino/Node. You would have to tweak the code of all the NODES and what I would do, is basically take the exact same code, but remove code that generates responses. They would all listen and generate the DCC signal on their driver outputs. You don't really need them to respond.

    The issue with this though, is using the extra outputs on your NODE for inputs and outputs, since the NODE responds back with their status. This leads me to idea #2.

    2. More complicated for sure but builds off idea#1. You could keep the code about generating a response, since a node would only respond when spoken to, but you would need to give each NODE an address. My preferred way, will eat a few I/O but you can add a small 4 bit DIP switch, that by setting the switches would create an address for that NODE. 16 combinations are possible. You could have the master base station connected handle the parsing and relaying of the instructions from the NODES back to the software. Some formatting of outputs and inputs would be needed here though. For example, Sensors/Outputs numbered 1XX would be NODE 1, 2XX would be NODE2, etc etc.. I haven't had much time to play with JMRI and DCC++ yet, so I am yet unsure about how they corrollate to individual pins.

    3. Check out this website, this guys uses an Arduino as well, to mimic C/MRI. You can create a second connection in JMRI, and use this, you can have thousands of I/O. It won't generate DCC signals for the tracks, but you can add huge amounts of I/O. His work and the original C/MRI are based on RS-485

    http://www.utrainia.com/tag/cmri
     
    Scott Eric Catalano likes this.
  4. crusader27529

    crusader27529 TrainBoard Member

    247
    167
    11
    We're NOT trying to use multiple DCC++ systems to power multiple districts, but SIMPLY control multiple MD boards with individually controlled outputs from 1 DCC++ system.....like one program track and 2 or 3 MD for the MAIN districts, and have only a single MD shut down if an over current happens in that district......plus maybe automatically re-power the district that just shut down after a short delay.
     
    Scott Eric Catalano likes this.
  5. brendanf

    brendanf TrainBoard Member

    62
    54
    8
    Ah, so you only want one Arduino, and to have multiple motor drivers from the one Arduino?
     
    Scott Eric Catalano likes this.
  6. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    a thought i just had (coffee high :coffee: ). if you use the same DCC++ signal lines for the other power districts, then the enable lines, and current sense lines could be monitored independently, and enabled/disabled independently.

    ~Travis
     
    Scott Eric Catalano likes this.
  7. brendanf

    brendanf TrainBoard Member

    62
    54
    8
    Not really.. My initial thought about the way crusader wants to do it would be to stack multiple shields or connect multiple boards to the same pin outs, the issue with that though, is the way the power is shutoff in the code. In the code it is fairly easy to add another analog input with the current sense to kill the power, but he only wants the area with a short to power down and the code actually stops the PWM.. So it's not going to a simple thing.

    I had some more thoughts about this tonight and had to dig up my old electronics textbooks to find it, but there is another way without modifying anything really, including the code.

    A foldback-current-limiting circuit could be built prior the VIN on the shields or boards that are being used and then the rest of the connections to the Arduino can be paralelled up (impedance would have to be considered at some point though) and then which ever track is shorted would only shutdown and the rest could continue.
     
    Scott Eric Catalano likes this.
  8. crusader27529

    crusader27529 TrainBoard Member

    247
    167
    11
    Yes....it's much simpler for small to medium layouts.
     
    Scott Eric Catalano likes this.
  9. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    Check again, unless there has been a significant change since i downloaded my copy, it shuts down the signal enable pins. and stacking the shields will only produce two main circuits, and two programming circuits.

    My version of the code already shuts down the main and programming tracks independently, so i don't think it wold be too far off an idea to include more motor drivers by sharing the DCC++ signal, and use independent enable and current sense pins. keep in mind, the motor drivers i am using are not shields.

    ~Travis
     
    Scott Eric Catalano likes this.
  10. crusader27529

    crusader27529 TrainBoard Member

    247
    167
    11
    YES, sharing the DCC++ signal is correct, with the enable and monitoring separately for each power district(MD board).

    It's reasonably simple, except I'm not conversant in C++, just plain C code.
     
    Scott Eric Catalano likes this.
  11. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    I will putter away on it and see what i can come up with.

    seems reasonable enough. the auto reset sounds interesting. though i would put a counter on it so if the short was not cleared after 2 or 3 tries, then it would stay off, pending a manual reset.

    The analog sensing is fine for both an UNO (6 pins) and a MEGA 2560 (16 pins), as the UNO can support a programming track, and 5 main districts. though for the enable pins, the UNO has room for 4 channels extra (3 if an ethernet w/SD shield is used). the MEGA has plenty of digital outputs extra, depending on attached sensors.

    I will do my best to include various hardware scenarios, though i will be testing on a MEGA 2560.

    ~Travis
     
  12. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    well, i did a little code optimizing to prep for the power-district option. i haven't posted it to my GitHub yet as i have not tested it.

    it looks easy enough to work with (for me...), but my hang-up is how SerialCommand.cpp/.h calls in the objects from the CurrentMonitor class. one would have to do a lot of editing to add more than two monitors. but anyway, i will test and make sure i can still run my locomotive around, and try a soft track short (low value resistor, momentary) to verify independent disconnection. then if it check out, i will send it off to my GitHub to fish for ideas from others on how to sort out the SerialCommand issue.

    ~Travis
     
    Scott Eric Catalano likes this.
  13. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    well, it seems to work. i soft-shorted the main, and it stopped for a bit, restarted. i did this i think 3 or 4 times, and it stayed off, as i wanted it to.

    Keep in mind, this is the actual code i am running, so it needs to be reconfigured to suit your hardware (maybe...). just confirm the pin assignments in DCCpp.h, and note i changed the init for the CurrentMonitor class to have additional parameters.
    Code:
    CurrentMonitor mainMonitor(3,A0,96,"<p2>");  // create monitor for current on Main Track
    CurrentMonitor progMonitor(11,A1,96,"<p3>");  // create monitor for current on Program Track
    
    the first parameter is the enable pin for the channel, the second is the current sense pin for the channel, the third is the CURRENT_SAMPLE_MAX value for the channel. the 96 suits my hardware, and may not work right with yours.

    there are a lot of various changes, so it may not work if you just take bits and pieces, but it works as a whole.

    Now i just have to figure out how to work the SerialCommand issue so that it can handle more than two channels.

    ~Travis[/code]
     
  14. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    the problem i am having with the SerialCommand.cpp/.h files is in the init, the class brings in current monitor classes sepperatly, as parameters. so to add more districts, they have to be hard-coded into both files. this is needed because the function "CurrentMonitor:: Power()" that i created is called from the "SerialCommand:: Parse()" function. it also calls "CurrentMonitor->current()" from "parse()" so that it can send the current to JMRI, or what-have-you.

    Somehow i have to make a function that tallies the registered CurrentMonitor class pin numbers, and turns them all on/off from a common function for the SerialCommand. also, it would be handy if the current variable reflected all main tracks as a whole. that is what i am working on at the moment.

    ~Travis
     
    Scott Eric Catalano likes this.
  15. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    (thinking out loud, anybody feel free to chime in)

    i have created two functions within CurrentMonitor.
    Code:
    void CurrentMonitor::PowerAll(bool enable)
    {
      for (int I = 0; I < PinCounter; I++)
      {
        digitalWrite(pins[I],enable);
      }
    }
    
    float CurrentMonitor::CalcTotalCurrent()
    {
      float OutVal = 0.0;
      for (int I = 0; I < PinCounter; I++)
      {
        OutVal=(OutVal + (float) analogRead(Apins[I])); // took out the smoothing for now
      }
      return (OutVal);
    }
    i need to be able to use 3 global variables:
    Code:
    int PinCounter = 0;
    int pins[16];
    int Apins[16];
    
    maybe it is just mental fatigue, but i don't remember how to use global variables inside a class. these must be outside the class to record for all instances of CurrentMonitor.

    ~Travis
     
    Scott Eric Catalano likes this.
  16. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    ahh, yes, "extern"! :D

    anyway, i may have it, but i haven't tested it yet. the new code is on my GitHub. it compiles, but i have not yet tested it yet. more of a developer release.

    if it works, it should make it relatively simple to add up to 16 channels (max analog inputs for a Mega 2560). better write-up to follow, if it works.
    Now, everybody cross your fingers...

    ~Travis
     
    sboyer2 and Scott Eric Catalano like this.
  17. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    (play by play)
    uploaded...
    loading JMRI...
    start track power... [power reads good]
    start train forward... [train runs around, forward]
    short track ... [train stops, and restarts after a moment]
    again... [same result]
    again... [same result]
    again... [train stays motionless]
    recycle the track power button... [train restores where it left off]

    ok, initial tests works. lets add a district and run off it.
    compiles...
    uploades...
    loading JMRI...
    start Track Power... [good on main, fail on extra test track using pin 30]
    try again with pin 5... [good on main, good on extra test track]

    I wonder why pins 22 - 49 don't work. i will have to look into the code a little deeper i guess. but it works with an alternate track on pin 5.

    ~Travis
     
    sboyer2 and Scott Eric Catalano like this.
  18. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    I am very happy to report the code works! :D
    what my issue was is that i had forgotten i had set pins 22 - 31 as sensor inputs when i was testing them. i set the extra track to pin 32, and it works.

    ~Travis
     
    sboyer2 and Scott Eric Catalano like this.
  19. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    there are three places where changes to my code needs to be done to add districts to Main.
    DCCpp.ino
    Code:
    CurrentMonitor mainMonitor(3,A0,96,"<p2>");  // create monitor for current on Main Track
    CurrentMonitor progMonitor(11,A1,96,"<p3>");  // create monitor for current on Program Track
    CurrentMonitor testMonitor(32,A2,96,"<p4>");
    the district testMonitor will be my example. the first parameter is the enable pin for the district. can be any UNUSED pin.

    the second parameter is the analog channel to use for current monitoring.

    the third parameter is the Current Sample Max (formerly CURRENT_SAMPLE_MAX in currentMonitor.h)

    the forth parameter is the debug message. best left in the current format, just increment the number.

    the power for the programming track and the main track are simply power districts. (programming still retains it's own signal pin) keeping in mind that the CV read/write functions were untouched, so it would be good to leave the programming track settings the same. i assume, but have not tested that programming on main still works.

    the second code change needed is in the loop() function of DCCpp.ino
    Code:
      if(CurrentMonitor::checkTime()){      // if sufficient time has elapsed since last update, check current draw on Main and Program Tracks 
        mainMonitor.check();
        progMonitor.check();
        testMonitor.check();
      }
    
      if ((millis() - LastRestartDelay) >= MAIN_LINE_RESET_DELAY)
      {
        LastRestartDelay = millis();
        mainMonitor.Reset();
        progMonitor.Reset();
        testMonitor.Reset();
      }
    an instance of check() and Reset() needs to be added for all districts.

    Code:
    // Options for Travis Farmer's code changes
    //
    
    // timer for reset checking, in milliseconds
    // NOTE: this is a GLOBAL timer. it doesn't reset X milliseconds after a short,
    // it resets when the global timer, set below, comes around to check for a reset.
    #define MAIN_LINE_RESET_DELAY 3000
    
    // how many times the circuit will reset before the main power must be recycled
    #define MAIN_LINE_RESET_MAX 3
    these are in Config.h, at the bottom.
    the delay is how often the polling reset comes around.
    the reset max is how many times the reset will occurs, before it stays off, and then you have to recycle the track power button.

    Also, i updated the VERSION in DCCpp.h to reflect what version i used as the base version, and my own version release (TJF001).

    Hopefully it works for those who use it :)

    ~Travis
     
    Atani, sboyer2, crusader27529 and 2 others like this.
  20. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    just curious, was my programming effort used by anyone?

    ~Travis
     
    Scott Eric Catalano likes this.

Share This Page