The Turntable at Red Bluffs, Part 3

The turntable at Red Bluffs is up and running, bringing the lower level of module 1 to substantial completion. The L&NC is really starting to look and function like a railroad.

The turntable rotating beacon comes on whenever the turntable is in motion.

This has been a long project. In the first post about the turntable I described the construction of the pit and bridge, and the mechanism for moving the bridge created from off-the-shelf Actobotics parts. In the second post, I talked about completing and wiring the bridge, which includes two light features: a rotating beacon at the top of the arch, and a simulated stove fire in the hut which I’ve previously discussed.

In this post I will bring all the pieces together to create a fully functional turntable. The surprise is how simple the system actually is; primarily because of the way stepper motors work, the mechanical integrity of the Actobotics system and the control features provided by a Sparkfun Easy Driver. From a coding standpoint I’m at an intermediate stage: I’ve got working code but it hasn’t been  integrated into the larger operating environment. For the time being, control is accomplished via the Serial Monitor and a menu option that allows me to tell it to go to a particular position by position number from 0 to 16. Position 0 is the calibration position; all others are the 16 possible ways the bridge can be oriented in the pit.

But First …..

Its the year end holiday period in this part of the world, so its allowed to have desert before your entree!  Accordingly, here is a little 4 minute film demonstrating the turntable. Enjoy!

About Stepper Motors

Stepper motor.

Stepper motors are a variation on the brushless DC motor that split the coil into multiple groups arranged symmetrically. By selectively energizing coil groups, the rotor can be rotated a precise distance. Steppers are used wherever something needs to rotate at a precise speed (for example, a hard drive spindle); or where it needs to be moved precisely and repeatedly across a range (for example, the read/write heads of a hard drive).

Steppers are precise without the need for a feedback mechanism. Further, while the coils are energized, steppers hold position strongly. Accordingly, once positioning is calibrated with a known starting point, the stepper should be able to move accurately for an indefinite period.

Steppers can be controlled by an Arduino using a darlington array (if the stepper is unipolar) or an H-bridge (if bipolar); steppers draw too much current to be directly attached to the microcontroller. Stepper motors vary in size, current draw, voltage requirements and resolution. By resolution I mean the number of steps in a full 360 degree rotation: the more steps per revolution, the greater the precision. The stepper I am using has a resolution of 200 steps per revolution (the “step angle” is 1.8 degrees).  The drive train has a 5 – 1 ratio, giving the assembly a resolution of 1000 steps per revolution of the bridge drive shaft. With the help of the Sparkfun Easy Driver 1/8 microstep mode, that resolution is improved to a final 8000 steps per revolution of the bridge.

Sparkfun Easy Driver.

The Sparkfun Easy Driver is a stepper motor controller card that interfaces with an Arduino. It provides control features you can’t get another way, including a microstep mode that can move a stepper a fraction of a step.

No special library is needed (3rd party libraries are available), but it does require 5 pins on the Arduino. You manipulate the Easy Driver, and thus the stepper, through the pins. MS1 and MS2 set the various modes of the driver; I use 1/8 microstep mode exclusively. DIR sets the rotation direction (HIGH for clockwise), and EN enables or disables the motor. STP is the step trigger. So, to set the default state I want, I use the following function:

void setEDPins(){
 digitalWrite(stp, LOW);
 digitalWrite(dir, HIGH); // CLOCKWISE 
 digitalWrite(ms1, HIGH); //Pull ms1, and ms2 high to set logic to 1/8th microstep resolution
 digitalWrite(ms2, HIGH);
 digitalWrite(en, LOW); // Enables motor control and turns coils on; HIGH disables
}

To take one step,

void doStep(){
 digitalWrite(stp,HIGH); //Trigger one step
 delay(1); // wait
 digitalWrite(stp,LOW); //Reset trigger
}

Simple as that. Everything else in the turntable object builds off of those basics.

The Controller Board

With 5 hard connections between the microcontroller and the driver, it made sense to create a controller board to facilitate those cross connections plus additional external connections.

The purpose of this board is to interconnect the Nano stack (a Nano plus an Ethernet shield) with the Sparkfun Easy Driver. The board also provides connection points for other pins, power and ground.

The Controller board with the Easy Driver and Nano stack installed.

Calibration: Setting Position 0

Way back when I build the pit and drive assembly, I glued two magnetic reed switches to an arbitrary point on the outside of the pit. The hut end of the bridge has a neodymium magnet glued to the head of a nylon screw, making its position adjustable.

Underside of the pit, showing the sensors, stand-offs for attaching to the Actobotics system, and a solid shaft I used for alignment during assembly.

The nylon screw mounted in the bridge makes the magnet position adjustable.

In the early construction stage, after gluing the sensors about a half inch apart, I set the magnet position so that it would trigger both switches consistently within a narrow position band. Single point accuracy isn’t possible, but isn’t necessary so long as you can trigger both switches consistently. To establish Position 0, I first move the bridge away from the sensors, then reverse direction and move until the first sensor triggers. Then I halve movement speed and continue stepping until switch 2 goes off. That gives me Position 0.

void FindAnchor(){
 digitalWrite(dir, CLOCKWISE);
 // if sensors currently tripped, rotate until both open
 while(analogRead(sw1) > 1000 || analogRead(sw2) > 1000){
   doStep();
   delay(step_delay * 2);
 }
 // additional steps to clear sensors
 doSteps(100, step_delay);
 // find the sensors again
 digitalWrite(dir, COUNTERCLOCKWISE);
 while(analogRead(sw1) < 1000){
   doStep();
   delay(step_delay);
 }
 while(analogRead(sw2) < 1000){
   doStep();
   delay(step_delay * 2);
 }
 current_direction = COUNTERCLOCKWISE;
 current_position = 0;
 current_step = 0;
 digitalWrite(ROTATING_BEACON, LOW);
 in_motion = false;
}

From there, it was simply a matter of determining the index of the 16 possible bridge positions. That index is expressed as a number of steps from Position 0, running clockwise.

I say simply somewhat facetiously. It took many hours of trial and error to establish the first 8 positions. The remaining ones were easier to find because they were 180 degrees offset from the first eight positions. The video, showing the accurate reversal of a locomotive, doesn’t cheat — the turntable really does move between positions accurately and seamlessly.

Each position requires both the step offset and the correct track polarity for that position. I store that information in a data array built from a simple structure:

#define NUM_POSITIONS 16
typedef struct ttp {
 unsigned int steps;
 byte polarity;
};

ttp positions[NUM_POSITIONS] = {
 {1797, REVERSE},
 {1967, REVERSE},
 {1973, REVERSE},
 {2122, REVERSE},
 {2264, REVERSE},
 {2417, REVERSE},
 {2672, REVERSE},
 {2916, REVERSE},
 {5804, NORMAL},
 {5961, NORMAL},
 {5973, NORMAL},
 {6122, NORMAL},
 {6279, NORMAL},
 {6429, NORMAL},
 {6672, NORMAL},
 {6925, NORMAL}
};

To move between positions, the system determines the positional difference and the direction to go then moves.

Challenges

Well, its almost that easy, with one catch.

Slack. The inherent play in the drive train mechanism; mostly play in the gears. The system is amazingly tight, but you can’t eliminate all slack without making it immovable. Lord knows I tried. So long as you always move in the same direction, you don’t even know its there. But when you reverse directions, the slack has to unwind and rewind, eating steps without moving the bridge and leaving you short of your destination.

On my turntable, the SLACK factor that is working is 20 steps. The function that sets a new position for the turntable and starts motion automatically adds/subtracts the SLACK factor whenever the bridge changes direction of rotation.

void GoToPosition(int id){
 struct ttp *pdata;
 digitalWrite(ROTATING_BEACON, HIGH);
 
 if(id == 0) {
   FindAnchor();
 } else {
   if(id > 0 && id <= NUM_POSITIONS && id != current_position) {
     pdata = &positions[id - 1];
     in_motion = true;
     destination = pdata->steps;
     if(destination < current_step) { // counterclockwise to destination
       digitalWrite(dir, COUNTERCLOCKWISE);
       if(current_direction != COUNTERCLOCKWISE){
         current_direction = COUNTERCLOCKWISE;
         current_step += SLACK; // compensate for change in direction
       }
     } else { // clockwise to destination
       digitalWrite(dir, CLOCKWISE);
       if(current_direction != CLOCKWISE){
         current_direction = CLOCKWISE;
         current_step -= SLACK; // compensate for change in direction
      } 
     }
   pdata->polarity == NORMAL ? reverser->setNormal(): reverser->setReverse();
   }
  }
}

The Reverser or Polarity Manager

GoToPosition() automatically sets the track polarity as required for the new position. The polarity manager is a DPDT relay with crossover wiring that reverses polarity. Nothing odd about that. To control it, I use a darlington array as the intermediary because of the current draw of the relay.

Relays come in two types — standard which always reverts position when the coil is off, or bistable which hold position without power between coil uses. I use both types on the layout, though I have come to the conclusion that the bistable type is best for track power management. From a logic standpoint, bistable relays require two connections to the coils, but standard require only 1 coil connection. The downside of the standard relay is that it has to draw power to maintain the non-default position  The turntable reverser uses a bistable relay which only requires brief coil energizing to change relay position.

Bridge Track Power Polarity Manager

To run this or other reversers, I’ve created a simple polarity_manager class that can use either relay type.

#define RELAY_BISTABLE 1
#define RELAY_REVERTS 2
#define POLARITY_NORMAL 1
#define POLARITY_REVERSE 0
#define TRIGGER_PERIOD 50

class polarity_manager
{
 // Properties
 private:
   int relayType;
   byte pinNormal;
   byte pinReverse;
   int state;
 public:
   // Constructor
   polarity_manager(int rtype, byte pnormal, byte preverse){
     relayType = rtype;
     pinNormal = pnormal;
     pinReverse = preverse;
 
     if(relayType == RELAY_BISTABLE && pnormal >= 0){
       pinMode(pinNormal, OUTPUT);
     }
     pinMode(pinReverse, OUTPUT);
     digitalWrite(pinReverse, LOW);
     setNormal();
   }
 
   int getPolarity(){
     return state;
   }
   void setNormal(){
     switch(relayType){
       case RELAY_BISTABLE:
         digitalWrite(pinNormal, HIGH);
         delay(TRIGGER_PERIOD);
         digitalWrite(pinNormal, LOW);
         break;
      case RELAY_REVERTS:
        digitalWrite(pinReverse, LOW);
        break;
      }
    state = POLARITY_NORMAL;
  }
  void setReverse(){
    switch(relayType){
      case RELAY_BISTABLE:
        digitalWrite(pinReverse, HIGH);
        delay(TRIGGER_PERIOD);
        digitalWrite(pinReverse, LOW);
        break;
      case RELAY_REVERTS:
       digitalWrite(pinReverse, HIGH);
       break;
     }
     state = POLARITY_REVERSE;
    }
  };

Things that Slowed Me Down

Once I had the Easy Driver I had complete control of positioning. So the electronics were straight forward and, though finding positions was tedious, that part went pretty fast.

The bridge, however, almost did me in, forcing me to remove and reinstall it several times.

The arch proved a little too delicate (and my adult arms a tad careless when reaching across the layout) and was broken twice. The beacon was broken off the first time and had to be remade. The beacon is made from three very small SMD leds with magnet wire; it is difficult to make to say the least.

After the second rebuild, one element of the beacon went dead. So I had to make it a third time.

In the mean time, I had some initial trouble getting the bridge rails even with the rails on the edge of the pit. If I were to start over, I’d come up with a way to raise/lower/level the deck with screws.  In this case, I used a blob of clear acrylic caulk at each end of the deck to adhere it to the girders below. I positioned the bridge so it lined up with rails on both ends, then set and held the bridge level with the rim tracks using hemostats as clamps while the caulk set. One advantage of the caulk method is I can remove the deck by slicing through the caulk at both ends.

The Turntable Class

Like the turnout class and the fire class, the turntable class creates a multitasking object. So, the turntable uses my standard multitasking method of incrementally acting through frequent calls to its update function in the main loop. If you have been following along, the code should look similar to code you’ve seen before:

void update(unsigned long current_millis){
 if(in_motion){
   if(current_millis - last_update >= step_delay){
     last_update = current_millis;
     doStep();
     current_direction == CLOCKWISE ? current_step++: current_step--;
     in_motion = current_step == destination ? false: true;
     if(!in_motion){
       digitalWrite(ROTATING_BEACON, LOW);
       setEDPins();
     }
   }
 }
}

Code

The class definitions for the turntable (turntable.h) and the reverser (polarity_manager.h) are available on the github site. To use these copy them to your sketch directory, then put #include “turntable.h” and #include “polarity_manager.h” at the top of your main sketch file.

Coming Next

In the course of integrating the software pieces, I made a small but important modification in the way the the fire class is updated. I will blog about that shortly.

Then, it’s time to introduce you to the lower level of module 2. Its small but full of detail. The most special feature of module 2 is I’m starting to experiment with sound. I’m already certain that I will be retrofitting module 1 for sound and will have sound throughout the upper level. As usual, with the help of a few inexpensive specialized parts, its far easier to add this feature than you might have imagined.

Until then, Happy Railroading!

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *