Fun with Arduino 35 Turn Table Control with Stepper Motor

With the ingredients of previous two videos, find zero and rotate an exact amount of steps, we’re able to control a turn table that has multiple stop positions. We need to find out how many steps it takes, counting from the zero point, to reach every position. We’ll use a separate ‘Tuning’ sketch for this. We will use digital inputs to send the turn table to any position we like. These inputs can be manually activated, by say push buttons’ or they can be connected to a DCC accessory decoder for automated operation.

For the tuning we will use two push buttons, one to rotate CW, one to rotate CCW. The serial interface shows the number of steps to the current position. We can write down these numbers to later enter them into the Turntable sketch. The two sketches can be combined into one, storing the numbers into EEPROM. But we haven’t talked about EEPROM yet, the next video we will. For the sake of simplicity, right now the Tuning sketch is separate.

Read on below the video …

The code for the stepper motor Tuning to find the stop positions:

#define MOTOR_PIN_1      A1
#define MOTOR_PIN_2      A2
#define MOTOR_PIN_3      A3
#define MOTOR_PIN_4      A4
#define POTM_PIN         A5
#define RUN_CW_PIN        6 // LOW = run CW
#define RUN_CCW_PIN       7 // LOW = run CCW
#define ZERO_PIN          9 // Start the zero find routine
#define ZERO_FOUND_PIN   12 // Switch or sensor of zero position
#define ONOFF_LED_PIN     5 // Motor running LED
#define DIR_LED_PIN       4 // Motor direction LED
#define PULSES_PER_REV 2048 // Pulses per revolution of blue/metal toy motor

byte dir, rpm, rpm_old, stepnr, state;
unsigned long pos, pos_old, timeoflaststep;

void find_zero() {
  Serial.println("Finding zero sensor");
  dir = 1;
  while(digitalRead(ZERO_FOUND_PIN) == HIGH) {
    if ((micros() - timeoflaststep) > stepinterval()) {
    timeoflaststep = micros();
    do_one_step();
    }
  }
  motor_idle();
  pos = 0;
  pos_old = 0;
  Serial.println("Zero sensor found");
  Serial.println(pos);
}

unsigned long stepinterval() { // calculates step timing based on potmeter input
  rpm = map(analogRead(POTM_PIN), 0, 1024, 1, 13); // max 12 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 motor_idle() {
  digitalWrite(MOTOR_PIN_1,   LOW);
  digitalWrite(MOTOR_PIN_2,   LOW);
  digitalWrite(MOTOR_PIN_3,   LOW);
  digitalWrite(MOTOR_PIN_4,   LOW);
  digitalWrite(ONOFF_LED_PIN, LOW);
}

void rotate() {
  if ((micros() - timeoflaststep) > stepinterval()) {
    timeoflaststep = micros();
    do_one_step();
    if(dir == 1) pos--;
    else pos++;
  }
}

void do_one_step() { 
  if(dir == 0) stepnr--;
  else stepnr++;
  stepnr = stepnr%4;

  switch (stepnr) { // remembers in which of the 4 phases the motor is
    case 0:
      digitalWrite(MOTOR_PIN_1, HIGH);
      digitalWrite(MOTOR_PIN_2, LOW);
      digitalWrite(MOTOR_PIN_3, LOW);
      digitalWrite(MOTOR_PIN_4, LOW);
    break;
    case 1:
      digitalWrite(MOTOR_PIN_1, LOW);
      digitalWrite(MOTOR_PIN_2, HIGH);
      digitalWrite(MOTOR_PIN_3, LOW);
      digitalWrite(MOTOR_PIN_4, LOW);
    break;
    case 2:
      digitalWrite(MOTOR_PIN_1, LOW);
      digitalWrite(MOTOR_PIN_2, LOW);
      digitalWrite(MOTOR_PIN_3, HIGH);
      digitalWrite(MOTOR_PIN_4, LOW);
    break;
    case 3:
      digitalWrite(MOTOR_PIN_1, LOW);
      digitalWrite(MOTOR_PIN_2, LOW);
      digitalWrite(MOTOR_PIN_3, LOW);
      digitalWrite(MOTOR_PIN_4, HIGH);
    break;
  }
}

void setup() {
  pinMode(RUN_CW_PIN,    INPUT_PULLUP);
  pinMode(RUN_CCW_PIN,   INPUT_PULLUP);
  pinMode(ZERO_PIN,      INPUT_PULLUP);
  pinMode(ZERO_FOUND_PIN,INPUT_PULLUP);
  pinMode(MOTOR_PIN_1,   OUTPUT);
  pinMode(MOTOR_PIN_2,   OUTPUT);
  pinMode(MOTOR_PIN_3,   OUTPUT);
  pinMode(MOTOR_PIN_4,   OUTPUT);
  pinMode(ONOFF_LED_PIN, OUTPUT);
  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();
  
  while(!digitalRead(RUN_CW_PIN)) {
    dir = 1;
    rotate();
  }

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

Lets use this code, find zero and then record 3 turn table positions. Let’s say the positions found are 0 (for the zero sensor), 100, 612 and 1124. These now are the numbers that we need to fill in for the stop positions in the code for our turntable:

unsigned long tt_position[4] = {0,100,612,1124};

The functions used are similar to the previous stepper motor videos. The loop() now takes care of reading the digital inputs that tell us the target turntable position. We then calculate the number of pulses and the direction to the new position, based on the position where we currently are.

#define MOTOR_PIN_1      A1
#define MOTOR_PIN_2      A2
#define MOTOR_PIN_3      A3
#define MOTOR_PIN_4      A4
#define POTM_PIN         A5
#define POS_1_PIN         9 // LOW = go to pos 1
#define POS_2_PIN         8 // LOW = go to pos 2
#define POS_3_PIN        12 // LOW = go to pos 3
#define ZERO_PIN          6 // Start zero find routine
#define ZERO_FOUND_PIN    7 // End switch to find zero position
#define ONOFF_LED_PIN     5 // Motor running LED
#define PULSES_PER_REV 2048 // Pulses per revolution of blue/metal toy motor

// Define number of positions, and steps from zero to each position
unsigned long tt_position[4] = {0,100,612,1124};

byte dir, current_pos, new_pos, rpm, rpm_old, stepnr, state;
unsigned long timeoflaststep;

void find_zero() {
  Serial.println("Finding zero sensor");
  dir = 0;
  while(digitalRead(ZERO_FOUND_PIN) == HIGH) {
    if ((micros() - timeoflaststep) > 60000000UL / PULSES_PER_REV) {
    timeoflaststep = micros();
    do_one_step();
    }
  }
  motor_idle();
  current_pos = 0;
  new_pos = 1; // move to pos 1 after finding zero
  Serial.println("Zero sensor found");
  Serial.println();
}

unsigned long stepinterval() { // calculates step timing based on potmeter input
  rpm = map(analogRead(POTM_PIN), 0, 1024, 1, 13); // max 12 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(unsigned long numsteps) {
  digitalWrite(ONOFF_LED_PIN, HIGH);
  Serial.print("To pos ");
  Serial.print(new_pos);
  Serial.print(", ");
  Serial.print(numsteps);
  Serial.print(" steps, dir = ");
  Serial.println(dir);
  while(numsteps > 0) {
    if ((micros() - timeoflaststep) > stepinterval()) {
      timeoflaststep = micros();
      do_one_step();
      numsteps--;
    }
  }
  motor_idle();
}

void motor_idle() {
  digitalWrite(MOTOR_PIN_1,   LOW);
  digitalWrite(MOTOR_PIN_2,   LOW);
  digitalWrite(MOTOR_PIN_3,   LOW);
  digitalWrite(MOTOR_PIN_4,   LOW);
  digitalWrite(ONOFF_LED_PIN, LOW);
}

void do_one_step() { 
  if(dir == 0) stepnr--;
  else stepnr++;
  stepnr = stepnr%4;

  switch (stepnr) { // remembers in which of the 4 phases the motor is
    case 0:
      digitalWrite(MOTOR_PIN_1, HIGH);
      digitalWrite(MOTOR_PIN_2, LOW);
      digitalWrite(MOTOR_PIN_3, LOW);
      digitalWrite(MOTOR_PIN_4, LOW);
    break;
    case 1:
      digitalWrite(MOTOR_PIN_1, LOW);
      digitalWrite(MOTOR_PIN_2, HIGH);
      digitalWrite(MOTOR_PIN_3, LOW);
      digitalWrite(MOTOR_PIN_4, LOW);
    break;
    case 2:
      digitalWrite(MOTOR_PIN_1, LOW);
      digitalWrite(MOTOR_PIN_2, LOW);
      digitalWrite(MOTOR_PIN_3, HIGH);
      digitalWrite(MOTOR_PIN_4, LOW);
    break;
    case 3:
      digitalWrite(MOTOR_PIN_1, LOW);
      digitalWrite(MOTOR_PIN_2, LOW);
      digitalWrite(MOTOR_PIN_3, LOW);
      digitalWrite(MOTOR_PIN_4, HIGH);
    break;
  }
}

void setup() {
  pinMode(POS_1_PIN,     INPUT_PULLUP);
  pinMode(POS_2_PIN,     INPUT_PULLUP);
  pinMode(POS_3_PIN,     INPUT_PULLUP);
  pinMode(ZERO_PIN,      INPUT_PULLUP);
  pinMode(ZERO_FOUND_PIN,INPUT_PULLUP);
  pinMode(MOTOR_PIN_1,   OUTPUT);
  pinMode(MOTOR_PIN_2,   OUTPUT);
  pinMode(MOTOR_PIN_3,   OUTPUT);
  pinMode(MOTOR_PIN_4,   OUTPUT);
  pinMode(ONOFF_LED_PIN, OUTPUT);
  Serial.begin(9600);
  Serial.println("Don't forget to find zero before operating");
  Serial.println();
}

void loop() {

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

  if(digitalRead(ZERO_PIN) == 0) find_zero();
  if(digitalRead(POS_1_PIN) == 0) new_pos = 1;
  if(digitalRead(POS_2_PIN) == 0) new_pos = 2;  
  if(digitalRead(POS_3_PIN) == 0) new_pos = 3;

  if(new_pos != current_pos) {
    if(new_pos > current_pos) {
      dir = 1;
      rotate(tt_position[new_pos] - tt_position[current_pos]);
    }
    else {
      dir = 0;
      rotate(tt_position[current_pos] - tt_position[new_pos]);
    }
    current_pos = new_pos;
  }
}

OK … time to test. Yes, this works fine. It’s only 3 positions in this case … but it could as well more. At some point we run out of digital inputs … what to do then? We could use an Arduino Mega, which has a lot more inputs available. Alternatively I2C could be used … a two wire communication protocol which will be the subject of a future video.

Next video we’ll have a look at using the built in EEPROM to store the latest turn table position. That way we do not need to do a zero find routine every time we start up … at startup we can read the memory and we know where we are. After every move we write the current position to memory.

— 0 —

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s