Jump to content

eldavo

Arduino accessory decoder experiments

Recommended Posts

 

You know how it happens, you start looking at one problem and you end up solving another. I was looking at ways to maximise the utilisation of the power generated by my solar panels by controlling the distribution of the power in my house using a microcontroller and some bits. This led me to the Arduino family of microcontroller boards.

 

These boards are reasonably cheap, quite capable and dead easy to programming as there is a ton of free bits of software available. I still haven't sorted out my household power control (yet) but I've learned a lot about the technology and started to look for other things to do with it.

 

My local model railway club, Winchester Railway Modellers, is currently building a new exhibition layout, Redbridge Wharf. More details here: http://www.rmweb.co.uk/community/index.php?/topic/58444-wrm-redbridge-wharf/

 

The fiddleyard for this layout has over 40 turnouts which would break the bank if we used Tortoise motors. We need something cheaper but don't fancy subjecting our handmade track to the thumping of solenoids. Servos seem like a good idea. We would also like to be able to operate the layout using conventional DC switching or DCC magic. We need a servo controller and DCC accessory decoder that is cheap(ish). This is where the Arduino comes in.

 

The Arduino can control a number of servos using a single wire to each. Each servo is connected to 0 volts, +5 volts and one of the PWM outputs of the Arduino. Using a PWM output is native to the microcontroller and requires no extra circuitry. The 0 and 5 volt lines are taken from an external power supply, probably one salvaged from an old PC.

 

What I have done is to hook up a couple of servos in this manner plus a couple of toggle switches. Again the Arduino can easily detect switch input, just switch one of the digital inputs to 0 volts. The toggle switch is used to detect whether the servo should be thrown to the left or right. That's DC control sorted then.

 

You need to be able to calibrate the throw for the servos really so another pushbutton switch has been added. Pushing this starts a calibration sequence on the last servo switched. One push and the servo centres, push again and the servo starts slowly stepping right, push again and the limit of throw is set and the servo returns to centre then starts slowly stepping in the other direction. Push the button once more and the other extreme throw is set and we are done.

 

So thats's DC control and calibration sorted what about DCC? Luckily some folks have thought of this before and come up with a solution (or two). A bit of external circuitry is added using an optoisolator (to prevent the white smoke escaping from the Arduino) providing a connection to the controller. The Arduino detects the pulses of the DCC signal and interprets them as individual message packets. Just need to look for the accessory messages and activate the relavent servos. Easy peasy.

 

Here's a pic of my breadboarded test set up.

 

IMG_20130405_121809_zps639f07b1.jpg

 

It 'ain't pretty but it works. The white box on the right is a cheapo 12v power supply as used in LED lighting rigs. It's feeding the Arduino.

 

If a DCC signal is detected the toggle switches are ignored so the one lash up works happily as a servo controller with or without DCC controller.

 

I've tested it with my Sprog and also a friend's ZTC 511 and all seems OK. Currently it doesn't work with my Roco Multimaus so there is some debugging to do.

 

There is also an embryonic command line interface which allows commands to be entered from a PC connected to the USB interface so I think it would be fairly simple to set up a scheme whereby the settings could be extracted from a board and reloaded. Quite useful if you need to replace a faulty board quickly.

 

I haven't yet got it controlling a Tortoise motor which would be cool as we have some of those to control on the scenic part of the layout. I'm sure it can be done with a bit of fiddling. There's also a lot of cleaning up needed in the code. All good fun.

 

Cheers

Dave

Edited by eldavo
  • Like 3

Share this post


Link to post
Share on other sites

Sounds promising, not sure how your costs will come out compared to other published build your own devices, but more designs are always welcome.

 

Are you aware that the addressing of accessories is different on Roco compared to many other makers? Try changing the expected address of the turnout by 4 and see if it works. There is also some toggling on/off of accessory messages which may be relevant.

 

Not sure if the board you are using can invert the power for tortoise. If not, look up H-bridge circuits/boards to act as drive, or just operate a relay which then switches the tortoise.

 

Nigel

Share this post


Link to post
Share on other sites

Although tortoises are specified for higher voltage they will run from 5V so you can just connect one between two outputs of the Arduino if you don't mind the tortoise being a little slower and quieter than normal. Current consumption is in the order of 6mA (less than an old fashioned LED) so it should not overload the Arduino.

Share this post


Link to post
Share on other sites

Are you aware that the addressing of accessories is different on Roco compared to many other makers? Try changing the expected address of the turnout by 4 and see if it works. There is also some toggling on/off of accessory messages which may be relevant.

 

 

Not sure if the board you are using can invert the power for tortoise. If not, look up H-bridge circuits/boards to act as drive, or just operate a relay which then switches the tortoise.

 

Nigel

 

Spot on Nigel. I set the address + 4 in my Multimaus and all works just fine. Hadn't realised this oddity of the Roco system. Might be able to accomodate it in the decoder software with a bit of research.

 

As to cost.  An Arduino Nano can be had for about £8 to £10 and the only extra required is an optoisolator, a diode and 3 resistors.  Total cost is probably about £12 or less.  This should control 6 servos and/or several Tortoise motors. Sounds a reasonable price given that it includes the DCC accessory decoder function and built-in servo calibration capability.  I haven't found a cheaper option but then again I haven't looked very far.

 

Cheers

Dave

Share this post


Link to post
Share on other sites

Although tortoises are specified for higher voltage they will run from 5V so you can just connect one between two outputs of the Arduino if you don't mind the tortoise being a little slower and quieter than normal. Current consumption is in the order of 6mA (less than an old fashioned LED) so it should not overload the Arduino.

 

Good thinking.  As a code hacker rather than an electrickery expert I hadn't thought of that.  Time to go down to the shed and experiment...

 

Cheers

Dave

Share this post


Link to post
Share on other sites

Spot on Nigel. I set the address + 4 in my Multimaus and all works just fine. Hadn't realised this oddity of the Roco system. Might be able to accomodate it in the decoder software with a bit of research.

 

As to cost.  An Arduino Nano can be had for about £8 to £10 and the only extra required is an optoisolator, a diode and 3 resistors.  Total cost is probably about £12 or less.  This should control 6 servos and/or several Tortoise motors. Sounds a reasonable price given that it includes the DCC accessory decoder function and built-in servo calibration capability.  I haven't found a cheaper option but then again I haven't looked very far.

 

Cheers

Dave

 

The addressing issue is an ambiguity in the DCC standards documentation.  Essentially the format/numbering of accessory packets is described in a table and in the text of the standard.  Unfortunately there is scope to read the document in two different ways, leading to the off-set of 4.   Most commercial systems work as per your Sprog,  but a handful (including Roco and some MERG designs) have the alternative interpretation which leads to the off-set.

 

Cost sounds quite good.

Its comparable per Servo to the LocoServo boards I've built in the past.  LocoServo are LocoNet (Digitrax/Uhlenbrock data bus) rather than DCC track signals for the instructions for operation.

 

 

- Nigel

Share this post


Link to post
Share on other sites

Hello Dave,

I just purchased an Arduino Uno and would like to throw turnouts using DCC, a servo and the Arduino board. Would you be willing to share your code so I am not reinventing the wheel? If I am able to add any improvements, I will share them.

Thank You,

John

You know how it happens, you start looking at one problem and you end up solving another. I was looking at ways to maximise the utilisation of the power generated by my solar panels by controlling the distribution of the power in my house using a microcontroller and some bits. This led me to the Arduino family of microcontroller boards.

These boards are reasonably cheap, quite capable and dead easy to programming as there is a ton of free bits of software available. I still haven't sorted out my household power control (yet) but I've learned a lot about the technology and started to look for other things to do with it.

My local model railway club, Winchester Railway Modellers, is currently building a new exhibition layout, Redbridge Wharf. More details here: http://www.rmweb.co.uk/community/index.php?/topic/58444-wrm-redbridge-wharf/

The fiddleyard for this layout has over 40 turnouts which would break the bank if we used Tortoise motors. We need something cheaper but don't fancy subjecting our handmade track to the thumping of solenoids. Servos seem like a good idea. We would also like to be able to operate the layout using conventional DC switching or DCC magic. We need a servo controller and DCC accessory decoder that is cheap(ish). This is where the Arduino comes in.

The Arduino can control a number of servos using a single wire to each. Each servo is connected to 0 volts, +5 volts and one of the PWM outputs of the Arduino. Using a PWM output is native to the microcontroller and requires no extra circuitry. The 0 and 5 volt lines are taken from an external power supply, probably one salvaged from an old PC.

What I have done is to hook up a couple of servos in this manner plus a couple of toggle switches. Again the Arduino can easily detect switch input, just switch one of the digital inputs to 0 volts. The toggle switch is used to detect whether the servo should be thrown to the left or right. That's DC control sorted then.

You need to be able to calibrate the throw for the servos really so another pushbutton switch has been added. Pushing this starts a calibration sequence on the last servo switched. One push and the servo centres, push again and the servo starts slowly stepping right, push again and the limit of throw is set and the servo returns to centre then starts slowly stepping in the other direction. Push the button once more and the other extreme throw is set and we are done.

So thats's DC control and calibration sorted what about DCC? Luckily some folks have thought of this before and come up with a solution (or two). A bit of external circuitry is added using an optoisolator (to prevent the white smoke escaping from the Arduino) providing a connection to the controller. The Arduino detects the pulses of the DCC signal and interprets them as individual message packets. Just need to look for the accessory messages and activate the relavent servos. Easy peasy.

Here's a pic of my breadboarded test set up.

 

http://i44.photobucket.com/albums/f7/eldavos/Workbench/IMG_20130405_121809_zps639f07b1.jpg

It 'ain't pretty but it works. The white box on the right is a cheapo 12v power supply as used in LED lighting rigs. It's feeding the Arduino.

 

If a DCC signal is detected the toggle switches are ignored so the one lash up works happily as a servo controller with or without DCC controller.

I've tested it with my Sprog and also a friend's ZTC 511 and all seems OK. Currently it doesn't work with my Roco Multimaus so there is some debugging to do.

There is also an embryonic command line interface which allows commands to be entered from a PC connected to the USB interface so I think it would be fairly simple to set up a scheme whereby the settings could be extracted from a board and reloaded. Quite useful if you need to replace a faulty board quickly.

I haven't yet got it controlling a Tortoise motor which would be cool as we have some of those to control on the scenic part of the layout. I'm sure it can be done with a bit of fiddling. There's also a lot of cleaning up needed in the code. All good fun.

Cheers
Dave

Share this post


Link to post
Share on other sites

There's an article on using the Arduino in the latest Model Railroader, if that's useful. I think it says how to get hold of some control software.

Share this post


Link to post
Share on other sites

Thank You Barry,

That Model Railroader article was what rekindled my interest. It's a good starting place but doesn't go deep enough.

John

There's an article on using the Arduino in the latest Model Railroader, if that's useful. I think it says how to get hold of some control software.

Share this post


Link to post
Share on other sites

Hello Dave,

I just purchased an Arduino Uno and would like to throw turnouts using DCC, a servo and the Arduino board. Would you be willing to share your code so I am not reinventing the wheel? If I am able to add any improvements, I will share them.

Thank You,

John

Apologies for the apparent death of this thread. Work on this got pushed down the priority stack and with months of good weather there was a lot of golf to play! I'm happy to share the code and will dig out my current (incomplete) version and post it shortly. I have no doubt it can be improved upon.

 

I have come back to work on this as we need an affordable point switching option for a Winchester Railway Modellers project, Redbridge, and I am thinking of reengineering Waton.

 

I had originally intended to use Arduino Nano boards which can be had for around £8-£10 but looking around there is a smaller and cheaper option, the Arduino Pro Mini which can be had for as little as £2.50! It has about the same capabilities as the Nano and the much larger Uno including the ability to drive 6 PWM outputs but lacks a USB connection. You need a breakout board to program it but that's not really a great hardship as it's (hopefully) a very infrequent activity.

 

Here are a couple of pics of my latest prototype using the Pro Mini configured to drive 5 servos...

 

IMG_20131111_121402.jpg

 

I've mounted the board using header sockets to a small piece of vero board and added the additional components for the DCC sensing. No additional components are needed for the servo control as I am simply using 5 digital ports in PWM output mode and 5 digital ports in input mode which I connect to ground via simple toggle switches. The external connection are broken out to a bit of strip board for convenience.

 

IMG_20131111_121303.jpg

 

Looking closer you can see there aren't too many additional components. There is an optoisolator to keep the DCC signal at 16-18volts AC from releasing the white smoke from the Arduino which runs at 5volts DC. There are also 3 resistors, a diode and a momentary contact push button switch. The latter is used to allow me to adjust the throws of the servos without having to re-program. Or at least it will do when i've finished that bit of code.

 

The code and the DCC componentry is based on the that outlined by Mynabay (http://www.mynabay.com/arduino/2-uncategorised/14-arduino-dcc-monitor)

 

Current costings:

 

Pro Mini board £2.50

Optoisolator £1.50

Header sockets £1

Tag strip £1

Other bits £1

 

Total around £7 for a DCC accessory decoder with 5 servo control outputs.

 

Cheers

Dave

  • Like 2

Share this post


Link to post
Share on other sites

For the codeheads amongst us here is the Arduino sketch that I have knocked together so far. Don't expect much in the way of documentation and there are clearly bits of code missing or incomplete! If anyone wants to do serious stuff with it I can send by email if you care to send me a PM as the forums don't allow ataching of this type of file.

 

As per normal Arduino coding there are setup() and loop() methods that do what they say on the tin. The initialisation calls to the DCC-Decoder library set up an interrupt on pin 2 and the handler inside the library decodes the DCC packets and sends them to various callback routines defined in the sketch to deal with raw packets or accessory decoder packets.

 

Cheers

Dave
 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// DCC_Accessory_Decoder
//
// Author: Dave Renshaw
//
// This sketch provides basic accessory decoder capabilites for operating servos or Tortoise pont machines. It uses
// the DCC_Decoder library created by Mynabay (http://www.mynabay.com/arduino/2-uncategorised/14-arduino-dcc-monitor)
//
// BEWARE This version is far from complete!
//

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

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// The configuration of the decoder is done via a string that is either retrieved from the persistent storage or
// sent via the serial interface. The format is as follows...
//
//   - DCC address terminated by a '#' character
// For a servo device...
//   - Device type character 's'
//   - DCC accessory address 
//   - PWM output port to use
//   - Switch input port to use
//   - Closed angular displacement in degrees terminated by a '#' character
//   - Thrown angular displacement in degrees terminated by a '#' character
// For a motor driven point machine
//   - Device type character 'm' 
//   - DCC accessory address 
//   - Digital output port to which 5volts is delivered to close terminated by a '#' character
//   - Digital output port to which 5volts is delivered to throw terminated by a '#' character
//   - Switch input port to use
//
// Arduino Uno, Nano and ProMini config data:
//    PWM output ports:  3,5,6,9,10,11
//    Interrupt ports:   2,3
//
char configData[] = "61#s03#04#060#120#62#s05#07#060#120#63#s06#08#060#120#64#s09#12#060#120#65#s10#11#060#120#!";

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Defines and structures
//
#define MAX_DEVICES               5

#define SET_UP                    14

#define kDCC_INTERRUPT            0

int DEBOUNCE_PERIOD =           500;           // the debounce time; increase if things get twitchy!

int TYPE_SERVO =                  1;
int TYPE_TORTOISE =               2;

boolean  dccMode = false;

unsigned long lastPacketMillis = 0;

int lastServoReferenced = -1;

boolean configOK = false;

typedef struct
{
    int               address;                // Address to respond to
    int               type;                   // Type of accessory (servo, Tortoise, etc.)
    int               outputPin;              // Arduino output pin to drive
    int               switchPin;              // Arduino output pin to read for switch input
    boolean           thrown;                 // true=thrown, false=closed.
    int               closedSetting;          // Angular displacement for closed position
    int               thrownSetting;          // Angular displacement for thrown position
    Servo             servo;
    int               oldSwitchState;
    long              lastSwitchChangeTime;
} DCCAccessoryAddress;


DCCAccessoryAddress devices[MAX_DEVICES];


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Decoder Init 
//
void configureDecoder() {

  // Assume the config is bad...
  configOK = false;
  
  // parse the config string...
  int nextDevice = 0;
  int nextChar = 0;  

  while (nextChar < sizeof(configData)) {
    // If the next character is the terminator we are all done and things are OK (maybe)...
    if (configData[nextChar] == '!') {
      configOK = true;
      break;
    }
    
    int addr = 0;
    
    char c = configData[nextChar++] ;
    while (c != '#') {
      if (c < '0' || c > '9') {
        Serial.print("**Error in config string - DCC address invalid at character ");
        Serial.println(--nextChar);
        nextChar = 9999;
        break;
      } else {
        addr *= 10;
        addr += c - '0';
        c = configData[nextChar++] ;
      }
    }
    Serial.print("Received DCC address ");
    Serial.println(addr);

    char type;
    if (nextChar < sizeof(configData)) {
      type = configData[nextChar++];
    } else {
      Serial.print("**Error in config string - Expected device type at character ");
      Serial.println(nextChar);
    }

    Serial.print("Received type character '");
    Serial.print(type);
    Serial.println("'");
    
    if (type == 's') {
      // servo...
      
      // pick out the output port number...
      int port = 0;

      c = configData[nextChar++] ;
      while (c != '#') {
        if (c < '0' || c > '9') {
          Serial.print("**Error in config string - output port invalid character '");
          Serial.print(c);
          Serial.println("'");
          nextChar = 9999;
          break;
        } else {
          port *= 10;
          port += c - '0';
          c = configData[nextChar++] ;
        }
      }
      Serial.print("Received port ");
      Serial.println(port);
      
      // ..then the switch port number...
      int sport = 0;

      c = configData[nextChar++] ;
      while (c != '#') {
        if (c < '0' || c > '9') {
          Serial.print("**Error in config string - switch port invalid character '");
          Serial.print(c);
          Serial.println("'");
          nextChar = 9999;
          break;
        } else {
          sport *= 10;
          sport += c - '0';
          c = configData[nextChar++] ;
        }
      }
      Serial.print("Received switch port ");
      Serial.println(sport);
      
      // ...then the closed and thrown angles...
      int closed = 0;     // angular position for closed
      int thrown = 0;     // angular position for thrown

      c = configData[nextChar++] ;
      while (c != '#') {
        if (c < '0' || c > '9') {
          Serial.print("**Error in config string - closed offset angle invalid character '");
          Serial.print(c);
          Serial.println("'");
          nextChar = 9999;
          break;
        } else {
          closed *= 10;
          closed += c - '0';
          c = configData[nextChar++] ;
        }
      }
      if (closed > 180) {
        Serial.println("**Error in config string - closed offset too big!");
        nextChar = 9999;
        break;
      } else {
        Serial.print("Received closed offset ");
        Serial.println(closed);
      }
      
      c = configData[nextChar++] ;
      while (c != '#') {
        if (c < '0' || c > '9') {
          Serial.print("**Error in config string - thrown offset angle invalid character '");
          Serial.print(c);
          Serial.println("'");
          nextChar = 9999;
          break;
        } else {
          thrown *= 10;
          thrown += c - '0';
          c = configData[nextChar++] ;
        }
      }
      if (thrown > 180) {
        Serial.println("**Error in config string - thrown offset too big!");
        nextChar = 9999;
        break;
      } else {
        Serial.print("Received thrown offset ");
        Serial.println(thrown);
      }
      
      devices[nextDevice].address = addr;
      devices[nextDevice].type = TYPE_SERVO;
      devices[nextDevice].outputPin = port;
      devices[nextDevice].switchPin = sport;
      devices[nextDevice].thrown = false;
      devices[nextDevice].closedSetting = closed;
      devices[nextDevice].thrownSetting = thrown;
      devices[nextDevice].oldSwitchState = 1;
      devices[nextDevice].lastSwitchChangeTime = millis();
      
      nextDevice++;
      
    } else if (type == 'm') {
      // motor driven point machine...
    } else {
      // duff type...
      Serial.print("**Error in config string - Unrecognised device type '");
      Serial.print(type);
      Serial.print(" at character ");
      Serial.println(nextChar);
      nextChar = 9999;
      break;
    }
  }  // while nextChar
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Basic accessory packet handler 
//
void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data)
{

    Serial.print("Acc Packet ");
    Serial.print(address, HEX);  
    Serial.print(" ");
    Serial.println(data, HEX);
    
    // Convert NMRA packet address format to human address
    address -= 1;
    address *= 4;
    address += 1;
    address += (data & 0x06) >> 1;
    
    boolean closed = (data & 0x01) ? 1 : 0;
    
    for(int i=0; i<(int)(sizeof(devices)/sizeof(devices[0])); i++)
    {
        if( address == devices[i].address )
        {
            Serial.print("Basic addr: ");
            Serial.print(address,DEC);
            Serial.print("   closed: ");
            Serial.println(closed,BIN);

            if (devices[i].type == TYPE_SERVO) {
              lastServoReferenced = i;
            
              if (closed) {
                devices[i].servo.write(devices[i].closedSetting);
              } else {
                devices[i].servo.write(devices[i].thrownSetting);
              }
            } else if (devices[i].type == TYPE_TORTOISE ) {
              if (closed) {
                digitalWrite(devices[i].outputPin + 1, LOW);
                digitalWrite(devices[i].outputPin, HIGH);
              } else {
                digitalWrite(devices[i].outputPin, LOW);
                digitalWrite(devices[i].outputPin + 1, HIGH);
              }
            }
        }
    }
    
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Basic accessory packet handler 
//
boolean RawPacket_Handler(byte count, byte* packet)
{
/*    Serial.print("Raw Packet ");
    Serial.print(count);  
    for (int i = 0; i++; i < count) {
      Serial.print(" ");
      Serial.print(packet[i], HEX);
    }
    Serial.println(" "); */
    
  
    lastPacketMillis = millis();
    if (!dccMode) {
      dccMode = true;
      Serial.println("DCC mode");
    }
    return false;
    
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Setup
//
void setup() { 
  Serial.begin(9600);
  DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true);
  DCC.SetRawPacketHandler(RawPacket_Handler);
  DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );
   
  // Enable the set-up/adjustment pushbutton...
  pinMode(SET_UP, INPUT);
  digitalWrite(SET_UP, HIGH);

  // Set-up pin 13 so we can illuminate the LED for info...
  pinMode(13, OUTPUT);
   
  // Do we have a saved configuration?
  if (EEPROM.read(0) != 255) {
    // Yes so read it back in...
    for(int i=0; i<(int)(sizeof(devices)); i++) {
      byte b = EEPROM.read(i);
    }
  } else {
    // Read the configuration string and set up the relavent input and putput ports...
    configureDecoder();
  }

  // Do we have a valid config?
  if (configOK) {  
    Serial.println("Config OK");
    // Setup output pins
    for(int i=0; i<(int)(sizeof(devices)/sizeof(devices[0])); i++) {
      // if the device address is defined then we can try and set things up...
      if (devices[i].address != 0) {
        if ( devices[i].type == TYPE_SERVO ) {
          pinMode( devices[i].outputPin, OUTPUT );
          devices[i].servo.attach(devices[i].outputPin);
          devices[i].servo.write(90);
          devices[i].servo.write(devices[i].closedSetting);
          pinMode(devices[i].switchPin, INPUT);
          digitalWrite(devices[i].switchPin, HIGH);
          Serial.print("Servo addr: ");
          Serial.print(devices[i].address,DEC);
          Serial.print(" ready on pin: ");
          Serial.println(devices[i].outputPin,DEC);
        } else if (devices[i].type == TYPE_TORTOISE ) {
          pinMode( devices[i].outputPin, OUTPUT );
          pinMode( devices[i].outputPin + 1, OUTPUT );
          digitalWrite(devices[i].outputPin, HIGH);
          digitalWrite(devices[i].outputPin + 1 , LOW);
        }
      }
    }
  }
}


int oldSetUpButtonState = 1;
long lastSetUpButtonChangeTime = millis();

int setUpMode = 0;
long lastSetUpStep;
int setUpStep;
int STEP_INTERVAL = 300;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Main loop
//
void loop() {
  ////////////////////////////////////////////////////////////////
  // Loop DCC library
  DCC.loop();
    
  if (dccMode && (millis() - lastPacketMillis > 1000)) {
    dccMode = false;
    Serial.println("DC mode");
  } 
  
  // See if any command data is coming in...
  while (Serial.available()) {
    // Are we waiting for a command?
  }
  
  // look at the set_up push button...
  int newState = digitalRead(SET_UP);
  
  if (newState != oldSetUpButtonState) {
    long timeNow = millis();
    if ((timeNow - lastSetUpButtonChangeTime) > DEBOUNCE_PERIOD) {
      lastSetUpButtonChangeTime = timeNow;
      // whatever the reading is at, it's been there for longer
      // than the debounce delay, so take it as the actual current state:
      
      if (newState == LOW && lastServoReferenced >= 0) {
        setUpMode++;
        Serial.print("set_up mode=");
        Serial.println(setUpMode);
        if (setUpMode == 1) {
          digitalWrite(13, HIGH);
          lastSetUpStep = timeNow;
          // starting set up so move servo to middle position...
          setUpStep = 90;
          devices[lastServoReferenced].servo.write(setUpStep);
        } else if (setUpMode == 3) {
          devices[lastServoReferenced].closedSetting = setUpStep;
          setUpStep = 90;
          devices[lastServoReferenced].servo.write(setUpStep);
        } else if (setUpMode == 4) {
          devices[lastServoReferenced].thrownSetting = setUpStep;
          setUpStep = 90;
          devices[lastServoReferenced].servo.write(setUpStep);
          setUpMode = 0;
        }
      }
      oldSetUpButtonState = newState;
    }
  }
  
  // do the servo set up thing...
  if (setUpMode > 0) {
    if ((millis() - lastSetUpStep) > STEP_INTERVAL) {
      digitalWrite(13, LOW);
      if (setUpMode == 2) {
        setUpStep--;
        devices[lastServoReferenced].servo.write(setUpStep);
        lastSetUpStep = millis();
      } else if (setUpMode == 3) {
        setUpStep++;
        devices[lastServoReferenced].servo.write(setUpStep);
        lastSetUpStep = millis();
      }
    } else {
      digitalWrite(13, HIGH);
    }
  }

  
  // look at the switches if we are in DC mode...
  if (!dccMode) {
    
    for(int i=0; i<(int)(sizeof(devices)/sizeof(devices[0])); i++) {
      int newState = digitalRead(devices[i].switchPin);
  
      if (newState != devices[i].oldSwitchState) {
        long timeNow = millis();
        if ((timeNow - devices[i].lastSwitchChangeTime) > DEBOUNCE_PERIOD) {
          devices[i].lastSwitchChangeTime = timeNow;
          // whatever the reading is at, it's been there for longer
          // than the debounce delay, so take it as the actual current state:
          Serial.print("devices[");
          Serial.print(i);
          Serial.print("] new switch state=");
          if (newState == LOW) {
            Serial.println("LOW");
            devices[i].servo.write(devices[i].closedSetting);
          } else {
            Serial.println("HIGH");
            devices[i].servo.write(devices[i].thrownSetting);
          }
          devices[i].oldSwitchState = newState;
          
          lastServoReferenced = i;
        }
      }
    }
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  • Like 1

Share this post


Link to post
Share on other sites

Hi Dave, good stuff. How did you post the 'sketch' as I have a similar post to do for my DCC controlled stepper motor turntable?

 

Ray.

Share this post


Link to post
Share on other sites

Hi Dave, good stuff. How did you post the 'sketch' as I have a similar post to do for my DCC controlled stepper motor turntable?

Ray.

When adding your post select the "More Reply Options" button then in the editing page select the bbcode(?) Button on the top left. It's probably the only one not greyed out. This should enable all the other options so look for the code option "<>". Bob is your uncle.

 

Oops. Just tried this with Chrome on my Android tablet and lo and behold the advanced options don't seem to be available!

 

Cheers

Dave

Edited by eldavo

Share this post


Link to post
Share on other sites

When adding your post select the "More Reply Options" button then in the editing page select the bbcode(?) Button on the top left. It's probably the only one not greyed out. This should enable all the other options so look for the code option "<>". Bob is your uncle.

Oops. Just tried this with Chrome on my Android tablet and lo and behold the advanced options don't seem to be available!

Cheers

Dave

Same for Safari on the iPad, will have to wait till I get back to the PC.

Share this post


Link to post
Share on other sites

Seriously clever stuff Dave.

 

You should market the product for those that don't understand but could utilise them.

 

Eddie

Share this post


Link to post
Share on other sites

Hello Dave,

 

Thanks for sharing this with us. This is exactly (to the last detail..) what I want for my layout; toggle switches controlling servos and at the same time being able to control the turnouts with my DCC controller. The only thing I would add is a LED route-indicator on my control board. But from what I have read about Arduino that should be peanuts compared to what you have done here. 

 

After a lot of dithering I am finally sure that I will get an Arduino and will control my points with servos instead of point motors. According to my calculations that would save me about half the price on every turnout, even more if it is a crossover... When I have my Arduino and servose etc. I will definitely pm you for the code, thanks a lot!

 

Do you have any update on the progress of this?

 

Have a nice day!

Edwin

Share this post


Link to post
Share on other sites

Great thread! I have a few basic questions.

 

Would you mind sharing a quick schematic of the parts you added to the arduino to protect it from the dcc signal and the other components you added?

 

Also are you using toggle switches (on or off) or momentary switches to trigger the servo movement?

 

I'd like to use dpdt toggles so I can use half of the pins to switch the polarity of the frog and the other pins to switch between 5v and ground. Did that make sense? And by pins I mean the pins of the toggle switch.

 

Lastly is there a way to cut the signal after movement to reduce heat and noise?

Edited by gcodori

Share this post


Link to post
Share on other sites

Would you mind sharing a quick schematic of the parts you added to the arduino to protect it from the dcc signal and the other components you added?

 

Info on the electronicals and the Arduino libraries is here: http://www.mynabay.com/arduino/2-uncategorised/14-arduino-dcc-monitor

 

Also are you using toggle switches (on or off) or momentary switches to trigger the servo movement?

I'd like to use dpdt toggles so I can use half of the pins to switch the polarity of the frog and the other pins to switch between 5v and ground. Did that make sense? And by pins I mean the pins of the toggle switch.

I use single pole on-off switches to connect the appropriate Arduino pin to 0 volts. Clearly you can achieve this using one side of a dpdt switch.

 

Lastly is there a way to cut the signal after movement to reduce heat and noise?

Yes. You can use a simple NPN transistor circuit to switch a relay controlling the servo power line. The base of the transistor is connected to a digital pin of the Arduino via a resistor. Whenever the state of a relay changes set the digital pin high to switch the transistor and turn the relay on to power the servos then after a couple of seconds turn it off.

 

Cheers

Dave

Share this post


Link to post
Share on other sites

Very interested in this topic, looks like a good alternative to DCC Concepts digital point motors which I have used on the first few points of my fledgling layout.

 

Anyone know of a good source(s) for suitable servos?

 

Andrew

Share this post


Link to post
Share on other sites

...

Anyone know of a good source(s) for suitable servos?

 

Lots of options on eBay. If you are prepared to wait for shipping from China you can get TowerPro 9g servos for very low prices.

Share this post


Link to post
Share on other sites

Hi

 

Thanks for this

 

Please see the Arduino thread I kicked off a few months back - it's linked in my signature below.

 

I'll link from that thread back to here as there are lots of common themes.

 

Best

Simon

Share this post


Link to post
Share on other sites

Just one small point that I don't see mentioned in any of the follow up replies

 

 

Each servo is connected to 0 volts, +5 volts and one of the PWM outputs of the Arduino.

Servos (using the normal servo library) will work on any of the Arduino I/O outputs - they don't need to be connected to the PWM capable pins. Note, however that using the servo library prevents the use of some of the PWM pins for use with the Arduino analogWrite() instruction.  For example, the Arduino Mega can control 48 servos but only has 16 PWM-capable pins.

 

The PWM which is used to control the position of a servo is different from the PWM that is produced by analogWrite() and which is used to control the speed of DC motors or the brightness of LEDs.

 

Also ...

 

The servos should not draw power from the Arduino 5v pin as they are likely to overload the Arduino voltage regulator with all sorts of strange consequences and possible damage to the Arduino. This is one of the commonest problems on the Arduino Forum.

 

...R

Edited by Robin2

Share this post


Link to post
Share on other sites

Also in the Netherlands some guys are experimenting with this Arduino stuff. Quite succesfull.

These Arduino DCC accessory decoder is working very well with the Hornby Railmaster program.

 

See: http://forum.beneluxspoor.net/index.php/topic,61213.0.html

 

In Dutch language (but use Google Translate).

 

Grzz, Hans

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.