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

Hello Geoff, thank you very

Hello Geoff,

thank you very much for the "special" version of the decoder program. When I try to compile it, I receive an error message " ´FN_GROUP´ has not been declared". 

Have tried to find the problem, but could not solve it. Do you have a hint for me?

Best Regards

Loeter

 

geoffb's picture

@Loeter

Hi Loeter,

Yoe are welcome!  smiley

I strongly suspect you are not using the newer NmraDcc library (Since this is SMA 12-- look in my blog for the later entries-- http://model-railroad-hobbyist.com/blog/geoff-bunza ). You can download the later library and examples here-- http://home.comcast.net/~gbglacier/Articles/New-Dual-MULTIFunction-Decod...

Remember to delete the old library in your .../Arduino/libraries/...  folder, usually found (in Windows machines) in your Documents folder.

Have fun!  smiley

Best regards,

Geoff

Toniwryan's picture

Control panel

Geoff,

  After reading most of the threads related to this project, I am curios about the possibilities of using an NCE "mini panel" or a second Arduino pro-mini to read the input switches and send a "DCC message" to the SMA 17.  This would ease up the timing constraints and complex coding issues.  A third SMA 17 could be set to respond to the same address messages as the servo controller, but be used to control indicators, and signalling.  Is there a simple library to allow us to SEND DCC commands from the Arduino?  I am envisioning this being used on a small shelf style layout, so the Arduino DCC bus would not necessarily be connected to the main DCC bus (though that might be nice to consider). 

The other question is related to crossovers and controlling two servos off one output.  Would this be possible, or would it be better to "gang" the servos in software to have them controlled by one input?

All these separate Arduino controls are starting to sound like LCC ...

Toni

 

Toni

geoffb's picture

@Toni re: Arduino as a DCC Control / Interface

Hi Toni,

You are right on target.Not only is it possible, 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.

Back to your question Toni:

Is there a simple library to allow us to SEND DCC commands from the Arduino?

Answer-- Yes-- There are a couple. The one I used in both the operating helicopter and the Floatplane animation orchestration were conducted 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!

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.

The other question is related to crossovers and controlling two servos off one output.  Would this be possible, or would it be better to "gang" the servos in software to have them controlled by one input?

I have successfully driven 2 servos with one output pin-- However, the cheap 9G servos I use do not  behave alike at all. One may swing from 15-175 and the next from 35-168. The reason why this can be a problem is that the current draw from many of these goes way up when you attempt to drive them beyond their limits. It is easy to simply modify the Arduino sketch so one function or Accessory address drives 2 or more servos on different pins. If you need help with this, just ask.

All these separate Arduino controls are starting to sound like LCC ...

In a way, perhaps yes. But my use of these is not for "global layout control" (my words alone), but rather for what might be considered to be "local" or "distributed" control. It's a bit different way of viewing the modeling world. I believe both are entirely appropriate, and I think LCC has much potential.

Have fun!  smiley

Best regards,
Geoff Bunza

/********************
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();
  }
}

 

Very Nice Result

Hello Geoff,

after adding the actual version of nmracdcc.h, it works fine. Now I understood the use of arrays and it is very easy to change the signal aspect to other types of signals. Your program has a big potential for further features and after seeing the result, I try to find additional applications.

Thanks a lot and

Best Regards

Loeter 

geoffb's picture

@Loeter re:Very Nice Result

Hi Loeter,

I'm glad you like your results.

I tried to adjust the fade on and off to something that looked realistic. You can adjust the fade timing yourself by changing the value of fadedelay. Also, the value of 60 incremental steps for the fade was chosen experimentally (that is by trial and error) until it looked appropriate. This is another value you can adjust. See the partial example code below:

void exec_function (int f_index, int FuncState)  {
  #define fadedelay 5
       if ((FuncState==1) && (!Last_Function_State[f_index])) {
              for (int i=0; i<60; i++) {
                for (int j=0; j<5; j++) if (Function_Lites[f_index][j]==1) digitalWrite( FPins_Assigned[f_index][j],1);
                delay(fadedelay*i/60.0);

There are a couple of unused pins, too. So you could consider using them to activate a relay or servo in conjunction with the signal change. This is one of the real values in using these-- they are really flexible and very low cost. Enjoy.

Have fun!  smiley

Best regards,

Geoff

Help Wanted

Hi Geoff,

Please excuse my English, I use the Google Translater.

I have been following with great interest and admiration of your performance your publications.
I can set the decoder from the article SMA10 successfully worked super on my Z21 Roco as an accessory decoder.
Unfortunately, I do not understand the procedure for the following decoders.

Unfortunately I have no end levels to understand'deine following versions:

--------------------------------------------------------------------------------------------------------------------
Open one of the sketches (programs), set your decoder address, and you are good to go! Once you have loaded the Pro
Mini, remove the “//” in the line: “//#define DECODER_LOADED” so it looks like below:

/ ******** UNLESS YOU WANT ALL CV'S RESET UPON EVERY POWER UP

// ******** AFTER THE INITIAL DECODER LOAD REMOVE THE "//" IN THE FOOLOWING LINE!!

#define DECODER_LOADED

And then RELOAD the Pro Mini. This will set up all the default CV’s and then permit you to modify them with your DCC
controller of choice. You can even reset the short address in CV 1.
--------------------------------------------------------------------------------------------------------------------

If time permits, ask a few words of explanation. I'll unfortunately else
not further.

Thank you for your work, Geoff

Ingo

Ingo.Faehse@t-online.de

geoffb's picture

@Ingo re:Start-Up Code Explanation

Guten Tag Ingo,

I'm glad you got your decoder to work.

Your question about this portion of the sketch (program) is a good one!

Like commercial decoders, we want to program an address and starting CV Values into our decoders and have the decoder remember these values. In the Arduino Pro Mini there is a small memory (called an eeprom -- Electrically Erasable Programmable Read Only Memory) which can be read and written that will retain its values even when powered off and on. This memory is used to store the decoder address. However, when you purchase a new Arduino, there is no guarantee whatsoever of what is in the eeprom memory. Unfortunately the Arduino does not have a way of setting the eeprom memory to known values before the program starts. Some people have relied on hoping that the values of the eeprom are all 0's but in reality this is not reliable. Many manufacturers load the boot program and the "Blink" program to test their Arduinos, and others may run more thorough tests-- including testing the eeprom, so we cannot rely on what is in the eeprom.

The code You refer to is used to do a first load, or a "cold" load of the Arduino with no assumptions made. Once the initial values are set, you can program them with Ops Mode DCC Programming. But to allow the new values to be programmed and saved, the address needs to be set, and the initialization code needs to be "turned off."

When you remove the “//” in the line: “//#define DECODER_LOADED” your action is un-commenting the line, making it:  #define DECODER_LOADED  which tells the program not to initialize the eeprom CV's later in the startup()  code .

If you always wanted the initial address and CV values to be reset every time the decoder powers on, leave the line commented (untouched).

I hope this makes it more clear. If not,  please ask more questions.

Have fun!  smiley

Best Regards,

Geoff Bunza

You lost me at the first hurdle

Geoff,

You are clearly  an intelligent  and informed man, but your first paragraph  lost me entirely.

I'm  not ignorant or uneducated but it is just gobbly-gook to me because I don't  speak  your  language.

If I started a thread with 'Practical waste removal on hand-cut concealed  mitre dovetails ' would you be  any wiser, or want to look deeper into my  offering?

Perhaps if I had written 'How to cut an invisible strong joint in wood so that no end-grain is left showing' I might have helped you decide whether or not the subject was for you.

What I often find is that we tend to think that everyone reading MRH speaks our own technical patois,  it ain't  so.

I accept that you don't  need to explain what a servo is/does, but, 'Arduino'? It sounds like an

Italian sea-food dish....

Electronic threads that start with 'How to stop your locomotives hitting the buffers...' or controlling  a semaphore signals and interlocking them with turnouts using servos' woulld grabmy attention.

I once went to a school to give a talk on 'Trees, and the part they play in our daily lives' and on arrival discovered my audience were 8-9 year olds. My charts with range and distribution, xylem structure, photosynthesis etc went out the window, we drew trees onthe chalk-board with little birds in them, and stressed how no-one shold vandalise a tree.
Please give your stuff a 'chalk-board' title for electronic 8-9 year olds like me, I might even be encouraged to try and learn something....

Respectfully, Doug.

Jim at BSME's picture

Starting point

Doug, of course that is one of the problems with this type of forum, if you don't start at the beginning of the thought process, one can easily be lost. And since the threads get displayed on the recent blog posts by when any response happens, you might see this post even though you haven't see the beginning threads to give you background.

Geoff has written many blog posts about Scale Model Animation (SMA) I think he is up to 18!  I don't remember what all they are, but if you type scale model animation in the MRH search box and hit search, you will find them all.  Some of the early ones were showcases of Geoff's work, but for the Ardunio I would start at: Scale Model Animation 8: Starting Projects and Virtual Rooms for Modeling.

You can always Google Ardunio to find out what it is, here is the technical definition: "Arduino is an open-source computer hardware and software company, project and user community that designs and manufactures microcontroller-based kits for building digital devices and interactive objects that can sense and control the physical world."  This definition to me is overly complicated knowing what an Ardunio is and having used them. I think the best way is to think of it as a micro sized (~3/4" x ~1.5" for the mini pro) computer that you write programs (called sketches) for to read input (e.g. button push), and write output (e.g. turn on/off LEDs)

By the way Ardunio started at Interaction Design Institute Ivrea in Ivrea, Italy.

Doug can you edit your post to remove all the blank lines after you signature?  If they are not in your post then it just must be a bug in the display of your post.

Thanks, and I hope my explanation helps,

- Jim B.
Baltimore Society of Model Engineers, Estd. 1932
O & HO Scale model railroading
Check out BSME on: FacebookInstagram

>> Posts index


Journals/Blogs

Recent Blog posts: