Manually controlling bipolar stepper motor with Arduino and EasyDriver

Manually controlling bipolar stepper motor with Arduino and EasyDriver


Stepper motors are great for accurate positioning because they move in discrete steps – a feature that makes them very appropriate for CNC software control. But every once in a while you have an application where you need to press a button and rotate some kind of a jig at a preset angle or move something a preset distance if it’s a stepper-driven linear stage. So, I decided to modify an earlier Arduino sketch I wrote for testing the world’s smallest stepper motor to make it a bit more useful (and clean any bugs in the process). Keep reading to see what came out …
Shout outs to forum user Yellow who in this thread provided an inspiration for the code modification. I had another project in mind but was dragging my foot for a long time, and seeing that someone else can also use results of your work provides a great motivation, so thanks, Yellow!


Arduino sketch for the manual EasyDriver control of bipolar stepper motors

Also see the code in the post below. The circuit is extremely simple because most of the hard work of commutating the windings of the stepper is done by the Allegro A3967 motor controller chip, mounted on the EasyDriver board. The Arduino can be any incarnation thereof. I used Nano and I had to move the outputs away from the D0 and D1 because it was messing with uploads (I think it’s only a problem with Nano specifically) but any Arduino will do – there are only 8 digital I/Os and one analog.

Circuit diagram for the EasyDriver + Arduino manual stepper control

Circuit diagram for the EasyDriver + Arduino manual stepper control

The parts list for the project is very short:

  1. Arduino. Any type will be adequate.
  2. EasyDriver board, populated. Please check with the author, Brian Schmalz on the best source of them.
  3. Bipolar stepper motor i.e. one with 4 leads. 6- and 8-lead unipolar stepper can also be converted to bipolar by connecting the proper ends of the windings together and floating the center point – not a very difficult task but outside of the scope of this post
  4. 2 x pushbuttons for LEFT/RIGH a.k.a. UP/DOWN a.k.a CW/CCW control
  5. 3 x LED for indicators, preferably different color
  6. 3 x 510 Ohm current limiting resistors for the LEDs
  7. 2 x 10K Ohm pullup resistors for the buttons
  8. 1 x 10K Ohm potentiometer (anything between 1K and 100K is fine)
  9. Couple of lines in the Arduino code you may want to look at and adjust to your needs are highlighted in the code below.
    Note the int stepsPassedMax = 160; line (line 28).Here 160 means 20 full steps in 1/8th microstepping mode. It just happens that the micro stepper I was using earlier (not the one on the video) had 20 SPR (Steps Per Revolution) and this would have been one 360 degree rotation of the motor’s shaft. If you have a better stepper (200SPR is common), it may only be 1/10th of one rotation – check with the datahseet on the motor and adjust the stepsPassedMax accordingly or send, say, 200*8= 1600 steps and see if the motor completes a full 360 degree revolution if you don’t have a datasheet and suspect that this is a 200SPR motor.
    Another adjustment you may make is the desired RPMs or, more appropriately, angular speed since you may not even need a full rotation, hence no R in RPM:
    The smaller the stepDelay variable, the faster the motor turns. See lines 36 and 60 in the code below.

    Below is the complete code:

    /* 
    EasyDriver stepper test sketch
     
    Circuit diagram and description at http://elabz.com/manually-controlling-bipolar-stepper-motor-with-arduino-and-esaydriver/
    
     */
    
    // constants won't change. They're used here to 
    // set pin numbers:
    const byte stepUpPin = 4;     // the number of the step pin
    const byte stepDownPin = 5;     // the number of the step pin
    const byte stepOut = 2;
    const byte directionOut = 3;
    const byte ledPin =  12;      // pin # for direction indicator LED 
    const byte readyPin =  11;      //pin # for LED that comes on when all the steps have been completed
    const byte inMotionPin = 10; // pin # for the LED that light up when the stepper is supposed to be still moving
    const byte ratePin = 0; // A0 - analog 0 pin on Arduino to control the stepping dealy (i.e. RPMs)
    const byte enablePin = 6; // turn EasyDriver off when not turning (saves power but ONLY use when there's no need to hold position)
    
    // Variables will change:
    byte ledState = LOW;         // the current state of the output pin
    byte lastStepUpButtonState = HIGH;   // the previous reading from the step UP pin
    byte lastStepDownButtonState = HIGH;   // the previous reading from the step DOWN pin
    byte directionState=HIGH;             // the current direction
    byte stepUpState=HIGH;             // the current state of UP button 
    byte stepDownState=HIGH;             // the current state of DOWN button
    int stepsPassed = 0; //how many steps?
    int stepsPassedMax = 160; // trying to calculate steps per revolution (remember the microstepping settings 1/8th is default)
    // most small and micro steppers, especially those that came from a CD- or DVD-R/RW drives 
    // have 20 steps per revolution. So 160 mircosteps should make the motor spin 360 degrees once.
    
    long lastStepUpDebounceTime = 0;  // the last time the output pin was toggled
    long lastStepDownDebounceTime = 0;  // the last time the output pin was toggled
    long debounceDelay = 50;    // the debounce time in ms; increase if the output flickers
    
    long stepDelayBase = 1; // 1ms base rate. Multiply by the reading from the RATE potentiometer for actual step delay
    long stepDelay; // 
    long lastStepTime = 0; // last time the step signal was sent
    
    void setup() {
      pinMode(stepUpPin, INPUT);
      pinMode(stepDownPin, INPUT);
      pinMode(ratePin, INPUT); 
      pinMode(stepOut, OUTPUT);  
      pinMode(directionOut, OUTPUT);
      pinMode(ledPin, OUTPUT);
      pinMode(readyPin, OUTPUT);
      pinMode(inMotionPin, OUTPUT);
      pinMode(enablePin, OUTPUT);
      digitalWrite(readyPin, HIGH); // turn OFF all LEDs in the beginning
      digitalWrite(inMotionPin, HIGH);  // turn OFF all LEDs in the beginning
      digitalWrite(ledPin, HIGH); // turn OFF all LEDs in the beginning
    }
    
    void loop() {
    
    // read the state of the switch into a local variable:
    int readingStepUp = digitalRead(stepUpPin);
    int readingStepDown = digitalRead(stepDownPin);
    stepDelay = analogRead(ratePin) * stepDelayBase/50;
    if(stepDelay < 1) stepDelay = 1; // reality check - a pot can read 0 and then it would mean infinite RMP - not possible
    
    if(readingStepUp == LOW || readingStepDown == LOW) { // only read buttons if either one of them is LOW
    
      // If the switch changed, due to noise or pressing:
      if (readingStepUp != lastStepUpButtonState) {
        // reset the debouncing timer
        lastStepUpDebounceTime = millis();
        lastStepUpButtonState = readingStepUp;
      } 
      
      if ((millis() - lastStepUpDebounceTime) > debounceDelay) {
        // whatever the reading is at, it's been there for longer
        // than the debounce delay, so take it as the actual current state:
        lastStepUpButtonState = readingStepUp;
        lastStepUpDebounceTime = millis();
        stepUpState = readingStepUp;
      }
    
    
    
      // If the switch changed, due to noise or pressing:
      if (readingStepDown != lastStepDownButtonState) {
        // reset the debouncing timer
        lastStepDownDebounceTime = millis();
        lastStepDownButtonState = readingStepDown;
      } 
      
      if ((millis() - lastStepDownDebounceTime) > debounceDelay) {
        // whatever the reading is at, it's been there for longer
        // than the debounce delay, so take it as the actual current state:
        lastStepDownButtonState = readingStepDown;
        lastStepDownDebounceTime = millis();    
        stepDownState = readingStepDown;
    
      }
    
      
    }else{
     stepUpState = HIGH;
     stepDownState = HIGH;
     
    } // end of if block that reads button states
      
    if(stepsPassed == 0 ) { // if the previous command has completed, make the direction decision 
        if(stepUpState == LOW || stepDownState == LOW) { 
          if(stepUpState == LOW && stepDownState == LOW) { directionState = LOW; } // why are you holding both UP and DOWN buttons?
          if(stepUpState == LOW && stepDownState == HIGH) { directionState = HIGH;} // go up
          if(stepUpState == HIGH && stepDownState == LOW) { directionState = LOW;} // go down
          stepsPassed = stepsPassedMax+1;
      }
     } 
      
    
    if(stepsPassed > 0 ) // send step signals now
    {
      digitalWrite(enablePin, LOW); // wake up
      digitalWrite(ledPin, directionState); // set proper direction
      digitalWrite(directionOut, directionState); 
      if((millis() - lastStepTime) > stepDelay) // delay expired, send another step
        {
            digitalWrite(stepOut, HIGH);
            stepsPassed--;
            lastStepTime = millis();
        }else
        {
          // time for one step not yet expired, hold the STEP signal low 
          digitalWrite(stepOut, LOW);
        }
    }else{
     digitalWrite(enablePin, HIGH); // go to sleep
    
    }
    
    if(stepsPassed == 0) { 
     digitalWrite(readyPin, LOW);
     digitalWrite(inMotionPin, HIGH); 
    
     }else{
     digitalWrite(readyPin, HIGH);
     digitalWrite(inMotionPin, LOW); 
     }
    
    }
    
    

    As always, I would appreciate your comments here and your questions and requests for help in the forums, Help section. Also, the Motor Control section is great for any discussions about this project since it involves stepper motor control.

Leave a Reply

Or use the Forums ! If your comment is a question, please consider posting it to a matching section of our Electronics Forums. The forums allow for a more natural conversation flow, especially if multiple replies are required. Additionally, you'll be able to style your writing (bold font, italics etc.) and post images which can help with a good answer.

Notify me of followup comments via e-mail. You can also subscribe without commenting.

Tools

Coming soon ...

Recent Comments
  • Ezequiel França dos Santos: Thank you very Much!
  • Deives: Hello friend, first I would like to congratulate you on an excellent project. Well I am Brazilian and I have...
  • admin: Well, I did give it some thought and have been meaning to try for some time now. But, in general, the approach...
  • Adi Soffer: Hey there. Great post and thank you for sharing. I’m trying to figure out how to control a...
  • Tony: So many thanks!
  • admin: Thanks for stopping by, Tony. I’ve posted an LTSpice model of the driver some time ago, check it out:...
  • Tony: a little question: I am using Multisim to simulate your circuit and what load should I use? When I use a...
  • admin: The sensor itself is fine, I’ve used exactly the same. They are incredibly simple devices – just a...
  • Phil: Thanks you for your anwser. I think I have understood the usage you make of interupts. But after tring several...
  • admin: I think the interrupt implementation is the same in ATtiny85 (although I have to admit I haven’t checked...
Meta