Trouble with the Stepper motor utility

I was having trouble getting a stepper motor to function using the example in the Web IDE using the <stepper.h> library. I was using a ROHS 28BYJ-48 motor and a ULN2003 driver board and an external 5v power supply to drive the board. Spent quite a lot of time on it to no avail. So I pulled all the spec sheets for the motor and driver board (btw there's a lot of mis-documentation out there on these components) and created my own sketch for stepper motor control. I thought others might be having the same trouble and would benefit from my sketch. I've attached it here.
Step_motor_self_Continuos.ino (12.9 KB)

/*
  Tom Gee Feb 22, 2022
  This Sketch allows coninuous control of a 4 phase (unipolar) stepper motor (Rohs 28BYJ-48).
  The Rohs 28BYJ-48 has a gear reduction ratio of 64:1 and 32 steps (64 half steps) per stepper rotor revolution
  so steps per motor output shaft rev = 32 x 64 = 2048
  Sending a serial command of '+' will increase the speed of the motor by 1 rpm
  Sending a serial command of '-' will decrease the speed of the motro by 1 rpm
  Sending a serail command of 'f' will cause the motor to rotate in the CW direction
  Sending a serail command of 'r' will cause the motor to rotate in the CWW direction
  Sending a serail command of 's' will cause the motor to stop
  Sending any serail command other than the ones above will start the motor again
  Serial window must be set to NO LINE ENDING for this to work!
  Commands will be responded to within 1 stator revolution.
  Max RPM is 18, beyond that the stator will not respond and the motor will stall
  A speed increase button will ground D3. Pressing this button will increase the speed of the motor by 1 RPM. Holding this button will increase the speed at a rate of 2 RPM/sec
  A speed decrease button will ground D4. Pressing this button will decrease the speed of the motor by 1 RPM. Holding this button will decrease the speed at a rate of 2 RPM/sec
  A Reverse button will ground D5. Pressing this will cause the motor to reverse direction. It only acts on rising (actually falling as they're pulled to ground) edges
  A Stop button will ground D6. Pressing this will cause the motor to stop. It only acts on rising (actually falling as they're pulled to ground) edges
  Pressing any button while the motor is stopped will resume it in the direction and speed last assigned

*/
const int ph1 = 8; //These are the inputs for the 4 phases of the stepper motor
const int ph2 = 9;
const int ph3 = 10;
const int ph4 = 11;
const int stepsPerRev = 2048; //Number of steps for one revolution of the motor output shaft
const int revLmt = 640; // This can be modified to stop the motor are this many output shaft revolutions
const int spdButtonUp = 3; //Speed up button. Grounding this input will increase the motor speed by RPMstep (1) RPM. Note Clipped at 18 RPM
const int spdButtonDn = 4; //Speed down button. Grounding this input will decrease the motor speed by RPMstep (1) RPM. Note Clipped at 1 RPM
const int revButton = 5; //Reverse Button. Grounding this input will cause the motor to reverse directions
const int stopButton = 6; //Stop Button. Grounding this input will cause the motor to stop or start depending on the previous state.
int timeBTWsteps; //Time between steps in MicroSeconds. Calcuated based on RPM
int RPM = 17; //Motor output shaft desired speed
int revs = 0; // Number of output shaft revolutions since last change in direction or stopped state
int incByte = 0; //Register to hold the incoming byte from the serial port
int RPMstep = 1; //Size of the RPM step during incrementing or decrementing motor speed
bool CCW = false; //Motor direction. False = CW. True = CCW. Intialized to CW.
bool CCWlst = false; //Motor direction during last software loop
bool stop = false; //Stop cammand. False = stopped. True = Run. Intialized to Run
bool spdTmrlth = false; //used to latch Speed timer. False = unlatched. True = Latched. Intialized to unlatched
bool stpBut = false; //state of the stop button
bool stpButLst = false; // state of the stop button on last software loop. Used to only act on rising edge of button press
bool revBut = false; // state of the Reverse button
bool revButLst = false; //state of the reverse button on last loop. Used to only act on rising edge of button press
int steps = 0; //Number of steps made by the motor. Resets on a full revolution
unsigned long spdTmr = 0; //timer parameters used during a speed button press to allow incrementing/decementing on a button hold
unsigned long spdTmrNew = 0;
unsigned long spdTmrOld = 0;


void setup() {
  Serial.begin(9600);
  pinMode(ph1, OUTPUT);
  digitalWrite(ph1, HIGH);
  pinMode(ph2, OUTPUT);
  digitalWrite(ph2, HIGH);
  pinMode(ph3, OUTPUT);
  digitalWrite(ph3, HIGH);
  pinMode(ph4, OUTPUT);
  digitalWrite(ph4, HIGH);
  timeBTWsteps = 60000000 / RPM / 2 / stepsPerRev;
  pinMode(spdButtonUp, INPUT_PULLUP);
  pinMode(spdButtonDn, INPUT_PULLUP);
  pinMode(revButton, INPUT_PULLUP);
  pinMode(stopButton, INPUT_PULLUP);
}

void loop() {
  if (Serial.available() > 0) { //controls motor thru the serial port
    incByte = Serial.read();
    if (incByte == '+') {
      RPM = RPM + RPMstep;
      timeBTWsteps = 60000000 / RPM / 2 / stepsPerRev;
      incByte = 0;
      stop = false;
      Serial.print("RPM = ");
      Serial.println(RPM);
      Serial.print("Time btw steps = ");
      Serial.println(timeBTWsteps);
    }
    else if (incByte == '-') {
      RPM = RPM - RPMstep;
      timeBTWsteps = 60000000 / RPM / 2 / stepsPerRev;
      incByte = 0;
      stop = false;
      Serial.print("RPM = ");
      Serial.println(RPM);
      Serial.print("Time btw steps = ");
      Serial.println(timeBTWsteps);
    }
    else if (incByte == 'r') {
      CCW = true;
      incByte = 0;
      stop = false;
    }
    else if (incByte == 'f') {
      CCW = false;
      incByte = 0;
      stop = false;
    }
    else if (incByte == 's') {
      incByte = 0;
      stop = true;
    }
    else {
      stop = false;
      steps = 0;
      revs = 0;
    }
  }

  if (digitalRead(spdButtonUp) == LOW) {    //controls motor thru button presses
    spdTmrNew = millis();                     //start timer
    if ((spdTmrNew - spdTmrOld) > 1000) {   //first time thru set TmrOld to valid value
      spdTmrOld = spdTmrNew;
    }
    spdTmr = spdTmr + (spdTmrNew - spdTmrOld); //Increment timer will button pressed
    spdTmrOld = spdTmrNew;

    if (spdTmr < 100 && spdTmrlth == false) { //increment spd on a single push, but only on rising edge
      spdTmrlth = true;
      if (RPM <= 17) {                        //increment RPM if less than 18
        RPM = RPM + RPMstep;
        timeBTWsteps = 60000000 / RPM / 2 / stepsPerRev; //recalculate time between steps based on ne RPM
        stop = false;
        Serial.print("RPM = ");   //acknowlegde RPM change and report time between steps
        Serial.println(RPM);
        Serial.print("Time btw steps = ");
        Serial.println(timeBTWsteps);
      }
      else {
        RPM = 18;     //Limit RPM to a max of 18
        Serial.println("RPM at upper limit 18 RPM"); //Acknowledge limit reached

      }
    }
    else if (spdTmr > 500) {  //increment RPM by RPM step every 0.5 sec if button is held
      spdTmr = 0;
      spdTmrOld = 0;
      if (RPM <= 17) {
        RPM = RPM + RPMstep;
        timeBTWsteps = 60000000 / RPM / 2 / stepsPerRev;
        stop = false;
        Serial.print("RPM = ");
        Serial.println(RPM);
        Serial.print("Time btw steps = ");
        Serial.println(timeBTWsteps);
      }
      else {
        RPM = 18;    //Limit RPM to a Max of 18
        Serial.println("RPM at upper limit 18 RPM");

      }
    }
  }
  else if (digitalRead(spdButtonDn) == LOW) { //if Up Button not pressed, check down button
    spdTmrNew = millis();
    if ((spdTmrNew - spdTmrOld) > 1000) {
      spdTmrOld = spdTmrNew;
    }
    spdTmr = spdTmr + (spdTmrNew - spdTmrOld);
    spdTmrOld = spdTmrNew;

    if (spdTmr < 100 && spdTmrlth == false) { //decrement RPM on short button press. Only acts on rising edge
      spdTmrlth = true;
      if (RPM > 1) {
        RPM = RPM - RPMstep;    //decrement RPM
        timeBTWsteps = 60000000 / RPM / 2 / stepsPerRev;
        stop = false;
        Serial.print("RPM = ");
        Serial.println(RPM);
        Serial.print("Time btw steps = ");
        Serial.println(timeBTWsteps);
      }
      else {
        RPM = 1;  //limit Min RPM to 1
        Serial.println("RPM at lower limit 1 RPM");
      }
    }
    else if (spdTmr > 500) { //decrement RPM by RPMstep every 0.5 sec if button held.
      spdTmr = 0;
      spdTmrOld = 0;
      if (RPM > 1) {
        RPM = RPM - RPMstep;
        timeBTWsteps = 60000000 / RPM / 2 / stepsPerRev;
        stop = false;
        Serial.print("RPM = ");
        Serial.println(RPM);
        Serial.print("Time btw steps = ");
        Serial.println(timeBTWsteps);
      }
      else {
        RPM = 1;  //limit Min RPM to 1
        Serial.println("RPM at lower limit 1 RPM");
      }
    }
  }
  else {  //Hold timers in reset when button not pressed
    spdTmr = 0;
    spdTmrNew = 0;
    spdTmrOld = 0;
    spdTmrlth = false;
  }

  if (digitalRead(revButton) == LOW && revButLst == false) { //Reverse motor direction when button pressed. Only acts on rising edge
    revBut = true;
    CCW = !CCW;
    stop = false;
    Serial.println("Reverse button pressed");
  }
  else if (digitalRead(revButton) == HIGH) {
    revBut = false;
  }
  revButLst = revBut;

  if (digitalRead(stopButton) == LOW && stpButLst == false) { //Stop motor or start motor on button press. Only acts on rising edge
    stpBut = true;
    stop = !stop;
    Serial.println("Stop button pressed");
  }
  else if (digitalRead(stopButton) == HIGH) {
    stpBut = false;
  }
  stpButLst = stpBut;


  if (CCW == false && stop == false) { //Execute a complete set of 8 half steps in the forward direction if CCW = false and Stop not commanded
    if (CCWlst == true) { //reset steps and revs on a motor direction change
      steps = 0;
      revs = 0;
    }
    digitalWrite(ph1, HIGH);
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, LOW);
    delayMicroseconds(timeBTWsteps);
    digitalWrite(ph1, HIGH);
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, LOW);
    digitalWrite(ph4, LOW);
    delayMicroseconds(timeBTWsteps);
    steps = steps + 1;
    digitalWrite(ph1, HIGH);
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, LOW);
    digitalWrite(ph4, HIGH);
    delayMicroseconds(timeBTWsteps);
    digitalWrite(ph1, HIGH);
    digitalWrite(ph2, LOW);
    digitalWrite(ph3, LOW);
    digitalWrite(ph4, HIGH);
    delayMicroseconds(timeBTWsteps);
    steps = steps + 1;
    digitalWrite(ph1, HIGH);
    digitalWrite(ph2, LOW);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, HIGH);
    delayMicroseconds(timeBTWsteps);
    digitalWrite(ph1, LOW);
    digitalWrite(ph2, LOW);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, HIGH);
    delayMicroseconds(timeBTWsteps);
    steps = steps + 1;
    digitalWrite(ph1, LOW);
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, HIGH);
    delayMicroseconds(timeBTWsteps);
    digitalWrite(ph1, LOW);
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, LOW);
    delayMicroseconds(timeBTWsteps);
    steps = steps + 1;
  }
  if (CCW == true && stop == false) { //Execute a full set of 8 half steps in the reverse direction if CCW = true and Stop not commanded
    if (CCWlst == false) {
      steps = 0;
      revs = 0;
    }
    digitalWrite(ph1, LOW);
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, LOW);
    delayMicroseconds(timeBTWsteps);
    digitalWrite(ph1, LOW);
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, HIGH);
    delayMicroseconds(timeBTWsteps);
    steps = steps + 1;
    digitalWrite(ph1, LOW);
    digitalWrite(ph2, LOW);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, HIGH);
    delayMicroseconds(timeBTWsteps);
    digitalWrite(ph1, HIGH);
    digitalWrite(ph2, LOW);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, HIGH);
    delayMicroseconds(timeBTWsteps);
    steps = steps + 1;
    digitalWrite(ph1, HIGH);
    digitalWrite(ph2, LOW);
    digitalWrite(ph3, LOW);
    digitalWrite(ph4, HIGH);
    delayMicroseconds(timeBTWsteps);
    digitalWrite(ph1, HIGH);
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, LOW);
    digitalWrite(ph4, HIGH);
    delayMicroseconds(timeBTWsteps);
    steps = steps + 1;
    digitalWrite(ph1, HIGH);
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, LOW);
    digitalWrite(ph4, LOW);
    delayMicroseconds(timeBTWsteps);
    digitalWrite(ph1, HIGH);
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, LOW);
    delayMicroseconds(timeBTWsteps);
    steps = steps + 1;
  }

  if (revs == revLmt) { //Stop the motor when revLmt is reached
    stop = true;
  }

  if (stop == true) { //Reset steps when motor is stopped
    steps = 0;
    digitalWrite(ph1, HIGH);  //set all phases high to prevent over heating a coil
    digitalWrite(ph2, HIGH);
    digitalWrite(ph3, HIGH);
    digitalWrite(ph4, HIGH);
  }

  if (steps >= stepsPerRev) { //Acknowledge when a revolution is complete and provide count
    Serial.print("steps = ");
    Serial.println(steps);
    steps = 0;
    revs = revs + 1;
    Serial.print(revs);
    Serial.println(" revs complete");
  }
  CCWlst = CCW;
}
1 Like

I think that your code would get a wider audience if it were posted correctly. Many members are reluctant to download code. Read the forum guidelines to see how to properly post code and some information on how to get the most from this forum.
Use the IDE autoformat tool (ctrl-t or Tools, Auto format) before posting code in code tags.

To atract helpers attach schematics as powering looks poor.

The Uno Board is powered thru the USB

Great! Really well done! Thanks, the UNO is powered by USB. Good.
The ULN2003 is really stone age technology. It uses, if I remember, a Darlington transistor stage and it waists a least one volt. What's the stepper? A genuine 5 volt? If so, I suggest cranking up the voltage supplying that stepper to at least 6 volt.

1 Like

Using INPUT_PULLUP for the inputs is perfect.

Looking through the code, it looks really good, not any heavy delay.

What does the trouble look like? I see no obvious reason for trouble.

The code above works fine. It's the example in the web IDE that I can't get to work. The motor just hums and jitters. I thought for a while that maybe the include <Stepper.h> library function was set up for a bipolar stepper, but I've found many examples of people using it with the ROHS 28BYJ-48 4 pole motor and the ULN2003 driver board, and claiming it worked just fine. I thought maybe my driver board or motor was bad, so I wrote my own code to test the hardware. My code works perfectly. Below is the example code I can't make work.


/*
  Stepper Motor Control - one revolution

  This program drives a unipolar or bipolar stepper motor.
  The motor is attached to digital pins 8 - 11 of the Arduino.

  The motor should revolve one revolution in one direction, then
  one revolution in the other direction.


  Created 11 Mar. 2007
  Modified 30 Nov. 2009
  by Tom Igoe

*/

#include <Stepper.h>

const int stepsPerRevolution = 200;  // change this to fit the number of steps per revolution
// for your motor

// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);

void setup() {
  // set the speed at 60 rpm:
  myStepper.setSpeed(60);
  // initialize the serial port:
  Serial.begin(9600);
}

void loop() {
  // step one revolution  in one direction:
  Serial.println("clockwise");
  myStepper.step(stepsPerRevolution);
  delay(500);

  // step one revolution in the other direction:
  Serial.println("counterclockwise");
  myStepper.step(-stepsPerRevolution);
  delay(500);
}

I changed the stepsPerRevolution to 2048, 1024, even tried 512 and 64 . It's not a big deal, but I searched the forum and didn't find any threads discussing this problem. So I did some Stepper motor research, wrote a little code and thought others might benefit from it as well, since there seems to be much misinformation on steppers motor on the web.

If you have any thoughts on why the example code doesn't work I'd be very interested in understanding the problem. I had it all wired the same except I didn't have any of the low side switches installed.

For a 28BYJ-48 ?
Wishful thinking.
Try less than 10.

Not sure if the compiler is clever enough to know that won't fit in an int (~32000 max).
Leo..

With your wiring above, you should use

Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);
1 Like

Thanks. I didn't mention it, but I tried 1, 5, 10, 15, ...etc. for Speed, but none worked. That's the other thing with the Stepper.h, has little documentation on specifics. It wasn't clear if myStepper.setSpeed() was rotor speed or output shaft speed. The 29BYJ-48 is capable of ~1000 rpm rotor speeds, but with a 64:1 reduction ratio only ~18 RPM output shaft speeds. As for the timeBTWsteps, the end calculation comes out to ~1000 and the compiler seems to handle the intermediate calculations ok.

Because the stepper.h is a library included in the IDE, you get the documentation here on the Arduino website.

MicroBahner, Thanks swapping outputs 9 & 10 solved the problem. That was not obvious in looking at the documentation on the Arduino website.

It's a problem of the motor wiring, not of the driver :wink: . With other motors, things maybe different. But I know this problem with this special stepper.

Thanks again!

"RoHS" is the Reduction of Hazardous Substances regulations, not a motor part number!!

The 28BYJ-48 is a not a 4-pole motor, its 16 pole (ie 32 fullsteps per revolution) but with an approx 64:1 reduction gear, so it behaves like a 1024 pole motor (2048 full steps per output shaft resolution).

There are some nice photos of a tear-down of the 28BYJ-48 here: https://cookierobotics.com/042/

You get 4 full steps per pole-pair per revolution, so for instance standard NEMA17 and 23 steppers are often 50 pole-pair (100 poles, 200 full steps).


ho hum.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.