Help with code for a turntable project

I am trying to adpat a sketch written for a unipolar stepper to one to control a bipolar stepper motor. I am using an Arduino Uno, an easy driver motor controler and a NEMA 17 stepper motor. The sketch I am using will successfully execute the find zero routine but does not execute to rotate CW or CCW routine nor print out the step value as called for in the void loop. The cod is as follows.

\\Model Railroad Turntable Control
\\ Finding zero/homing position and printing out steps.
\\ Sketch adapted for a bipolar stepper motor. 

#define ENABLE_PIN A0       // LOW = driver enabled
#define M1_PIN A1           // M1 microstepping mode
#define M2_PIN A2           // M2 microstepping mode
#define DIR_PIN A3          // to DIR pin of the driver
#define STEP_PIN A4         // to STP pin of driver
#define POTM_PIN A5         // stepper speed control
#define ENABLE_LED_PIN 4    // stepper running LED
#define DIR_LED_PIN 5       // stepper direction LED
#define DIR_CW_PIN 6        // CW stepper direction control
#define DIR_CCW_PIN 7       // CCW stepper direction control
#define ZERO_PIN 9          // Start the zero find routine
#define ZERO_FOUND_PIN 10   // Switch or sensor of zero position
#define PULSES_PER_REV 200  // Pulses per revolution of blue/metal toy motor


byte dir, current_pos, new_pos, rpm, rpm_old;
unsigned long pos, pos_old, timeoflaststep;

void motor_enable() {
  digitalWrite(ENABLE_PIN, LOW);  // LOW = driver enabled
  digitalWrite(ENABLE_LED_PIN, HIGH);
  digitalWrite(DIR_PIN, dir);
}

void motor_idle() {
  digitalWrite(ENABLE_PIN, HIGH);  // HIGH = driver disabled
  digitalWrite(ENABLE_LED_PIN, LOW);
}




void find_zero() {
  Serial.println("Finding zero sensor");
  dir = 0;
  motor_enable();
  while (digitalRead(ZERO_FOUND_PIN) == HIGH) {
    if ((micros() - timeoflaststep) > stepinterval()) {
      timeoflaststep = micros();
      digitalWrite(STEP_PIN, HIGH);
      //  delayMicroseconds(10); // only needed if step pulse is too short to be detected
      digitalWrite(STEP_PIN, LOW);
    }
  }
  motor_idle();
  Serial.println("Zero sensor found");
  Serial.println();
  current_pos = 0;
  new_pos = 1;
}

unsigned long stepinterval() {                           // calculates step timing based on potmeter input
  rpm = map(analogRead(POTM_PIN), 0, 1024, 1, 21) * 10;  // max 200 rpm, else pulses get lost
  if (rpm != rpm_old) {
    Serial.print("RPM: ");
    Serial.println(rpm);
    rpm_old = rpm;
  }
  return 60000000UL / PULSES_PER_REV / rpm;
}

void rotate() {

  if ((micros() - timeoflaststep) > stepinterval()) {
    timeoflaststep = micros();
    digitalWrite(STEP_PIN, HIGH);
    //  delayMicroseconds(10); // only needed if step pulse is too short to be detected
    digitalWrite(STEP_PIN, LOW);
  }
}

void setup() {

  pinMode(ZERO_PIN, INPUT_PULLUP);
  pinMode(ZERO_FOUND_PIN, INPUT_PULLUP);
  pinMode(DIR_CW_PIN, INPUT_PULLUP);
  pinMode(DIR_CCW_PIN, INPUT_PULLUP);
  pinMode(M1_PIN, OUTPUT);
  pinMode(M2_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(ENABLE_PIN, OUTPUT);
  pinMode(ENABLE_LED_PIN, OUTPUT);
  pinMode(DIR_LED_PIN, OUTPUT);
  digitalWrite(M1_PIN, LOW);  // set M1,M2 to 0,0 for no microstepping
  digitalWrite(M2_PIN, LOW);
  Serial.begin(9600);
  Serial.println("Don't forget to find zero before tuning");
  Serial.println();
}

void loop() {

  stepinterval();  // read speed and show RPM on serial monitor

  if (digitalRead(ZERO_PIN) == 0) find_zero();

  while (!digitalRead(DIR_CW_PIN)) {
    dir = 1;
    rotate();
  }

  while (!digitalRead(DIR_CCW_PIN)) {
    dir = 0;
    rotate();
  }
  if (pos != pos_old) {
    Serial.println(pos);
    pos_old = pos;
  }
}

Any help with this problem will be greatly appreciated . Ted

Since you are using serial.Print() for other things, why not use it to trace the logic that does not seem to be working?

Where in loop() do you ever write the dir pin HIGH or LOW before calling rotate()?

don''t understand what this code is attempting to do

  • shouldn't find_zero() be executed once in setup ()
  • why should there be loops for both CW and CCW?
  • don't understant the purpose the the if (pos != pos_old) since it just make the 2 variables equal

where are the input that determine what position to turn the turntable to by stepping the turntable either CW or CCW the appropriate # of steps to reach a desired position?

[/quote]

Ah

Wrong sort of turntable. Turning model locomotives, not for playing "Now that's what I call music Volume 157"

Oops.
The motor is only ever enabled bay a call to motor_enable() in find_zero(), and that finishes with a motor_idle() to disable the motor, so I'd say your code is doing what it's expected to do... nothing.

Did you forget add motor_enable() to your rotate() routine?

I have made changes to the sketch I posted originally. The command for CW and CCW rotation now includes a digital write statement changing the value of the dir pin input. The problem now is that after finding zero I can not rotate CW or CCW when pressing the appropriate button. the code is as follows :

void loop() {
  motor_idle();
  stepinterval(); // read speed and show RPM on serial monitor

  if(digitalRead(ZERO_PIN) == 0) find_zero();
  
  while(!digitalRead(DIR_CW_PIN)){
  digitalWrite(DIR_PIN, LOW);
     dir = 1;
    rotate();
  }

  while(!digitalRead(DIR_CCW_PIN)){
  digitalWrite(DIR_PIN, HIGH); 
    dir = 0;
    rotate();
  }
  
  if(pos != pos_old) {
    Serial.println(pos);
    pos_old = pos;
  }
}

Please pay attention to posts #6 and #7. They have identified the error causing the motor not to run with a button press.

1 Like

won't as soon as you move off the Zero pt, it attempt to relocate it?

since it looks like you only rotate the turntable when a CW or CCW input is activated, that you don't turn the TT to a specific position, there's no need to zero it.

why not simply

void setup () {
   .
   .
   .
  motorEnable ();
}

void loop() {
  while(!digitalRead(DIR_CW_PIN)){
     digitalWrite(DIR_PIN, LOW);
     rotate();
  }

  while(!digitalRead(DIR_CCW_PIN)){
    digitalWrite(DIR_PIN, HIGH); 
    rotate();
  }
}

I have made changes to the setch I posted last. So now the command for CW and CCW rotate the stepper motor after the zero routine. The reason for the zero routine is so the exact number of steps to any point on the arc of the stepper motor is know and repeatable. Attached is a copy of the working sketch.

// Model Railroad Turntable Control //
// Finding zero/homing position and printing out steps. //
// Sketch adapted for a bipolar stepper motor. //
// 24.09.2025//

#define ENABLE_PIN  A0       // LOW = driver enabled
#define M1_PIN  A1           // M1 microstepping mode
#define M2_PIN  A2           // M2 microstepping mode
#define DIR_PIN A3          // to DIR pin of the driver
#define STEP_PIN  A4         // to STP pin of driver
#define POTM_PIN  A5         // stepper speed control
#define ENABLE_LED_PIN 4    // stepper running LED
#define DIR_LED_PIN  5       // stepper direction LED
#define DIR_CW_PIN  6        // CW stepper direction control
#define DIR_CCW_PIN  7       // CCW stepper direction control
#define ZERO_PIN  9          // Start the zero find routine
#define ZERO_FOUND_PIN  10   // Switch or sensor of zero position
#define PULSES_PER_REV  200 // Pulses per revolution of blue/metal toy motor


byte dir, current_pos, new_pos, rpm, rpm_old;
unsigned long pos, pos_old, timeoflaststep;

void motor_enable() {
  digitalWrite(ENABLE_PIN, LOW);  // LOW = driver enabled
  digitalWrite(ENABLE_LED_PIN, HIGH);
  digitalWrite(DIR_PIN, dir);
}

void motor_idle() {
  digitalWrite(ENABLE_PIN, HIGH);  // HIGH = driver disabled
  digitalWrite(ENABLE_LED_PIN, LOW);
}




void find_zero() {
  Serial.println("Finding zero sensor");
  dir = 0;
  motor_enable();
  while (digitalRead(ZERO_FOUND_PIN) == HIGH) {
    if ((micros() - timeoflaststep) > stepinterval()) {
      timeoflaststep = micros();
      digitalWrite(STEP_PIN, HIGH);
      //  delayMicroseconds(10); // only needed if step pulse is too short to be detected
      digitalWrite(STEP_PIN, LOW);
    }
  }
  motor_idle();
  Serial.println("Zero sensor found");
  Serial.println(pos);
  current_pos = 0;
  new_pos = 1;
}

unsigned long stepinterval() {                           // calculates step timing based on potmeter input
  rpm = map(analogRead(POTM_PIN), 0, 1024, 1, 21) * 10;  // max 200 rpm, else pulses get lost
  if (rpm != rpm_old) {
    Serial.print("RPM: ");
    Serial.println(rpm);
    rpm_old = rpm;
  }
  return 60000000UL / PULSES_PER_REV / rpm;
}

void rotate() {

  if ((micros() - timeoflaststep) > stepinterval()) {
    timeoflaststep = micros();
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(10); // only needed if step pulse is too short to be detected
    digitalWrite(STEP_PIN, LOW);
    if(dir == 1) pos++;
    else pos--;
  }

}

void setup() {

  pinMode(ZERO_PIN, INPUT_PULLUP);
  pinMode(ZERO_FOUND_PIN, INPUT_PULLUP);
  pinMode(DIR_CW_PIN, INPUT_PULLUP);
  pinMode(DIR_CCW_PIN, INPUT_PULLUP);
  pinMode(M1_PIN, OUTPUT);
  pinMode(M2_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(ENABLE_PIN, OUTPUT);
  pinMode(ENABLE_LED_PIN, OUTPUT);
  pinMode(DIR_LED_PIN, OUTPUT);
  digitalWrite(M1_PIN,LOW);  // set M1,M2 to 0,0 for no microstepping
  digitalWrite(M2_PIN, LOW);
  Serial.begin(9600);
  Serial.println("Don't forget to find zero before tuning");
  Serial.println();
}



  void loop() {
  motor_idle();
  stepinterval(); // read speed and show RPM on serial monitor

  if(digitalRead(ZERO_PIN) == 0) find_zero();

  motor_enable();
  while(!digitalRead(DIR_CW_PIN)){
  digitalWrite(DIR_PIN, LOW);
     dir = 1;
    rotate();
  }

  while(!digitalRead(DIR_CCW_PIN)){
  digitalWrite(DIR_PIN, HIGH); 
     dir = 0;
    rotate();
   
  }
  if(pos != pos_old) {
    Serial.println(pos);
    pos_old = pos;
  }
}

  

but where do you have a command to specify some # of either CW or CCW steps from that zero position?

the code looks like is has a momentary switch to simply rotate either CW or CCW while the button is pressed

if this isn't obvious

a turntable controller would have a programming mode to determine the position of various tracks that would be stored in EEPROM.

during normal operation, there is an input to specify a track and the controller turns the bridge the appropriate # of steps from its current position to align the bridge with that track. The controller would compare the required # of steps in the CW and CCW directions and choose the smaller # of steps and direction

the controller typically has a way to align the bridge 180 deg as well so that a loco on the bridge is facing the desired direction

the bridge is "zeroed" on start-up in both modes and resets the postion whenever it detects the zero position