SMA12 - 17 Channel Configurable Multifunction $5 DCC Decoder For Servos

geoffb's picture

One of my earlier entries:  SMA10 – Build a 17-Function DCC Decoder for about $5 ( http://model-railroad-hobbyist.com/node/19070 ) generated a considerable amount of interest regarding the possible enhancement for controlling Servomotors (Servos). This is my next version of a 17 Channel Multifunction DCC Decoder based on a low cost $2.56 Arduino Pro Mini. This version supports configuring each of the 17 function pins for On/Off (LED/TTL) Control, or Configurable Blinking Control, or Configurable Servo Control, or Configurable Pairs Blinking Control. Yes, that does mean it can support 17 servos, each with rate, start point, and end point setting via DCC CV’s, per pin, as well as new LED configurable functions. Read On. Additional doc found here: http://model-railroad-hobbyist.com/node/19775 There is another decoder version added herein. Look for "New Decoder Version to Control Lighting Groups" in Page 12 of the Comments: http://model-railroad-hobbyist.com/node/19446?page=11   The most recent Update can be found here: SMA20 New Low Cost 17 Channel DCC Decoders with PC Boards & Dual Motor, LED, & Servo Control    http://model-railroad-hobbyist.com/node/24316

Comments

Thank You Geoff

That is very good to hear as I have already purchased 12 Pro Mini's and 30 servos.

Don

GregW66's picture

My background is in

My background is in electronics and computers, yet I had ignored this thread until recently. Having read it and understand how easy it really is, this will solve my problem of how I will throw my turnouts. I had always thought that servos could work but this nails it home for me. A very elegant and more importantly, inexpensive solution.

GregW66

 

 

GregW66's picture

Turnout control

I am thinking of using this and servos to control turnout points. Last night I couldn't sleep so was thinking about this. When the decoder resets, will it also reset the position of the servos? I am thinking of a situation where the DCC buss experiences a short and it resets, will this remember the position of the servos?

GregW66

 

 

geoffb's picture

@GregW66 re:decoder memory

Hi Greg,

where the DCC buss experiences a short and it resets, will this remember the position of the servos?

The short answer: No, it does not remember the servo position. In fact, upon power up it sets each of the servos to a known starting position.

The long answer: The code was actually set up to remember the position of the servos! But...decoders (other than CV settings) are actually assumed not to remember their last commands! When a decoder powers up (and the DCC system too) you may set, let's say, F0 on, but what actually happens if that the command station sends a packet (collection of encoded bits) to the decoder to set F0 on and, if it is the first command sent to the decoder, F1, F2, F3, and F4 would all be set off simultaneously! So even if you remembered the state of the serovs corresponding to F1, F2, F3 and F4 they would be changed with the first packet sent to change any one of the F0-F4 functions. Likewise, upon power up the command station would have no memory of accessory decoder settings, if you were to use the accessory decoder configuration to set switches, although the command packet is different. There would still be inconsistency between what the command station knew and the servo settings. So, I thought it would be better to have all the servos power up to a known position, which is what is implemented (all corresponding to functions off).

These decoders can be configured to remember their last positions, but I don't know how to resolve the base station consistency problem. It is actually a DCC "system" wide issue, not a decoder issue.

There is another issue that most people also don't realize. Dcc packets can and are repeatedly sent out to decoders, intentionally. This was originally meant to increase the reliability of the communication to decoders when operating with dirty or gapped trackage. So functions and switches are meant to only be a mirror of the throttle/base station information, and not intelligently initiating or assuming control of any functions.

If you have more considered thoughts on this I would welcome them.

Have fun!  smiley

Best regards,

Geoff Bunza

A little late to the game, but a question

Can this be used "stand alone"?

 

I don't have a need for DCC operation, but would what you have going on here looks like a simple / cheap way to control many servos.

 

Thanks 

John

 

geoffb's picture

@John re:stand alone use

Hi John,

Can this be used "stand alone"?

Not precisely as is, but with a new sketch (program) Yes! it sure can be used as a stand-alone controller for Servos. You would need at least one button per servo to throw the switch in the other direction, and a LED indictor (perhaps) to show how the servo is positioned. At best this would halve the number of servos one Pro Mini could handle strictly by itself (that is without additional hardware). The bigger issue would be to define a way to set the throw limits of each servo (side to side)-- likely requiring more pins to be used-- my guess is at best another 2. So without giving it more thought, you could have one Pro Mini Arduino set up and control 8 servos from a panel, with indicators and servo set up (18 pins available, 2 per servo, 2 for set up).

Sounds like another project to put on the to-do pile!

Have fun!  smiley

Best Regards,

Geoff

thanks for the info

Geoff,

 

Thanks for a great reply.

 

I am currently using the Octopus 8 from Tam Valley and it works well.  I will need about a 100 servos on the layout and was looking for  a simple / cheap way as an alternative to Tam Valley.

 

Will keep following along for sure.

 

Thank You

John

 

 

Arduino as a Transmitter?

Could an Arduino be used as a "transmitter" to initiate the DCC commands? I'm thinking of one of your decoders in each building or scene and a buss completely independent from track control to run everything other than the trains. I'm liking the idea of Arduinos for each scene but shudder to think of controlling them all from a throttle as thing get more complicated. Bob

geoffb's picture

@Bob re: Arduino as DCC Throttle/Base Station/Controller

Hi Bob,

Could an Arduino be used as a "transmitter" to initiate the DCC commands?

Absolutely yes! it's desirable in many modeling instances to use an Arduino as the DCC command originator.

Preamble (Bear with me for a bit):
Realize that what is standardized with DCC decoders is fundamentally the communication protocol, not their use. Consider any decoder as a 2-wire control interface to motors, lights servos, relays, etc. This decoder series attempts to show you can control all these with a 2-wire connection. In really small animated models --like a helicopter (http://model-railroad-hobbyist.com/node/23598) or small float plane (http://model-railroad-hobbyist.com/node/14762) minimizing the wire connections is important.

Further, there is no reason why you couldn't run multiple DCC busses around a layout-- say one for train control, one for signalling and one for animation. Cost would be an obvious limitation. But suppose you could have DCC control without the overhead that an NCE, Digitrax, or other system provides-- Much fewer features, but radically less cost-- say for $8 or less. I am not talking about a DCC base station / throttle look-alike.

I use an Arduino-based DCC controller in both the operating helicopter and the Floatplane animations.These were controlled entirely by a single Arduino Pro Mini running the CmdrArduino library by Don Goodman-Wilson (see http://railstars.com/software/cmdrarduino/ and download from https://github.com/Railstars/CmdrArduino). It takes a little getting used to, but it does work! Following here is the Arduino Sketch I used for controlling the Helicopter. You'll note that DCC commands are often resent to the decoder. Much of what a commercial base station does for you is in your hands when you do something like this.

But realize, that it becomes entirely possible to have a dedicated "DCC Command Station" (in principle) for a single animation-- pretty neat! Other modelers on this MRH forum have also written up their work on creating mini DCC base stations too. Use the search function in the top right of these pages to find them.

As an aside I am getting some encouragement to write these up, so they are being put on the pile of "to-do" projects-- no timeframe or ETA.

Have fun!  smiley

Best regards,

Geoff

/********************
SOUND via custom programmed Digitrax SDN144PS
* The DCC waveform is output on Pin 9, and 10 (inverted)
*
********************/
#include <DCCPacket.h>
#include <DCCPacketQueue.h>
#include <DCCPacketScheduler.h>
#define funct0 0x01
#define funct1 0x02
#define funct2 0x04
#define funct3 0x08
#define funct4 0x10
#define DCC_EN 2
#define p1     8    // F0 Red/Green Navigation Lights
#define p2     7    // F2 Rear Red Rotating Beacon
#define p3     6    // F3 Cabin Light and Lower Spot Light
#define p4     5    // F1 Engine Control Turbine Start/Stop & Engine Revs
DCCPacketScheduler dps;
uint8_t functs = 0;
uint8_t F0 = 0;
uint8_t F1 = 0;
uint8_t F2 = 0;
uint8_t F3 = 0;
uint8_t F4 = 0;
uint8_t F40 = 0;
uint16_t decoder = 3;
int8_t last_locoSpeed=0;
uint16_t locoAdr=3;  
unsigned long timing_start,timing_end;
int lit_time= 5 ;
int8_t dcc_speed = 1;

void setup() {
  pinMode(p1, INPUT);    
  pinMode(p2, INPUT);  
  pinMode(p3, INPUT);  
  pinMode(p4, INPUT);
  digitalWrite (p1,HIGH);
  digitalWrite (p2,HIGH);
  digitalWrite (p3,HIGH);
  digitalWrite (p4,HIGH);
  dcc_speed = 1;
  functs = 0;
  pinMode(DCC_EN,OUTPUT);    // will be kept high, DCC_EN pin
  digitalWrite(DCC_EN, 1);
  delay(6000);
  dps.setup();
  delay (5);
  dps.setSpeed128(locoAdr,DCC_SHORT_ADDRESS,dcc_speed); // Speed 0 forward
  delay (10);
}
void loop() {  
    if ((digitalRead(p1)&&digitalRead(p2)&&digitalRead(p3)&&digitalRead(p4))==HIGH) {
      while (true) {
        functs = funct2;                  // Rotating Rear Beacon ON
        dcc_cmd_repeat ( lit_time );
        functs = funct0|funct2;           // Turn Red/Green Navigation Lights ON too
        dcc_cmd_repeat ( lit_time );
        functs = funct0|funct2|funct3;    // Turn Cabin Light and Lower Spot Light ON too
        dcc_cmd_repeat ( lit_time );
        functs = funct0|funct1|funct2|funct3; // Start Turbine/Engine Sound
        dcc_cmd_repeat ( 8 );
        dcc_speed = 7 ;                      // Start Engine Rotation
        dcc_cmd_repeat ( 1 );
        dcc_speed = 8 ;                      // Start Engine
        dcc_cmd_repeat ( 1 );
        wait_dcc(50);
        dcc_speed = 9;                      // Rev Up Engine
        dcc_cmd_repeat ( 2 );
        dcc_speed = 10;                      // Rev Up Engine
        dcc_cmd_repeat ( 2 );
        dcc_speed = 11;                      // Rev Up Engine
        dcc_cmd_repeat ( 3 );
        dcc_speed = 12;                      // Rev Up Engine
        dcc_cmd_repeat ( 3 );
        dcc_speed = 13;                      // Rev Up Engine
        dcc_cmd_repeat ( 2 );
        dcc_speed = 14;                      // Rev Up Engine
        dcc_cmd_repeat ( 1 );
        wait_dcc(700);
        dcc_speed = 15;                      // Rev Up Engine
        dcc_cmd_repeat ( 14 );
        functs = funct0|funct2|funct3;        // Start Turn Off Engine
        dcc_cmd_repeat ( 1 );
        dcc_speed = 15;                      // Rev Down Engine
        dcc_cmd_repeat ( 3 );
        dcc_speed = 14;                      // Rev Down Engine
        dcc_cmd_repeat ( 2 );
        dcc_speed = 13;                      // Rev Down Engine
        dcc_cmd_repeat ( 3 );
        dcc_speed = 12;                      // Rev Down Engine
        dcc_cmd_repeat ( 2 );
        dcc_speed = 11;                      // Rev Down Engine
        dcc_cmd_repeat ( 4 );
        dcc_speed = 10;                      // Rev Down Engine
        dcc_cmd_repeat ( 4 );
        dcc_speed = 9;                      // Rev Down Engine
        dcc_cmd_repeat ( 4 );
        dcc_speed = 8;                      // Rev Down Engine
        dcc_cmd_repeat ( 4 );
        dcc_speed = 6;                      // Rev Down Engine
        dcc_cmd_repeat ( 5 );
        dcc_speed = 1;                      // Rev Down Engine
        dcc_cmd_repeat ( 4 );
        functs = funct2|funct3;
        dps.setFunctions0to4(locoAdr,DCC_SHORT_ADDRESS,functs); // Nav Lights Off
        wait_dcc(lit_time*600);
        functs = funct3;
        dps.setFunctions0to4(locoAdr,DCC_SHORT_ADDRESS,functs); // Top Light Off
        wait_dcc(lit_time*600);
        functs = 0;
        dps.setFunctions0to4(locoAdr,DCC_SHORT_ADDRESS,functs); // F3 cabin lights Off
        wait_dcc(lit_time*600);
        functs = 0;
        dps.setFunctions0to4(locoAdr,DCC_SHORT_ADDRESS,functs); // F3 cabin lights Off
        wait_dcc(lit_time*600);
        functs = 0;
        dps.setFunctions0to4(locoAdr,DCC_SHORT_ADDRESS,functs); // F3 cabin lights Off
        wait_dcc(lit_time*2200);
      }
    }
    else {
      while (true) {
        if (digitalRead(p4)==HIGH) {
          
          functs |= funct1 ;
          dcc_cmd_rpt ( 8 );
          dcc_speed = 7 ;                      // Start Engine
          dcc_cmd_rpt ( 1 );
          dcc_speed = 8 ;                      // Start Engine
          dcc_cmd_rpt ( 1 );
          wait_dcc(50);
          dcc_speed = 9;                      // Rev Up Engine
          dcc_cmd_rpt ( 2 );
          dcc_speed = 10;                      // Rev Up Engine
          dcc_cmd_rpt ( 2 );
          dcc_speed = 11;                      // Rev Up Engine
          dcc_cmd_rpt ( 3 );
          dcc_speed = 12;                      // Rev Up Engine
          dcc_cmd_rpt ( 3 );
          dcc_speed = 13;                      // Rev Up Engine
          dcc_cmd_rpt ( 2 );
          dcc_speed = 14;                      // Rev Up Engine
          dcc_cmd_rpt ( 1 );
          wait_dcc(700);
          dcc_speed = 15;                      // Rev Up Engine
          dcc_cmd_rpt ( 8 );
          while (digitalRead(p4)==HIGH) {
            dcc_cmd_rpt ( 2 );
          }
          functs = (functs&(~funct1))&0x1f ;  // Start Turn Off Engine
          dcc_cmd_rpt ( 1 );
          dcc_speed = 15;                      // Rev Down Engine
          dcc_cmd_rpt ( 3 );
          dcc_speed = 14;                      // Rev Down Engine
          dcc_cmd_rpt ( 2 );
          dcc_speed = 13;                      // Rev Down Engine
          dcc_cmd_rpt ( 3 );
          dcc_speed = 12;                      // Rev Down Engine
          dcc_cmd_rpt ( 2 );
          dcc_speed = 11;                      // Rev Down Engine
          dcc_cmd_rpt ( 4 );
          dcc_speed = 10;                      // Rev Down Engine
          dcc_cmd_rpt ( 4 );
          dcc_speed = 9;                      // Rev Down Engine
          dcc_cmd_rpt ( 4 );
          dcc_speed = 8;                      // Rev Down Engine
          dcc_cmd_rpt ( 4 );
          dcc_speed = 6;                      // Rev Down Engine
          dcc_cmd_rpt ( 5 );
          dcc_speed = 1;                      // Rev Down Engine
          dcc_cmd_rpt ( 4 );
          wait_dcc(lit_time*2600);
        }
        dcc_cmd_rpt ( 1 );
      }
    }
}
void dcc_cmd_rpt (int seconds_cnt ) {
   for (int ii=0; ii < seconds_cnt; ii++)  {
        if (digitalRead(p1)==HIGH) functs |= funct0;
          else functs = (functs&(~funct0))&0x1f ;
        if (digitalRead(p2)==HIGH) functs |= funct2;
          else functs = (functs&(~funct2))&0x1f ;
        if (digitalRead(p3)==HIGH) functs |= funct3;
          else functs = (functs&(~funct3))&0x1f ;
        dps.setSpeed128(locoAdr,DCC_SHORT_ADDRESS,dcc_speed); // Start
        dps.setFunctions0to4(locoAdr,DCC_SHORT_ADDRESS,functs); // Turbine sound on
        wait_dcc(949);
      }
}
void dcc_cmd_repeat (int seconds_cnt ) {
   for (int ii=0; ii < seconds_cnt; ii++)  {
        dps.setSpeed128(locoAdr,DCC_SHORT_ADDRESS,dcc_speed); // Start
        dps.setFunctions0to4(locoAdr,DCC_SHORT_ADDRESS,functs); // Turbine sound on
        wait_dcc(950);
      }
}
int wait_dcc (int wait_time) {
  unsigned long tempul;
  tempul = wait_time;
  timing_start = millis();
  timing_end = timing_start+ tempul;
  while (timing_start<timing_end) {
  dps.update();
  timing_start = millis();
  }
}

Thanks

For someone who has never used an arduino, I can see I've got some studying to do. In your sketch I can see where you defined pins 1-4 as inputs but don't see where you defined anything as outputs for the DCC signal. Coming from a discrete component background, the learning curve looks like a cliff from here but I'll take it one step at a time. Long live the 555! smiley I think I'll build your 17 channel decoder first and test it with my NCE throttle then tackle the encoder portion of the show. In the end I'd like to automate a day/night cycle with the appropriate business opening and closing, lights coming on and layout lights dimming through pwm to my led drivers. Thanks, Bob


>> Posts index


Journals/Blogs

Recent Blog posts: