Trains4Africa
Because all Boys (and some girls) love Trains

DCC STEPPER MOTOR TURNTABLE

There are a number of turntable projects that have been circulating the internet for some time- all these projects however are very similar in system design and overall objectives being: Motorizing/controlling turntables, utilising DC motors, Stepper Motors, gears, gearboxes, toothed belts and incorporated DCC control.

The project presented here describes the construction of a simple DCC controlled turntable utilising a stepper motor directly connected to the turntable shaft (no gearboxes, gears, belts) and an ARDUINO microcontroller programmed to do all the hard stuff.

Electronic Components

  • ARDUINO UNO / Pro Mini
  • ADAFRUIT Motor Shield V2 / Pololu stepper
  • Stepper Motor (SM-42BYG011-25)
  • Hall Effect Sensor (RS370-6896) / OPB716Z Sensor
  • Magnet (RS189-5512)
  • Bellows Coupling (RS693-2467)
  • Opto Isolator 6N137 (RS671-1359)
  • 10k resistors x 2
  • 1K resistor x2
  • IN4148 diode x 1

Notes:

  • As an alternative to the Hall Effects Sensor/Magnet, an infrared sensor pair can also be used to same effect.
  • The Arduino Uno can be substituted with an alternative microcontroller

 

Part 1:  Electronic Circuit

Due to the use of the Arduino the amount of wiring involved with this project is minimal (the complex stuff is being done by the Arduino/Adafruit modules).

All you need to do is solder a few connectors to the Adafruit module and build the DCC interface circuit on a piece of strip(vero)board.

Circuit Diagram:

tt5

R1/2  –  10k
R3     –  1K
D1     –  1N4148
U2    –  6N137

tt6

tt8

 

The Arduino / Adafruit

The Arduino is an open source microcontroller development board and is very well documented.

The Adafruit motor is a fully self-contained stepper motor controller board that interfaces to the Arduino with minimal connection needed.

The stepper motor is connected to the Adafruit board connections M3 and M4.

Blue/Yellow to M3, Green/Red to M4.

tt7

The connections to the Adafruit board on the DCC I/F circuit above are as printed on the board.

Part 4:   Initial Test Scripts

Testing the Reference Sensor and setting the Speed and Acceleration

At this point it is a good time to test the reference Sensor and the Speed/Acceleration settings of the stepper motor

Firstly mark a point on the end of the turntable deck containing the magnet so you can check the alignment and repeatability of the test.

For this test we need to create a new sketch for the code.

File:  Stepper_test

Start the Arduino IDE and from the ‘File’ drop down menu select ‘New’
Now copy/paste the following code into the new sketch:

****************************************************************

// Turntable test routine
//
// Requires the Adafruit_Motorshield v2 library
//   https://github.com/adafruit/Adafruit_Motor_Shield_V2_Library
// And AccelStepper with AFMotor support
//   https://github.com/adafruit/AccelStepper

// This sketch is for Adafruit Motorshield v2 only!
// Will not work with v1 shields

#include <AccelStepper.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include “utility/Adafruit_PWMServoDriver.h”

Adafruit_MotorShield AFMStop(0x60); // Default address, no jumpers

// Connect stepper with 200 steps per revolution (1.8 degree)
// to the M3, M4 terminals (blue,yellow,green,red)

Adafruit_StepperMotor *myStepper2 = AFMStop.getStepper(200, 2);

// you can change these to SINGLE, DOUBLE, INTERLEAVE or MICROSTEP!

// wrapper for the motor! (3200 Microsteps/revolution)
void forwardstep2() {
myStepper2->onestep(FORWARD, MICROSTEP);
}
void backwardstep2() {
myStepper2->onestep(BACKWARD, MICROSTEP);
}

// Now we’ll wrap the stepper in an AccelStepper object

AccelStepper stepper2(forwardstep2, backwardstep2);
void setup()
{

AFMStop.begin(); // Start the shield

//configure pin3 as an input and enable the internal pull-up resistor
pinMode(3, INPUT_PULLUP);
//read the sensor (open collector type) value into a variable
int sensorVal = digitalRead(3);

// step forward to sensor index point
while (sensorVal == HIGH) {
sensorVal = digitalRead(3);
forwardstep2();
delay(50);
}
delay(5000);

// set stepper speed, acceleration and position
stepper2.setMaxSpeed(50.0);
stepper2.setAcceleration(10.0);
stepper2.moveTo(800);

}

void loop()
{
// Change direction at the limits

if (stepper2.distanceToGo() == 0)
{ delay(5000);
stepper2.moveTo(-stepper2.currentPosition());
}

stepper2.run();
}

********************************************************************

Before uploading this sketch save it (File/Save As) giving it a new name, TurntableTest.

(TurntableTest will now appear in the ‘File/Sketchbook’ menu for future loading).

Now upload the sketch.

The turntable should now rotate slowly until the mark reaches a point very near the sensor on the well.
It will pause here for 5 seconds during which time mark a point on the well side exactly by the mark on the deck.
The deck will now continue to rotate another 90 degrees and pause for 5 seconds, again, mark this position on the well side.
Now the deck will rotate back through 180 degrees and pause for 5 seconds. You guessed it, mark this position as well.

The deck will continue to turn back and forth between these two positions until you turn the power off.
Leave it running for an hour or so occasionally checking that the deck marker and end points (marked on the well) align at the end of each sweep.
If it looses alignment (misses steps) it would suggest you have some unwanted friction/shaft misalignment in the system somewhere which will need sorting out.

Notice at the start and end of each sweep the deck accelerates from rest and decelerates to stop, you might also notice some jitter in the motion.
(if you have a high rate of jitter it may suggest undue friction still in the system)
The rate of acceleration/deceleration and the turntable speed are easily adjusted and will effect any perceived jitter.
This is going to be very subjective so its up to you to play with the speed and acceleration parameters to suit your needs.

Look for this section of the code in the sketch:
// set stepper speed, acceleration and position
stepper2.setMaxSpeed(50.0);
stepper2.setAcceleration(10.0);
stepper2.moveTo(800);

The setMaxSpeed parameter (50.0) adjusts the speed of rotation in steps per second. A value of 100 would take about 32 seconds for a complete rotation so I wouldn’t go any faster than this. If you slow it down too much you will start to introduce jitter.

The setAcceleration parameter (10.0) adjusts the rate of acceleration/deceleration in steps per second per second. 10 is probably to slow but anything above 100 probably wouldn’t be noticeable at the maximum speeds we are using.

Play around with these values until you are satisfied with the results.

 

Part 4: Initial DCC Testing 

In this part we will add DCC control to the turntable setting one position about 45 degrees from the reference and another rotated 180 degrees from this.

This is controlled by an Accessory Command from your DCC hand set.

i.e Select Accessory Address 200, select Thrown or Normal (or whatever nonemclature your handset uses)

Switching between Thrown and Normal will rotate the deck by 180 degrees.

As before, start the Arduino IDE and from the ‘File’ drop down menu select ‘New’ and copy/paste the following code into the new sketch and save it as ‘DCC Test’

File:  DCC_test

////////////////////////////////////////////////////////////////////////////////
// DCC Turntable Control Test Routines (Accessory Address 200)

#include <DCC_Decoder.h>
#include <AccelStepper.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include “utility/Adafruit_PWMServoDriver.h”

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Defines and structures
#define kDCC_INTERRUPT            0

typedef struct
{
int               address;                // Address to respond to

}

DCCAccessoryAddress;

DCCAccessoryAddress gAddresses[1];

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

// Adafruit Setup

Adafruit_MotorShield AFMStop(0x60); // Default address, no jumpers

// Connect stepper with 200 steps per revolution (1.8 degree)
// to the M3, M4 terminals (blue,yellow,green,red)

Adafruit_StepperMotor *myStepper2 = AFMStop.getStepper(200, 2);

// you can change these to SINGLE, DOUBLE, INTERLEAVE or MICROSTEP!

// wrapper for the motor! (3200 Microsteps/revolution)
void forwardstep2() {
myStepper2->onestep(FORWARD, MICROSTEP);
}
void backwardstep2() {
myStepper2->onestep(BACKWARD, MICROSTEP);
}

// Now we’ll wrap the stepper in an AccelStepper object

AccelStepper stepper2(forwardstep2, backwardstep2);

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Decoder Init
//
void ConfigureDecoder()
{
gAddresses[0].address = 200;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Basic accessory packet handler
//
void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data)
{
// Convert NMRA packet address format to human address
address -= 1;
address *= 4;
address += 1;
address += (data & 0x06) >> 1;

boolean enable = (data & 0x01) ? 1 : 0;

if( address == 200 )
{
Serial.print(“Basic addr: “);
Serial.print(address,DEC);
Serial.print(”   activate: “);
Serial.println(enable,DEC);

if( enable )
{
stepper2.moveTo(400);
}
else
{
stepper2.moveTo(2000);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Setup
//
void setup()
{
Serial.begin(9600);

AFMStop.begin(); // Start the shield

//configure pin3 as an input and enable the internal pull-up resistor
pinMode(3, INPUT_PULLUP);
//read the sensor (open collector type) value into a variable
int sensorVal = digitalRead(3);

//set stepper motor speed and acceleration
stepper2.setMaxSpeed(30.0);
stepper2.setAcceleration(20.0);
//  stepper2.moveTo(800);

// if near reference point move away
sensorVal = digitalRead(3);
while (sensorVal == LOW) {
sensorVal = digitalRead(3);
forwardstep2();
delay(50);
}

// step forward to sensor index point
while (sensorVal == HIGH) {
sensorVal = digitalRead(3);
forwardstep2();
delay(50);
}

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

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Main loop
//
void loop()
{
static int addr = 0;

////////////////////////////////////////////////////////////////
// Loop DCC library
DCC.loop();

////////////////////////////////////////////////////////////////
// Loop Stepper

stepper2.run();
}

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

Assuming all is working ok, after uploading to the Arduino board the turntable deck will rotate to the reference point and stop. While it is doing this you can open the serial monitor as before by clicking the icon in the top right corner of the Arduino IDE.

 

When the turntable stops select Accessory address 200 from your handset and set Normal.

 

The turntable should rotate to a position about 45 degrees from the reference.

Again when the T/T stops select Accessory address 200 and set Thrown.

The T/T should now rotate 180 degrees.

If you’ve opened the Serial monitor you should see something like this

tt10c

The activate value changing between 0 and 1 as you select between Normal and Thrown positions.

So, that’s about it as far as testing goes all that needs doing now is to develop the software to suit your individual needs

 

Part 5: Setting Up The Table – Towards a refined Sketch

Once we have the basic operation of the turntable working, we can work on the sketch to add more features.

Complete Sketch:   DCC Turntable1r

To have a seven road turntable is just an extension of a single road version, and the only difference in the software is in the sections headed:

‘Decoder Init’
‘Basic accessory packet handler’

The Decoder Init section sets up the recognisable DCC decoder addresses. in Part 6 we only had 1 at address 200

gAddresses[0].address = 200;

So firstly we need to add a few more addresses for the other roads.

// Decoder Init
//
void ConfigureDecoder()
{
gAddresses[0].address = 200;
gAddresses[1].address = 201;
gAddresses[2].address = 202;
gAddresses[3].address = 203;
gAddresses[4].address = 204;
gAddresses[5].address = 205;
gAddresses[6].address = 206;
gAddresses[7].address = 207;
}

This gives us addresses in the range 200-207 (but we only use 201-207)

The section ‘Basic accessory packet handler’ now needs to decode which address was sent and if in the range 201-207 whether it was a ‘straight’ or ‘turnout’ command.

We do this using the ‘Switch/Case’ function

// Basic accessory packet handler
//
void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data)
{
// Convert NMRA packet address format to human address
address -= 1;
address *= 4;
address += 1;
address += (data & 0x06) >> 1;

boolean enable = (data & 0x01) ? 1 : 0;

for(int i=0; i<(int)(sizeof(gAddresses)/sizeof(gAddresses[0])); i++)
{
if( address == gAddresses[i].address )
{
Serial.print(“Basic addr: “);
Serial.print(address,DEC);
Serial.print(”   activate: “);
Serial.println(enable,DEC);

if( enable )
{
switch (i) {
case 1:
stepper2.moveTo(200);
break;
case 2:
stepper2.moveTo(400);
break;
case 3:
stepper2.moveTo(600);
break;
case 4:
stepper2.moveTo(800);
break;
case 5:
stepper2.moveTo(1000);
break;
case 6:
stepper2.moveTo(1200);
break;
case 7:
stepper2.moveTo(1400);
break;
}
}else{
switch (i) {
case 1:
stepper2.moveTo(1800);
break;
case 2:
stepper2.moveTo(2000);
break;
case 3:
stepper2.moveTo(2200);
break;
case 4:
stepper2.moveTo(2400);
break;
case 5:
stepper2.moveTo(2600);
break;
case 6:
stepper2.moveTo(2800);
break;
case 7:
stepper2.moveTo(3000);
break;
}
}
}
}
}

 

This can be extended for more roads if required, just add more recognisable address in the ‘Decoder Init’ section and add another ‘Case’ statement for each additional address.

The ‘moveTo’ commands (stepper2.moveTo(200); etc) relate to absolute positions of the turntable deck from the ‘reference’ point.

As there are 3200 steps for a complete rotation the ‘moveTo’ positions after the ‘else’ statement are 1600 steps bigger than those before, giving a 180 degree rotation for each road position.

When (if) you get this far you will probably find that the tracks don’t quite line up exactly when the deck is rotated through 180 degrees, maybe up to a rail width out. This is probably due to the accuracy of the step in the stepper motor, but don’t worry its easily fixed as the error is very stable and repeatable.

All you have to do its to tweak the values of the relevant ‘moveTo’ command.

So for Road 1:

case 1:
                    stepper2.moveTo(200);
                  break;

the corresponding 180 degree turn after the ‘else’ statement is

case 1:
                    stepper2.moveTo(1800);
                  break;

Just adjust (by trial and error) the value 1800 to get the rails to lines up exactly. An increase (or decrease) in value of about 4 relates to about a rail width.

Do this for each road, and there you have it, a Seven (or more) Road, DCC Controlled Turntable.

Note:

  • Currently the software as written finds the reference point by turning in one direction so if the table is just past it will complete a 360 degree turn to find it. That part of the code will need rewriting, I’ll have to think about that one.

 

Part 6: Some ideas and possible solutions..

Problem 1: Currently the software as written finds the reference point by turning in one direction so if the table is just past it will complete a 360 degree turn to find it.

Solution 1:One solution to be to place the sensor a couple of degrees beyond one of the normal, operating limits of travel so that upon start up the table has to move in the same direction to find it? In other words, if in normal operation the table travels 120 degrees clockwise from Point A to Point B and 120 degrees, counter-clockwise from Point B to Point A, the table will always be between these points at shut down. If the sensor way placed at (say) 5 degrees anticlockwise from Point A and the table travelled anti-clockwise as part of the start up routine, it would always find the sensor before reaching the physical limit of travel.

 

Problem 2: If there is a possibility of the turntable going too far and causing damage I would put a limit switch (perhaps a microswitch) at each end of the travel – to be sure to be sure. On the other hand if the only problem is that the turntable will stall the motor and miss steps (it won’t harm the motor) I would just have a routine that re-establishes position by moving to the “home” detector

 

 

Part 7: Turntable Sketch – using Infrared Sensor

File:   DCC Turntable 2

What this Sketch does:

Once the Arduino is powered up, the stepper motors move the traverser bed slowly towards the sensor until that is tripped by a mechanical fixture breaking the IR beam and they then stop.  The “zero” position is then set ready for that session.  It is possible to save the last position on the Arduino while it is powered down but I figured it wouldn’t hurt to keep this as a calibration procedure for each session to ensure a known start point to calculate steps from without any need to calculate movement and direction relative to the current position.

The desired track address can then be selected on the DCC controller and once enabled, the LED displays the desired track number and the motors will move to the designated position and stop when it is reached.  The decimal point then lights up to indicate the traverser is in position.  There is also acceleration built in to give a soft start and end to the movement to reduce any “jolt” to the rolling stock.

By having set the zero position at the start, the Moveto command is used to drive the motors to move to a predetermined target position (number of steps) based on the track selected on the controller.

I currently have six parallel tracks on 67mm centres so I have currently set it up to move a fixed number of steps multiplied by the track number to determine the step count required.

However, I am planning to load the step counts for each of the track positions into an array at the start of the code so I can readily accommodate any variances between the physical track centres into the step count for each track.

I also plan to add a “kill” switch for emergency stops (I probably should have done that first but as it is all on breadboard at this stage, I haven’t made the effort!)

The sensor I used is a ZD1901 photo interrupter, but I am sure there would be suitable alternatives readily available elsewhere.

tt11

 

The Sensor:

tt11b

 

Part 7: Turntable with Turn Knob

File:   DCC_Turntable_v5

This project uses a Potentiometer to move the table to a position.

This bit of code recognizes the input and outputs an integer 0~12 based on position of the knob.

 //configure alalog pin 1 as an input
        int potDisp = 1;
        int reading = analogRead(potDisp);
        int analogout = reading / 79; //1023 / 13

The value is displayed when you make a DCC address selection (as part of that routine).  It is not ideal, but I wanted to see if I could read the value before I tried to make it perform work.

   Serial.print("Analog Location: ");
          Serial.println(analogout);

Part 8:  Arduino IDE

The project requires that the Arduino be programmed and for this we need to setup the IDE (integrated development environment) on a PC. This really is just a fancy way of saying we need to load some software and associated device libraries on the computer..

Firstly you will need to install the Arduino IDE on a PC/Laptop so you can upload the code to the Arduino Uno module.

This link http://arduino.cc/en/Guide/Windows#toc1 will give full instructions on how to install the IDE, connect the Arduino Uno to your PC/Laptop and run a simple program called ‘Blink’ to check that the Arduino is working ok (I’d leave the Adafruit and DCC I/F disconnected at this stage until you’re sure the basic Arduino working ok).

Next you will need to download the Libraries for the Adafruit Stepper Motor Module and the DCC Decoder control.

For the Adafruit Library go to http://learn.adafruit.com/adafruit-motor-shield-v2-for-arduino/install-software and click on the ‘Download latest Adafruit Motor Shield V2 Library’ button.

This will download a zip file ‘Adafruit_Motor_Shield_V2_Library-master.zip’ to a location on your PC.

Browse to this file double click on it and click ‘Extract all files’ giving a new folder  Adafruit_Motor_Shield_V2_Library-master.

Change the ‘-‘ to an ‘_’ as Libraries can’t have ‘-‘ signs

Now copy (or move) this folder and its contents to  c:\……..\My Documents\Arduino\libraries\

Install the modified Stepper Library:

https://github.com/a…it/AccelStepper

Install the DCC Libraries:

Download them from here: http://www.mynabay.com/arduino/2-uncategorised/14-arduino-dcc-monitor

‘DCC-Decoder Library Version 4’

Again extract the files from the zip file and change the ‘.’ In the folder name (dcc_decoder.v4) to a ‘_’ and move the folder to C:\………My Documents \Arduino\libraries\

You should now have two new folders in c:\……..\My Documents\Arduino\

Adafruit_Motor_Shield_V2_Library_master
dcc_decoder_v4

Now plug the Adafruit onto the Arduino module and connect up the Stepper Motor and Sensor (disconnect Arduino from the power/USB first). You will now need to supply 9-12DC to the Power terminal of the Adafruit module for the Motor supply and reconnect the USB lead from the Arduino to the PC.

Start the Arduino IDE (the same way as for the ‘Blink’ test).

Now browse to the ‘Stepper Test’ sketch from the ‘File’ menu.

And upload the sketch.

At this point you need to have your DCC track connected to the DCC interface board and be able to generate some DCC commands, ideally Turnout commands for Accessory Decoder 200. This will depend on your system but generally push buttons to:

Select Accessory Address and give it a command Normal (or Reverse)

From the Arduino IDE Window, open up the Serial Monitor by clicking on the small Icon near the top righthand side of the window. This will open up a new window and if all is well you should see some data scrolling up the screen.

tt10a

Don’t worry if they’re not the same as these, if you’re getting anything it’s probably working.

Now send a few Dcc Accessory commands from your DCC controller.

You should see an extra line of data appear each time.

tt10b

Again don’t worry if the numbers are not the same as these.

OK, so hopefully we now have the Stepper and DCC Interface working, next test is for the Sensor for the deck reference point.

That will be in Part 5 as I’ve not written any test code for that yet, (it just worked so I didn’t bother) along with a test sketch for setting the speed/acceleration and deceleration and a 180deg turn

Part 6: Construction

The following images are from Ray’s project and shows one possible build.

Ray used a PECO LK55 turntable kit, but this can be substituted as needed.

Notes:

  • Build the turntable as described in the supplied instructions but don’t fix the bridge deck to the well using the retaining collar. Paint and weather as desired.
  • Drill a hole in one end of the Bridge deck cross member for the sensor magnet and mount so it just misses the well when rotated. Use some packing washers for this.
  • The track pick-up for the Bridge deck is via a split ring arrangement (if you want to use sound locos you may want to modify this as there is a dead spot in the rotation)
  • Find the dead spot (using a meter and rotating the deck) and mount the Hall Sensor between the rail positions on the outside of the well up under the lip of the well (superglue). The embossed writing on the sensor should be against the well wall. This will be the reference point for the turntable controller and avoided as one of the exit roads.
  • The stepper motor is mounted directly under the Bridge deck pivot so the base of the well needs strengthening as it’s a bit flimsy. Cut a disk of 6-9mm ply the same diameter as the well. Drill a 20mm hole in the centre. Glue this to the base of the well.
  • Make a drive spindle using a suitable diameter steel rod. It should be a tight fit into the deck spindle, but to make sure it doesn’t slip clean and glue in place with superglue.
  • Now insert the deck, mark and cut to length so that about 20mm of the shaft protrudes below the ply base.

tt1

tt2    tt3

tt4

tt8

tt9