Fun with Arduino 34 Stepper Motor Control Rotate Exact Amount of Steps

With the code of the previous video 33 we are able to ‘zero’ the stepper motor. From that point on we can position the motor by counting up or down an exact number of steps. This way we can accurately position, say, a turn table or an elevator, or anything we like.

For most applications the number of pulses to rotate from one position to another can be hard coded. With the code shown here the number can be entered via the serial monitor, just for demonstration purposes.

Read on below the video …

With running the motor continuously it was nice that we could change direction while running, but now that we want to run an exact amount of steps, changing direction while running is NOT what we want. We need to disable that … we will read the dir_switch before we start to rotate and once we are running we do not look at it anymore.

The rotation is done inside a function. What is new is that it is a function to which a number is sent when it is called in the loop(). The number that we send is the number of steps that we want to rotate, it is an unsigned long.

The code to accurately position the stepper motor after finding the zero point:

#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 ONOFF_PIN        12 // LOW starts the motor
#define DIR_PIN           8 // LOW = rotate CCW, HIGH = rotate CW
#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 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 int numsteps_reading = PULSES_PER_REV;
unsigned long timeoflaststep;

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 find_zero() {
  dir = 0;
  Serial.println("Finding zero sensor");
  while(digitalRead(ZERO_FOUND_PIN) == HIGH) {
    if ((micros() - timeoflaststep) > 60000000UL / PULSES_PER_REV) {
    timeoflaststep = micros();
    do_one_step();
    }
  }
  motor_idle();
  Serial.println("Zero sensor found");
  Serial.println();
}

void read_numsteps() {
  if (Serial.available() > 0) {
    numsteps_reading = Serial.parseInt();
    Serial.print("Number of steps changed to: ");
    Serial.println(numsteps_reading);
    Serial.println();
  }
}

void rotate(unsigned long numsteps) {
  digitalWrite(ONOFF_LED_PIN, HIGH);
  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 read_dir() {
  dir = digitalRead(DIR_PIN);
  digitalWrite(DIR_LED_PIN, !dir);
}

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(ONOFF_PIN,     INPUT_PULLUP);
  pinMode(DIR_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);
  pinMode(DIR_LED_PIN,   OUTPUT);
  Serial.begin(9600);
  Serial.println("Don't forget to find zero before operating");
  Serial.println();
  Serial.println("Type the number of steps to go");
  Serial.println("in the field above and hit Enter");
  Serial.println("1 revolution = 2048 steps");
}

void loop() {
  stepinterval(); // read speed potmeter, show RPM on serial monitor
  read_numsteps();
  read_dir();

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

  if(digitalRead(ONOFF_PIN) == 0) rotate(numsteps_reading);
}

Yes … this is OK.

There is an Arduino stepper library available which could also be used. It shortens the code, we do not need the do_one_step() and the stepinterval() function anymore, the library takes care of that. What I did not like about the library though is that:

  • with each step it switches on 2 coils which is not needed for our toy motor
  • it does not switch off the motor coils after it reached its position, a current keeps flowing through 2 coils
  • it is not possible to change speed while running.

On to the next video where we are going to control a model railway turntable with 3 positions.

— 0 —

4 thoughts on “Fun with Arduino 34 Stepper Motor Control Rotate Exact Amount of Steps

Leave a comment