Skip to main content

Turntables...using Stepper Motors!

Maybe a little out of my league since the TxNamib Railroad does not reach far enough south to connect to Luderitz, where the last, unconnected turntable is.

But a good friend Chip Romig had turntable trouble and we decided implement two manual controls for the "get it closer quicker" and then "align it perfectly, going slower, smoothly" Arduino.

Of course, people who has the turntable as the main focus point, want to have better control, including sensors to align the table, but certainly has spent the time setting it up and calibrating it. And quite a number of times doing it again, which is where Chip decided to let the operator do all the alignment work himself.

So here it is, the crude two center-off-toggle switch solution to move a stepper motor faster with the first toggle and then slower with the second, so you can align them by eye-sight, like I've seen them do in Durango.


/*
  4 inputs, pulled high internally. Clockwise, counterclock-
   wise and another set for going even slower.
  Cycle through the states to turn the stepper clockwise or 
   in reverse for ccw, using the 4 output pins to drive the
   FETs turning the coils on with the correct polarity.
  LED flash every 2.5 seconds or faster depending on 
   which mode it is in.
 
  Author: Gert 'Speed' Muller
  2016.03.28
*/

#define VERSION "Stepper r004"
#define LED                13
// 4 Stepper motor pins:
#define P1                  4
#define P2                  5
#define P3                  6
#define P4                  7

#define DELAYBETWEEN        3
#define DELAYBETWEENMAX    20
#define DELAYPIN            5

#define TIMETOCHANGE     2000

unsigned long before   =    0;
unsigned long now      =    0;

int timestep =   DELAYBETWEEN;
int readByte =              0;

// which pin is being toggled
int state =                 0;

// bigger than zero to avoid divide by 
zero, see LED toggle later
int mode =                  1;
    
// The setup function runs once when 
// you press reset or power the board
void setup( ) {
  pinMode( LED, OUTPUT );
  digitalWrite( LED, LOW );

  pinMode( P1, OUTPUT );
  pinMode( P2, OUTPUT );
  pinMode( P3, OUTPUT );
  pinMode( P4, OUTPUT );
  digitalWrite( P1, LOW );      // turn pin off
  digitalWrite( P2, LOW );      // turn pin off
  digitalWrite( P3, LOW );      // turn pin off
  digitalWrite( P4, LOW );      // turn pin off

  Serial.begin( 115200 );
  Serial.println( VERSION );

// need a GND pin on the same side as the A1-4 inputs
  pinMode( A0, OUTPUT );         
  digitalWrite( A0, LOW );      // so drive it low.

  pinMode( A1, INPUT_PULLUP );  // CW slow
  pinMode( A2, INPUT_PULLUP );  // CCW slow
  pinMode( A3, INPUT_PULLUP );  // CW slower
  pinMode( A4, INPUT_PULLUP );  // CCW slower

  // check inputs on startup, no real 
  // other use, just debug!
  if ( digitalRead( A1 ) != HIGH ) {
    Serial.println( "A1 LOW" );
  } // if
  if ( digitalRead( A2 ) != HIGH ) {
    Serial.println( "A2 LOW" );
  } // if
  if ( digitalRead( A3 ) != HIGH ) {
    Serial.println( "A3 LOW" );
  } // if
  if ( digitalRead( A4 ) != HIGH ) {
    Serial.println( "A4 LOW" );
  } // if

  now = millis( );
  before = now;

  state = 0;
  mode = 1;
} // setup


void stepPin( char pin, char multiply ) {
  digitalWrite( pin, HIGH );      // coil on
  delay( DELAYPIN );              // wait
  digitalWrite( pin, LOW );       // coil off
  delay( DELAYPIN );              // wait
  delay( timestep * multiply ); 
} // stepPin


void counterOrClockWise( bool COUNTER, char multiply ) {
  if ( COUNTER ) {
    state -= 1;  // states decreases going counter clockwise
    if ( state < 0 ) state = 3; // roll under goes to 3
  } else {
    state += 1;  // states increases going clockwise
    if ( state > 3 ) state = 0; // roll over goes to 0    
  } // else
  
  switch ( state ) {
    case 0:
      stepPin( P1, multiply ); 
      break;
    case 1:
      stepPin( P3, multiply );
      break;
    case 2:
      stepPin( P2, multiply );
      break;
    case 3:
      stepPin( P4, multiply );
      break;
    default:
      state = 0;
      break;      
  } // switch
} // clockwise


// the loop function runs over and over again, forever
void loop( ) {
  // sometimes you missed the VERSION, 
  // so sending a '?' repeats it
  if ( Serial.available( ) > 0 ) {
    readByte = Serial.read( );
    if ( readByte == '?' ) {
      Serial.println( VERSION );
    } // if help    
  } // if available
  
  // time tells if we need to toggle the LED
  now = millis( );

  // toggling the LED with a rate based on the mode
  if ( ( now - before ) > ( TIMETOCHANGE / mode ) ) {
    before = now;
    digitalWrite( LED, !digitalRead( LED ) );  // toggle LED
  } // if

  // look which pin is connected to ground and do the highest 
  // priority one, clockwise slow, ccw slow, cw slower, 
  // ccw slower, else do nothing
  if ( digitalRead( A1 ) != HIGH ) {
    counterOrClockWise( false, 0 );  // false:CW, 0:slow
    mode = 2;
  } else {
    if ( digitalRead( A2 ) != HIGH ) {
      counterOrClockWise( true, 0 ); // true:Counter, 0:slow
      mode = 3;
    } else {
      if ( digitalRead( A3 ) != HIGH ) {
        counterOrClockWise( false, 1 );   // false:CW, 1:slower
        mode = 4;
      } else {
        if ( digitalRead( A4 ) != HIGH ) {
          counterOrClockWise( true, 1 );  // true:Counter, 
                                          // 1:slower    
          mode = 5;
        } else {
          mode = 1;  // no button, flash LED slowest!  
        } // else, not cw0
      } // else, not ccw0
    } // else, not cw1
  } // else, not ccw1
} // loop

// Sketch uses 3042 bytes (9%) of program 
//   storage space. Maximum is 30720 bytes.
// Global variables use 240 bytes (11%) of dynamic 
//   memory, leaving 1808 bytes for local variables. 
//   Maximum is 2048 bytes.


The Stepper motor for the code above, has 5 or 6 wires, which can be controlled with the 5th or the 5th and 6th wire connected to Vcc and then turning any of the 4 coils on by connecting the other wires to ground. Of course, not all at the same time, but by following the sequence diagram as shown in the motor's datasheet. This is called the Unipolar motor configuration.


So WHT and YEL gets connected to 12Vdc and BLK, GRN, RED and BLU is switched to GND when needed.

In the Bipolar motor configuration you only get 4 wires (see BLK,GRN,RED and BLU below).

 The polarity of BLK/GRN and RED/BLU is switched when needed.

To turn the coils on with current flowing in all four steps, you need to be able to control the direction the current through the 2 coils as well. This requires an H-bridge circuit ($0.15 on Ebay), and shown below:

One coil shown, blue and green for opposite current flows

Two H-bridge circuit for less than $1
And here is a short sketch to control the Stepper motor in your floppy disk or CDROM drive in one direction:


/*
  2 inputs, pulled high internally. Clockwise/Counterclock-
   wise and On/Off.
  Cycle through the states to turn the stepper clockwise or 
   in reverse for ccw, using the 4 output pins to drive the
   H-bridge turning the bi-polar coils on with the correct polarity.
  LED is on in the first phase (well for DELAY milliseconds)
 
  Author: Gert 'Speed' Muller
  2017.10.31
*/

#define Coil1A    4
#define Coil1B    5
#define Coil2A    6
#define Coil2B    7
#define InputPin A0
#define LEDPin   13
#define DELAY    50

unsigned int myPhase;

void setup() {
  pinMode( Coil1A, OUTPUT );
  pinMode( Coil1B, OUTPUT );
  pinMode( Coil2A, OUTPUT );
  pinMode( Coil2B, OUTPUT );
  pinMode( LEDPin, OUTPUT );
  pinMode( InputPin, INPUT_PULLUP );

  myPhase = 0;
  myStep( myPhase );

  digitalWrite( LEDPin, HIGH );   // flash with boot message
  Serial.begin( 115200 );
  delay( 100 );
  Serial.write( "StepperFan 0.1..\n" );
  digitalWrite( LEDPin, LOW );


} // setup

void myStep( unsigned int phase ) {
  digitalWrite( LEDPin, LOW );            // turn the LED off
  switch( phase ) {
    case 1: digitalWrite( LEDPin, HIGH ); // turn the LED on
            digitalWrite( Coil1A, LOW );
            digitalWrite( Coil1B, HIGH );
            digitalWrite( Coil2A, LOW );
            digitalWrite( Coil2B, HIGH );
            break;
            
    case 2: digitalWrite( Coil1A, HIGH );
            digitalWrite( Coil1B, LOW );
            digitalWrite( Coil2A, LOW );
            digitalWrite( Coil2B, HIGH );
            break;
            
    case 3: digitalWrite( Coil1A, HIGH );
            digitalWrite( Coil1B, LOW );
            digitalWrite( Coil2A, HIGH );
            digitalWrite( Coil2B, LOW );
            break;
            
    case 4: digitalWrite( Coil1A, LOW );
            digitalWrite( Coil1B, HIGH );
            digitalWrite( Coil2A, HIGH );
            digitalWrite( Coil2B, LOW );
            break;
    default:
            digitalWrite( Coil1A, LOW );
            digitalWrite( Coil1B, LOW );
            digitalWrite( Coil2A, LOW );
            digitalWrite( Coil2B, LOW );
            break;
  } // switch
} // myStep

void loop() {
  delay( DELAY );
  if ( digitalRead( InputPin ) == LOW ) {
    myPhase++;
    if ( myPhase > 4 ) myPhase = 1;
  } else {
    myPhase--;
    if ( myPhase < 1 ) myPhase = 4;
  } // if LOW
  
  myStep( myPhase );
} // loop

// Sketch uses 2400 bytes (7%) of program 
//   storage space. Maximum is 30720 bytes.
// Global variables use 202 bytes (9%) of dynamic 
//   memory, leaving 1846 bytes for local variables. 
//   Maximum is 2048 bytes.

Who does not have an unused CDROM drive?

There are also three or four example Stepper motor sketches in the Arduino IDE’s Example folder if you only need to control a single Stepper motor. Please try them all out. They use the local Stepper library, defined in Stepper.h to create a Stepper object with the number of steps you complete a rotation and the 4 pins connected to the Stepper, or the switches controlling the Stepper windings, example:

#define STEPS 100
 Stepper stepper( STEPS, 8, 9, 10, 11 );

And in the setup( ) function they typically set the speed in RPMs:
stepper.setSpeed( 30 );

And what follows in some form or fashion in loop( ):
stepper.step( numberOfSteps );
You can now add a potentiometer and either increase the position by stepping forward, or decrease it be step backwards with negative numbers, or keep stepping and change the speed with setSpeed according to the rotation of the knob on the potentiometer.

For controlling more than one Stepper, or need acceleration and deceleration, then AccelStepper would be a better choice.

Moving a Stepper back and forth with AccelStepper’s bounce example:

#include <accelstepper.h&gt
AccelStepper stepper;             // Defaults to the 4 pins on 2,3,4 and 5

void setup( ) { 
  stepper.setMaxSpeed( 100 );     // set the speed
  stepper.setAcceleration( 20 );  // set the acceleration
  stepper.moveTo( 500 );          // set the target position
} // setup

void loop( ) {   
   if ( stepper.distanceToGo( ) == 0 ) // when it gets there
     stepper.moveTo( -stepper.currentPosition( ) );
                                  // change the target

   stepper.run( );                // make at most one step, call this often
} // loop
Something you need to keep in mind, a call to the Stepper.step( ) function is “blocking”, meaning that you will need to wait for that command to finish before you can do the next thing. Not important if you are simply commanding something scripted play by play, but if you also need to read an input like an Infrared TV remote control or an LCC command coming from JMRI, you might miss that completely while the Stepper code is “blocking”. On the other hand, a call to AccelStepper.run( ); will only check if a step is needed, make a step and return, or if not needed, just return. In this “non-blocking” way, you can quickly check if a command was sent over the Serial, Infrared or CAN bus and process that command as well.
When the coils in the motor have a DC resistance of 9 or 25 Ohms, you can calculate that a 5 Vdc supply would need to deliver 0.55 A or 0.2 A (from I = V/R) and the Arduino board or chip by itself cannot provide that current. So the Vs pin on the H-bridge circuit need to go straight to a good power supply and not the 5 Vdc on the Uno or Nano board.

Popular posts from this blog

Roll-up Doors with a State Machine

Roll-up a door please!  (with a State Machine in code, part 1) MHO Junction’s Molson Brewery in a 3D Layout View ( all photos and drawings by Lloyd Henchey, Ontario, Canada) We all know we can open and close close something with an Arduino and a Servo, but what if we want to a little bit more operating fun with some lights and sounds, all after a manager gives approval to open or close the door to the beer brewery before your engine is allowed to pick up or deliver a car? Control Panel at the brewery   Six doors under construction at the Molson brewery on the MHO Junction  Engine pulls up to the red light, shift manager (operator) grants permission, door raises up with alarms and a flashing red light, door goes all the way up, light turns green , engine or car can proceed! Same flashing red light going down and maybe different door squeaking noises. All good articles start with a YouTube video: youtube.com/watch?v=-8EnpUgTqR4 ! This article will introduce you to a “Finite State Mac

Pi Pico, we smell competition in the land of the RRRduino! 

Pi Pico on an N-scale gondola Pi Pico, we smell competition in the land of the ‘duino!  Our very favorite low cost microcontroller system is seeing some fresh competition.  Everyone by now has heard about the Raspberry Pi, some fruity company in the United Kingdom, making single board computers!  They run Raspbian (or other flavors of Linux and are capable of some Windows versions) for as little money as $10 for the Pi Zero W.  The more popular Model 4, with 2 GB of RAM, retails for about $29.  Add a $5  micro SD card and you have a real computer with which you can surf the internet, write code and even program Arduinos.  It also runs our other favorite, JMRI.  Of course, plug it into a small or big screen television with an HDMI cable and you can even stream Netflix.  If you want a really cool computer built into a keyboard, also check out the brand new, Raspberry Pi 400 , you might just think you own a ZX Spectrum again. These are all “computers” with processors and now the Raspberry

Not too important IR remote info...

Onn 4-Device Universal Remote manual... setting codes... Up Arrow TV Mode: Decoded NEC(1) : Value:20DF00FF Adrs:0 (32 bits) Raw samples(68): Gap:5484   Head: m8800  s4500  0:m550 s600     1:m500 s600            2:m500 s1700     3:m550 s550           4:m550 s600     5:m500 s600            6:m500 s600      7:m550 s550           8:m500 s1700    9:m550 s1700          10:m500 s600     11:m550 s1650          12:m550 s1700    13:m550 s1650         14:m550 s1650    15:m550 s1700          16:m500 s600     17:m500 s600          18:m500 s600     19:m550 s550          20:m550 s600     21:m500 s600          22:m500 s600     23:m500 s600          24:m550 s1650    25:m550 s1700         26:m550 s1650    27:m550 s1650          28:m550 s1700    29:m550 s1650         30:m550 s1650    31:m550 s1700          32:m550 Extent=67100 Mark  min:500     max:550 Space min:550     max:1700 CBL Mode: Decoded RC6(4) : Value:2758 Adrs:0 (20 bits)  Raw samples(36): Gap:43150   Head: m2600  s900  0:m350 s900