Fun with Arduino 37 Control a NEMA 17 Stepper Motor with Easy Driver

StepperSome applications may need a stronger stepper motor, one that can deliver more torque. The picture shows a NEMA 17 stepper. It’s a form factor (it means the size is 1.7 x 1.7 inch) in which a range of motors with different specifications is available. They are widely used in (DIY) 3D printers and NC machines.

The A4988 or the A3967 ‘Easy Driver’ are two popular drivers for these stepper motors. They can usually operate up to 30V and they can deliver enough current. The Easy Driver even has a 5V output that can be used to power the Arduino.

A4988Easy_Driver

These drivers do not have the 4 pulse input of the toy motor, they have an enable, a  direction and a step input. This makes things easier in the software. When we want to rotate,  the enable and direction input are set first and then we send the needed amount of pulses to the step input.

Usually these drivers also have 2 or 3 inputs that are used to set a ‘micro stepping’ mode. With micro-stepping the motor can be driven at 1/2, 1/4, 1/8, or even 1/16 steps. This can make the motor run smoother, with less audible noise, as well as improve accuracy. Check the user manual of the drivers on how to use these micro stepping modes.

Read on below the video …

Let’s use the turntable control of the previous video as a basis. We do not have to change very much in our software to make this stepper driver work. The do_one_step() function, that was used to generate 4 step phases with 4 output pins, is deleted. We generate the pulses that are needed to drive the motor inside the rotate() function by simply switching on and off the step output. The enable and the dir pin of the driver are switched inside a new motor_enable() function. The motor_idle() function resets the enable output.

This is the resulting code.

#define NUM_POSITIONS     3
#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 driver
#define STEP_PIN         A4 // to DIR pin of driver
#define POTM_PIN         A5 // Used to change speed
#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 // LOW = start find zero routine
#define ZERO_FOUND_PIN    7 // LOW = zero switch found
#define ONOFF_LED_PIN    13 // Motor running LED
#define DIR_LED_PIN       4 // Motor direction LED
#define PULSES_PER_REV  200 // Pulses per revolution

// Define number of positions, and steps from zero to each position
unsigned long tt_position[NUM_POSITIONS + 1] = {0,50,300,550};

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

#include <EEPROM.h>

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

void motor_idle() {
  digitalWrite(ENABLE_PIN,    HIGH); // HIGH = driver disabled
  digitalWrite(ONOFF_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(unsigned int numsteps) { 
  Serial.print("moving to pos ");
  Serial.print(new_pos);  
  Serial.print(" pulses ");
  Serial.print(numsteps);
  Serial.print(" dir ");
  Serial.println(dir);
  motor_enable();
  while(numsteps > 0) {
    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);
      numsteps--;
    }
  }
  motor_idle();
  Serial.print("pos ");
  Serial.print(new_pos);
  Serial.println(" reached");  
}

void setup() {
  pinMode(ZERO_PIN,      INPUT_PULLUP);
  pinMode(ZERO_FOUND_PIN,INPUT_PULLUP);
  pinMode(POS_1_PIN,     INPUT_PULLUP);
  pinMode(POS_2_PIN,     INPUT_PULLUP);
  pinMode(POS_3_PIN,     INPUT_PULLUP);
  pinMode(M1_PIN,        OUTPUT);
  pinMode(M2_PIN,        OUTPUT);
  pinMode(DIR_PIN,       OUTPUT);
  pinMode(STEP_PIN,      OUTPUT);
  pinMode(ENABLE_PIN,    OUTPUT);
  pinMode(ONOFF_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);
  motor_idle();
  current_pos = EEPROM.read(0);
  if(current_pos > NUM_POSITIONS) {
    current_pos = 1;
    EEPROM.write(0,1);
  }
  new_pos = current_pos;
  Serial.begin(9600);
  Serial.println();
  Serial.print("Current memory position = ");
  Serial.println(current_pos);
  Serial.println();
  Serial.println("If memory position is NOT according");
  Serial.println("to reality, then find zero first!");
  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;
    EEPROM.write(0, current_pos);
  }
}

The video shows the test … the motor runs smoothly.

The next video is about a fun little device … distance measurement based on high frequency (inaudible) audio. It is surprisingly accurate.

— 0 —

22 thoughts on “Fun with Arduino 37 Control a NEMA 17 Stepper Motor with Easy Driver

  1. Hi Rudy, Just let you know I tried the Easy Driver and it works fine not sure why DRV8825 didnt like, know time to make more positions in sketch… Regards John

    Like

      • Hi Rudy
        Thanks for these excellent tutorials. I have a number of Arduinos under my layout already but looking at your Fun with Arduino (FwA) posts I can see there will be many more.
        I will start with this one (FwA37) to operate my turntable with a NEMA 17 and an Easy Driver. Positions are selected by making position pins low. This seems a nutural to combine with your FwA29, the dcc accessory decoder. If the code is modified so that the LED pins are normally high can they be connected directly to the position pins of the FwA37 Arduino to pull them low if the grounds are connected.
        Once again, many thanks for these great tutorials.
        Don

        Like

      • Yes, it’s perfectly fine to connect an output pin of one Arduino to an input pin of another. As long as their GNDs are connected that is.

        Like

  2. Hi Rudy not sure if you can help me, extended to 8 inputs and was getting strange results using pin 0,1,2,3,4 as extra inputs, found eventually input 1 on Arduino was problem so know using 0, 2,3,4,5 got rid of LED light on 5 and know works fine, just had to change the (NUM_POSITIONS +6)

    Regards John

    Like

  3. Yes, pins 0 and 1 are also used for USB serial communication. They can be used as Input pins, but not together with USB communication at the same time … you’ll need to disconnect the wires during USB communication. Not sure as to what your question is now?

    Like

  4. Hi Rudy got your reply, I was using USB at the same time for upload and 5 volts so thats prob the reason was getting 65000 steps and know works fine using 0,2,3,4,5 .. but, I know using pin 10 and 11 for LED Direction Red DIR 1 green DIR 0 , and added to sketch to turn on and off for both directions …

    John

    Like

  5. Hi Rudy am I explaining in riddles sorry I will try again, when I increased the position pins from 3 to 8 using Arduino pins additional to your original 3 you used 9,8,12 I used 0,1,2,3,4 and 9,8 and 12, I was getting very weard motion, I was powering Uno from Laptop and uploading sketch via USB and Easy Driver was powered by 12 volt PSU, then tried individual position 0 then 0 and 1 on Uno then after using 1 that went wrong so used 0 and 2 digital pins on Uno that worked after increasing the Num_Position number, eventually got all 8 working using Digital pins 0,2,3,4,5,9,8,12 as 4 was already used I changed useage and eventually used pins 10 and 11 for DIR_LED_PIN1 and DIR_LED_PIN2 ( I added an extra LED) added this into sketch so I have a Red and Green LED for DIR 0 and DIR 1 I added this in sketch after dir = 1; in void loop part of sketch to go High then after the rotation etc to go LOW and again after the dir = 0; in the else below … Hope that is a bit clearer…
    All seems to work fine I know need to put all on better PCB and get the rotating gantry assembled … many thanks for the hard work you have done on this project…

    Regards John

    Like

  6. Hy Rudy,
    is it possible to put the zero find routine in the setup so it runs every time you start up the arduino?

    Regards Rieks.

    Like

    • Of course it is. I would still use a push button with it, so you decide when it is safe to start the zero routine. And once you have a push button you can also place it outside setup, then you can find zero when ever you want / need.

      Like

  7. This is the program I’ve been looking for my train turntable! My problem is that I’m very Arduino illiterate. I can follow your code somewhat. My current problem is wiring up all the connections. Especially the switches. Do you have a diagram?

    Thanks!
    Rod

    Like

    • There’s no diagram. The connections are all mentioned at the top of the code in the define statements. Any switch connected to an input needs to switch to GND. Any LED connected to an output needs a series resistor.

      Like

      • Thanks for your work. I got it going. I think the hang up was that I was using a Hall sensor and needed to make it an INPUT vs INPUT_PULLUP. Works great! Thanks again!

        Like

  8. Hi Rudy. A great series for using a stepper motor for a turntable. I’ve been able to modify this sketch to go to 10 positions by repurposing the LED and microstepping pins

    Like

  9. Hi Rudy

    I am back at this after a few months away and I have my turntable working flawlessly to seven positions. It is homemade on a 250mm lazy suzan bearing. The NEMA 17 motor has a 12mm toothed 3d printer pulley and a toothed belt drives a 240mm plywood wheel under the turntable. This arrangement is amazingly accurate.
    I would like to make it more realistic by having the motor slow down as it approaches each position, just as a real one does. What would be the best way to go about doing this?
    Thanks for doing these videos. They are a wonderful way to learn about this stuff.

    Don

    Like

  10. I’m now finding that when the stepper motor changes direction, the motor “stalls” for a second, then starts to move. However due to the stall, the turntable does not go fully to the correct position. Any thoughts on why this “stall” is occurring? If the direction does not change, the motor turns as intended and goes to the correct position.

    Thanks as always!

    Like

    • I really couldn’t say and currently I don’t have a setup available to try things out. Maybe you can avoid the position error by adding a 1.5 second delay before the motor starts to move, at the right spot in the code, as a test?

      Like

Leave a comment