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 —

Advertisements

7 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

  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

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