Driving stepper motor with Arduino and A4988

Hello everyone,

I just want to warn you that I am still an Arduino newbie, so please don’t be too tough with me :slight_smile: !

This message is an update of the project from this thread : https://forum.arduino.cc/index.php?topic=500786.0

I made quite a bit of progress on the project. The circuit now looks something like this :

I am now able to control the motor speed and time of running from an interactive menu, displayed on the Nokia 5110 LCD screen (https://www.gotronic.fr/art-module-graphique-84x48-points-lcd10168-21944.htm). Everything in the menu is controlled via a rotary encoder (https://fr.rs-online.com/web/p/encodeurs-rotatifs-mecaniques/6234237/), presented right below on the scheme. The motor (Stepper Motor: Bipolar, 200 Steps/Rev, 35×36mm, 2.7V, 1 A/Phase) is controlled with a A4988 from pololu (Pololu - A4988 Stepper Motor Driver Carrier) and powered with a 24V, 1A power supply (http://fr.rs-online.com/web/p/alimentations-enfichables/7066524/).

I am really happy with how the small project works except from one small detail.

I am currently running the motor using this kind of code :

////////////////////////////////////////////////////////////////
/*Importing Libraries*/

#include <ClickEncoder.h>

////////////////////////////////////////////////////////////////
/*Setting variables*/

const int stepPin = 9; 
const int dirPin = 8;
boolean middle = false;

ClickEncoder *encoder;
int16_t last, value;

////////////////////////////////////////////////////////////////
/*Setup*/

void setup() {
  
  pinMode(stepPin,OUTPUT);
  pinMode(dirPin,OUTPUT);
  
  encoder = new ClickEncoder(A1, A0, A2); 
}

////////////////////////////////////////////////////////////////
/*Loop*/

void loop() {

   ClickEncoder::Button b = encoder->getButton(); /*Checks if Encoder central button is pressed*/
   if (b != ClickEncoder::Open) {
   switch (b) {
      case ClickEncoder::Clicked:
         middle=true;
        break;
    }
  }   

if (middle) //Middle Button is Pressed
  {
    middle = false;
    digitalWrite(dirPin,LOW); // Enables the motor to move in a particular direction

    // Makes 200 pulses for making one full cycle rotation
    for(int x = 0; x < 200 ; x++) 
       {
       digitalWrite(stepPin,HIGH); 
       delayMicroseconds(2000);
       digitalWrite(stepPin,LOW); 
       delayMicroseconds(2000);
       }
  }

Obviously the code presented here is just a tiny piece of the real full code, but anyway. I just wanted to know if there is an actual way to add a piece of code that would stop the motor from running when I press the central button of the rotary encoder while the motor is running?

Any ideas? I saw some threads mentioning the millis() function as of way of doing this but couldn’t really figure out how to implement this functionality? The problem is that the code for the whole project is about 700 lines long right know so I guess mills() might not be a way to go?

Thanks in advance!

Tom

You need to change two things.

{A} don't use FOR to cause all the steps to happen because FOR blocks until it completes. Just us a counter variable and IF and allow loop() to do the repetition.

And {B} don't use delayMicroseconds() because it also blocks. Have a look at how the timing is managed using millis() and micros() in the second example in this Simple Stepper Code

...R

Hi Robin2!

Thank you for the reply!

Do you think that this piece of code will work in my case then?

////////////////////////////////////////////////////////////////
/*Importing Libraries*/

#include <ClickEncoder.h>

////////////////////////////////////////////////////////////////
/*Setting variables*/

const int stepPin = 9; 
const int dirPin = 8;
const int counter = 0;
boolean middle = false;
boolean on_off = true;

ClickEncoder *encoder;
int16_t last, value;

unsigned long curMillis;
unsigned long prevStepMillis = 0;
unsigned long millisBetweenSteps = 25; // milliseconds

////////////////////////////////////////////////////////////////
/*Setup*/

void setup() {
  
  pinMode(stepPin,OUTPUT);
  pinMode(dirPin,OUTPUT);
  
  encoder = new ClickEncoder(A1, A0, A2); 
}

////////////////////////////////////////////////////////////////
/*Loop*/

void loop() {

curMillis = millis();
actOnButtons();
stepping();

   ClickEncoder::Button b = encoder->getButton(); /*Checks if Encoder central button is pressed*/
   if (b != ClickEncoder::Open) {
   switch (b) {
      case ClickEncoder::Clicked:
         middle=true;
        break;
    }
  } 

void actOnButtons() {
 if (middle == true && on_off == true) {
 middle = false;
 on_off = false;
 digitalWrite(dirPin, LOW);
 singleStep();
 }
 else if (middle == true && on_off == false) {
 on_off = true;
 middle = false;
 break;
 }
}

void stepping() {
  if (counter < 200) {
    if (curMillis - prevStepMillis >= millisBetweenSteps) {
    prevStepMillis += millisBetweenSteps;
    digitalWrite(stepPin, HIGH);
    digitalWrite(stepPin, LOW);
    counter += 1;
    } 
  }
}

Best,

Tom

Tomtop: Do you think that this piece of code will work in my case then?

It seems to be pointed in the right direction. But the only way to know if your code will work is by trying it.

Think about the sort of things that might go wrong and include Serial.print() statements to help you identify where problems arise.

...R

Hey!

I just tried the coded that I pasted on my previous answer,

Works nicely (with couple modifications haha :slight_smile: )

But there is the problem that I have expected : The motor is running very very slowly even if the variable millisBetweenSteps is set to 0…

I guess this is because the code is very long?

So if I get it correctly, the only way to speed up the motor is to clean up the code?

Tom

Tomtop: I guess this is because the code is very long?

Post the code that includes the modifications. There is no point commenting on an out of date version.

...R

Hello Robin2,

Sorry for the delay in answering, I was a little busy these days,

Unfortunately I cannot copy/paste the code within this answer due to a length constraint,

I attached the last version of the code to this message,

https://ufile.io/y3wn9

As I mentioned in my prior post, everything works fine, I can run the motor and stop it using this function :

void stepping_inf() 
{
  if (counter < 10000 && on_off == false) {
    if (curMillis - prevStepMillis >= millisBetweenSteps) {
    prevStepMillis += millisBetweenSteps;
    digitalWrite(stepPin, HIGH);
    digitalWrite(stepPin, LOW);
    counter += 1;
    } 
  }

And I am calling the function in the void loop() only in the specific condition in which menu item 6 is selected and the push button of the rotary encoder is pressed :

boolean on_off = true;

void loop() {

if (middle) //Middle Button is Pressed
  {
    if (page == 1 && menuitem == 6) 
    {
      actOnButtons();
    }
  }

void actOnButtons() {
 if (on_off == true) {
 on_off = false;
 }
 else if (on_off == false) 
 {
  on_off = true;
 }
}
}

Unfortunately, even if unsigned long millisBetweenSteps if set to 0 in the code, the stepper is running SUPER slow…

Any thoughts about how to improve the speed? I cleaned up the code a little bit so the void loop() doesn’t have too many functions to call but still it only improved the maximum speed about 2X…

Best,

Tom

Is your stepper actually obeying the number of steps commanded? Two digitalWrite() in a row may be giving a pulse too short for the stepper driver to see properly. I would put delayMicroseconds(1) between those two lines.

Tomtop: Unfortunately I cannot copy/paste the code within this answer due to a length constraint,

Add your .ino file as an attachment. I am not going to pay $5 to download code.

...R

Oh sorry,

I thought it was free,

There you go :

Tom

Motor_Test.ino (17.2 KB)

To answer to MorganS question :

That is actually what I did before using this type of code :

int volume = 0;
float speed = 0.0;
int sp = 0;
boolean middle = false;

void loop {

     if (middle) //Middle Button is Pressed
     {
       middle = false;
       if (page == 3 && menuitem == 2)
       {
         sp = volume;

         if (sp>0)
         {
            sp = 0;
            digitalWrite(dirPin,LOW); // Enables the motor to move in a particular direction
            
                // Makes 200 pulses for making one full cycle rotation
                for(int x = 0; x < 200*3.1878*volume ; x++) {
                  digitalWrite(stepPin,HIGH); 
                  delayMicroseconds(732/speed);
                  digitalWrite(stepPin,LOW); 
                  delayMicroseconds(732/speed);
            page = 1;
           }
}

This works without any problem,

However, I want to be able to stop the motor by pressing the central button of the rotary encoder a second time. The problem is that if I use this code, the Arduino is stuck in the loop until the motor finishes turning. It doesn’t read the central button state anymore…

Best,

Tom

Reading quickly the thread, I did something recently where while the stepper was be operated it could accept commands to stop or change direction.

The trick is to call a function outside of main loop() that steps the stepper with some parameters and then say use an interrupt on the middle push button to then issue a stop condition whereby it the steeper stops on the next execution of function.

Or, you can call a function in the period of time after each step to check the push button state and then stop stepping.

Looking, again very quickly, at your code, I would separate any GUI menu functionality from actual workings, you will find it much easier to develop, scale and maintain.

This is not going to work as you think because counter is an int

counter < 200*3.1878*volume

And it is best to avoid floating point maths because it is very slow on an Arduino.

My guess is that all the display stuff is taking too long and when you test for

if (curMillis - prevStepMillis >= millisBetweenSteps) {

the interval is actually long past.

Get your program to print out the actual value for curMillis - prevStepMillis and see how it compares to millisBetweenSteps.

If my guess is correct then you need to streamline your display code. For example you probably don’t need to draw the menu in every iteration of loop(). And maybe you don’t need to update the complete menu screen in one go. The human eye is relatively slow and if you update one line at a time the human probably won’t notice.

…R

Thanks a lot for your answers!

I will try out all your advices this evening when I have access to the setup!

Will keep up updated, I'm excited to try all of this!

Thank you again, it was very helpful,

Best,

Tom