Fun with Arduino 30 DCC Servo Decoder

Based on the DCC Accessory Decoder from the previous video we can create a DCC Servo Decoder. A servo decoder can be used to operate turnouts on the model railway layout, or to operate a gate, or garage doors … or to move the arm of a semaphore signal. Or, using a relay, we can switch the polarity of ‘electrofrog’ turnouts, along with operating the servo.

All we have to do is to add the code to operate the servo to the DCC accessory control that already works. That way a DCC address can now operate a servo and/or can switch an output.

In the code, what we need to add is to toggle between two servo setpoint angles based on the DCC status and the code to control the servo with a millis() timer. To find the correct servo angles the Servo Tuner code of video 26 can be used after which we edit these angles into this DCC decoder code.

Read on below the video …

At the top of the code we configure how many servos we have, and at what speed we want to operate them:

#define NUMSERVOS   6 // Enter the number of servos here
#define SERVOSPEED 30 // [ms] between servo updates, lower is faster

// GO TO setup() TO CONFIGURE DCC ADDRESSES, PIN NUMBERS, SERVO ANGLES

#include <DCC_Decoder.h>
#include <Servo.h> 

unsigned long timetomove;

typedef struct DCCAccessoryData {
  int   address;   // User Configurable DCC address
  byte  outputpin; // User Configurable Arduino pin
  byte  dccstate;  // Internal use DCC state of accessory: 1=on, 0=off
  byte  angle;     // Internal use current angle of servo
  byte  setpoint;  // Internal use current angle of servo
  byte  offangle;  // User Configurable servo angle for DCC state = 0
  byte  onangle;   // User Configurable servo angle for DCC state = 1
  Servo servo;
};
DCCAccessoryData servo[NUMSERVOS];

void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data) {
  address -= 1;
  address *= 4;
  address += 1;
  address += (data & 0x06) >> 1;
  // address = address - 4 // uncomment this line for Roco Maus or Z21
  boolean enable = (data & 0x01) ? 1 : 0;
  for (int i=0; i<NUMSERVOS; i++) {
    if (address == servo[i].address) {
      if (enable) servo[i].dccstate = 1;
      else servo[i].dccstate = 0;
    }
  }
}

void setup() { 
// CONFIGURATION OF SERVOS
// Copy & Paste as many times as you have servos 
// The amount must be same as NUMSERVOS
// Don't forget to increment the array index
  servo[0].address   =   1 ; // DCC address
  servo[0].outputpin =  14 ; // Arduino accessory pin
  servo[0].servo.attach( 3); // Arduino servo pin
  servo[0].offangle  =  70 ; // Servo angle for DCC state = 0
  servo[0].onangle   = 150 ; // Servo angle for DCC state = 1

  servo[1].address   =   2 ; // This is an accessory without servo
  servo[1].outputpin =  15 ;
 
  servo[2].address   =   3 ;
  servo[2].outputpin =  16 ;

  servo[3].address   =   4 ;
  servo[3].outputpin =  17 ;

  servo[4].address   =   5 ;
  servo[4].outputpin =  18 ;

  servo[5].address   =   6 ;
  servo[5].outputpin =  19 ;

  DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true);
  DCC.SetupDecoder( 0x00, 0x00, 0 );

  for(byte i=0; i<NUMSERVOS; i++) {
    pinMode     (servo[i].outputpin, OUTPUT);
    digitalWrite(servo[i].outputpin, LOW);
    servo[i].angle = servo[i].offangle;
    servo[i].servo.write(servo[i].angle);
    delay(1000); // wait 1 second before activating the next servo
  }
}

void loop() {
	
  for(byte i=0; i<NUMSERVOS; i++) {
    DCC.loop(); // Call to library function that reads the DCC data
    if (servo[i].dccstate == 1) {
      digitalWrite(servo[i].outputpin, HIGH);
      servo[i].setpoint = servo[i].onangle;
    }
    else {
      digitalWrite(servo[i].outputpin, LOW);
      servo[i].setpoint = servo[i].offangle;
    }
  }

// Move the servos when it is timetomove
  if (millis() > timetomove) {
    timetomove = millis() + (unsigned long)SERVOSPEED;
    for (byte i=0; i<NUMSERVOS; i++) {
     if (servo[i].angle < servo[i].setpoint) servo[i].angle++;
      if (servo[i].angle > servo[i].setpoint) servo[i].angle--;
      servo[i].servo.write(servo[i].angle);
    }
  }
}

When we connect the optocoupler to the DCC voltage and upload this code to the Arduino, we can control servos and accessories. Testing … yes it works!

In the next video we are going to control a stepper motor.

— 0 —

28 thoughts on “Fun with Arduino 30 DCC Servo Decoder

  1. I am interested in making a Digitrax Compatible throttle. I have several designs, that can be modified. One using rotary encoder controls, etc. I have all of the hardware, but, I am not a programmer as such. I have a degree in electrical, and a minor in digital electronics. I am retired now, and am trying to learn all about the Arduino, and controllers. I am very interested in creating this to share with all model railroaders who may be interested. Would you be interested in helping me out with this? I am EXCELLENT in circuit board design, and manufacture, so I can help with this also. The only modifications I can see that need to be done, is the programming. I have several designs of DCC Throttles we can use ideas from. I can help greatly with the electronic design part with you.

    Thanks for your articles. I have been with you for some time, and learned greatly from you tutorials!!!

    THANKS!!!

    Taz…

    Like

    • I have been working on a Loconet Throtle myself. It contains an OLED screen, 4 pushbuttons and a rotary encoder. I have it working on Digikeijs DR5000, but for some reason it does not work flawless on a Digitrax CS. I still have to figure out why but I lack the time to investigate further. As to working together to make it a joint project I would say I’d like that … it is just that at the moment I lack the time needed to invest.

      Like

      • I have TONS of time right now, and my programming skills are improving. I just finished a CTC/ATC Signal control’s for my layout, and I am going to market them at a inexpensive price, where they can be used for ANY size scale, and any manufacture of a signal towers. It IS able to interface with Digitrax for setting traffic control, through the CTC areas. It is automatic with ATC. Sets up immediately upon power up!! I have also made it so the CTC Crossovers, gave their own servo interface(s), for turnout control. I use the target, and newer 3 light signal towers for the era I am modelling, (BN/SANTA FE merger), and they are awesome. My track occupancy controls are IR, and can cover as long of blocks that you may need, without spending a ton of money on, and work reliably, with occupancy detectors in the track ties, where they blend in very well. I will be making a video soon and will send you a copy for y’all to see it work.
        I lost almost $100,000 of HO scale trains and accessories when Hurricane Harvey hit a couple of years ago, so I hope to sell boards and kits to help recover my losses, as insurance did not cover most of my hobby…. Bot I will survive, and build another.

        I also have a 4 function Loco decoder design, that a friend and I have been working on, and hope to test a prototype very soon. It is Arduino based, and will be compatible with Digital command stations.and throttles.
        Let me know if I can help with your throttle. I will do whatever I can if you give me a schematic, and parts list, i will put one together, and we can program it together!! I’m retired, so I have a bit more time sometimes than most….

        Keep up the good work on this blog…. I really enjoy reading it, and learning from you!!

        Like

      • Next week I’ll send you a couple of pictures and the code of the sketch as I have so far. Maybe you can make something out of it.

        Like

      • Sounds good Rudy. I will help all I can, and as much as I can. We can do this together….
        I look forward to it!!

        Taz…

        Like

      • Hello Rudy!
        Have you finished your Throttle? May you share it with us, please?
        Thanks in advance!
        Otto

        Like

      • Well, the status has not changed, I never found a solution why I could not get it to work on Digikeys and I lost the energy to try figure it out … too many other more pleasant things to do! 🙂 It’s not in a state to share publicly, but if you’d like to have the code I can send it to you.

        Like

  2. Hi Rudy,
    Thanks for this great work, it’s a huge help.
    Question: Is it possible to control two Arduino pins with one DCC-Adress-command at same time, for example to control a frog relay together with the servo?

    Like

    • I don’t know why you would need two relays for the frog polarity, but if you do … yes, it is perfectly possible to control two pins … you can just configure another pin with the same DCC address.

      Alternatively you can add a ‘pinB’ to the structure that contains the data for every DCC address, add it in the configuration blocks, and in loop() also send out this pinB output.

      Like

      • Thanks for your answer – will do so.

        Just to explain the question: As I understood you need one pin for the servo and you would need another for the frog-relay. I don’t see how to make this with one pin for both purposes.
        But in my case I have another, but similar problem: I have to control KATO-turnouts. They are Japanese style: one solenoid only, controlled via polarity change. The simple way is to have two relays, one taking care for polarity and one for the switching impulse.

        For a single turnout the double use of DCC-address would the way. For a bunch of such turnouts the ‘pinB’-solution would be the better idea, as you can use the ‘polarity’-relay for all turnouts and needs to add only one ‘switch impulse’-relay for each additional turnout.
        Thanks again for your help!

        Like

      • Yes, you have one servo pin plus one relay pin. When you have to switch two relays, then no need for a servo, just use two accessories on the same address. However … there is more to it. The coil relay needs to be a pulse with limited time length, or you will probably burn the coil. Also, the coil relay needs to get its pulse both on a DCC 0>1 and a DCC 1>0. This can be made: add detection for both the 0>1 and the 1>0 transition and add a timer to generate the pulse.

        Like

    • I don’t know why you would need two relays for the frog polarity, but if you do … yes, it is perfectly possible to control two output pins … you can just configure another pin with the same DCC address.

      Alternatively you could add a ‘pinB’ to the structure that contains the data for every DCC address, add it in the configuration blocks, and in loop() send out this pinB output along with the pinA.

      Like

  3. I downloaded your sketch on the servo motor controller and attached it to the dccpp and rocrail on a raspberry pi. The problem is I cannot get the servos to operate. I download your sniffer sketch and although it tries to read the dcc data the information is not the same as what rocrail is giving me. The TX led is flickering when commands are sent.
    Can you guide me on what to do?
    The dccpp has its baud rate fixed to 115200baud, and the dcc servo does not have a field to adjust it to this value.
    I first tried the dcc servo setting sketch and the servos work.

    Best regards

    Chris

    Like

    • I don’t expect it, but to eliminate the possibility that it has something to do with the servo’s, can you set a DCC accessory output to pin 13 so you can see if the DCC comes thru … the Arduino on board LED should now light when you toggle the DCC address.

      If that does not work, then probably the DCC data that the Arduino sees is not correct. The most common problem is that the optocoupler circuit is not working as it should. The fact that the DCC sniffer gives different results than Rocrail also hints in that direction. It is difficult to diagnose … if you have an oscilloscope or data logger you could have a look at the purity of the DCC signal on Arduino pin 2. Else … build another optocoupler circuit and test again.

      Like

      • Hi Rudy,
        I still could not get any data on sniffer, so I connected sniffer to my lenz controller, and Sniffer read data.
        I then installed Jmri and data was read once again.
        However not data was read when Rocrail was uploaded.
        In each case I used my Lenz controller.
        Did you or anybody else have this result?

        Chris

        Like

      • Thanks Rudy. If any of your viewers may have the same problem I would appreciate it if you pass him on to me.

        Like

  4. Hi Rudy,
    your sketch hits that what i´am looking for. The item 6N137 is working well – and lazy as i´am, i found a already configured part, which works very well. See link. I´ve ordered some items – and in the 24V Version does, what it has to do.

    https://www.ebay.de/itm/Optokoppler-Optokoppler-Isolation-Board-6N137-5V-NPN-Eingang-3-3V-24V-DC/163171595152?hash=item25fdc8f390:m:mrIKMOh-Yck_7nqp4iRbRkw

    But my question is how many servos can i max. use with e.g. Mega2560, Uno or Pro Mini.
    Do you have a number of possible servos which i can use. May be you talked about in your video, i looked it serveal times, but i do not find the position in your video, where you talked about it.
    I mean, that i had read anywhere in the past, that the servo.h library allows a max of 48 servos – that would be heaven on earth.

    How many pinouts can i use max for accesorries – a Mega has quite a lot of them – would be great as well. Hope for some more info – thx4help

    Greets Arno

    Like

    • On an UNO max 10 servos can be used. This is a limitation of the servo library. For the MEGA I don’t know the max number of servos, maybe it can be found in the MEGA or servo library documentation on the Arduino website. As for accessory pins … you can use as many as the hardware has available.

      Like

  5. Hello Rudy,

    as i can see you put on the pin for the servo with
    servo[0].servo.attach( 3); // Arduino servo pin –

    this work well, but the servo is allways on, the servo is trembling.allways
    i tried to put a „detach“ in the code – does not work, servos has stopped trembling, but dont working anymore. Do you have an Idea, without „detach“ servos wont stop trembling

    Greets Arno

    Like

  6. Hello, I am eager to use this decoder circuit. However, I am finding that when I use more than 2 servos in the sketch with an 8MHz 3.3V Arduino Pro Mini, I often miss DCC instructions and have to resend them multiple times before the decoder “sees” the command. Is this the result of using a slower chip? Perhaps I am missing interrupts? I’ve tried adjusting multiple parameters but so far the only way to increase reliability is to reduce the number of servos (which I assume means that the longer for loops are taking too long to execute). I’d really like to avoid having to start over.. but I could use a pro Micro running at 16 MHz 5V if that is the only solution. Thanks in advance for any suggestions.

    Like

    • Hi. I’ve sent you an email with a sketch that controls 10 servos and that works here on my own layout. First try you could do is plave the DCC.loop() statement inside the ‘for’ loop, else use the code I sent, which works a little different. As an alternative you could have a look at http://www.arcomora.com and use the free MARDEC code, then you can configure everything via PC user interface.

      Like

Leave a comment