Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Arduino

An Arduino Library to Control the 28BYJ-48 Stepper Motor

4.50/5 (15 votes)
20 Dec 2013CPOL7 min read 96K   4.7K  
This article shows how to create a simple C++ class library to properly control a 28BYJ-48 stepper motor using its control board.

Introduction

I have recently discovered the Arduino board system. Although it has been out for quite some time I didn't know about this very interesting system. The Raspberry Pi appears to be much more popular, but the Arduino really deserves to be known.

Its specification are way less impressive than the Raspberry PI, but its purpose is totally different. Those who studied electronics like I did more than 25 years ago should find it very familiar and extremely interesting. The Arduino system is a micro-controller prototyping board that can be extended with the only limit of your imagination.

For $38 you can buy online a starter kit that contains many interesting components and solder-less prototyping boards to build many experiments. When I was a student I learned to program a Motorola CPU with I/O circuits on board called Motorola Kit D5. This CPU board the size of a modern laptop was powered by a 6802 with few Kbytes of RAM and primitive operating system that would let you enter assembly instruction in hexadecimal with small 25 keys keyboard using a 6 digits LED display.

Some specs can be found there: http://www.old-computers.com/museum/computer.asp?c=504&st=1

The Arduino is a modern version of this Kit D5 and includes a lot of interesting on-board features:

  • 12 digital configurable I/O Pin, 6 of them configurable as PWM output
  • 6 10 bits ADC input I/O that can be configured as digital output as well
  • 2 in-board timers
  • 32K of NVRAM
  • 1K of SRAM
  • 2K of EEPROM
  • RISC instruction set

Of course it doesn't look that impressive if you compare it with the Raspberry PI but the purpose of such a board is not the same as PI. The main purpose of that board is to interface with the outside world using the I/O pins.

The board can be programmed with a simple IDE connected via a USB port to a PC. Once the program installed in the board memory it runs as an infinite loop. Libraries can be written in C++ and used to build more complex applications.

Controlling a Stepper Motor

In this article I'll show you how to write a simple class to control a stepper motor, the one provided with the starter kit I bought, its reference is 28BYJ-48. The Arduino comes with an installed Stepper class in its libraries but this class doesn't work properly with this motor. There are two problems with this class when it drives the 28BYJ-48:

  • It takes 2048 steps to do a 360deg revolution while the specs of the 28BYJ-48 states that it would take 4096 steps
  • When sending a negative value to the step() method the motor continues to turn in the same direction!

It took me quite some time to understand what was wrong and find the correct specs for the 28BYJ-48 and its control board, then I could understand why it was not working properly. You can find the correct specs there: Geetec Stepper Motor

The trick is the sequence that has to be followed when writing the input commands to the pins of the board. The Stepper class of the Arduino library follows a sequence of 4 values of 4 bits but the 28BYJ-48 requires a sequence of 8 values of 4 bits. One step of the motor correspond to 1 value of the sequence. Those 8 values must be repeated in this order to turn the motor in one direction and in the reverse order to turn the motor in the other direction.

This is what the code of the StepperMotor class does. This class is intended to work with this motor, it must be modified to work with a different motor that would have a different control sequence.

The sequence of quartets to send to the 28BYJ-48 is the following:

In 1 In 2 In 3 In 4
0 0 0 1
0 0 1 1
0 0 1 0
0 1 1 0
0 1 0 0
1 1 0 0
1 0 0 0
1 0 0 1

This sequence will have the motor turn in counter clockwise direction, if you run this sequence in reverse order the motor will turn in clockwise direction.

Another parameter is the speed of rotation. It is not clear from the specs what the capability of the motor is and its board, but after a few experiments I found that operated fine with a 1 ms period. This means that you can change the state of the input pins every ms. Above that frequency the board won't respond properly and the motor won't move.

class StepperMotor 
{
  public:
    /**
     * Constructor assigning the motor control PIN
     */
    StepperMotor(byte motorPin1, byte motorPin2, byte motorPin3, byte motorPin4); 
 
    /**
     * Sets the period to send the command quartet
     *
     * @param period Period in ms between to control quartets
     */
    void setPeriod(int period);
 
    /**
     * Moves the motor of a given number of steps.
     * Positive in a direction, Negative in the opposite
     * 
     * @param numberOfSteps Number of steps to move
     */
    void move(int numberOfSteps);
    
    /**
     * Stops the motor, setting all bits to LOW and raising the 
     * interrupt flag when interrupt is set to true
     *
     * @param interrupt If true it will stop the current sequence. When set to true
     * this method is meant to be called from an interruption serving
     * routine (ISR). When false it doesn't raise the interrupt flag, just
     * set all the PIN to LOW. If not called from an interrupt routine, it 
     * prevents the motor from running. 
     */
    void stop(bool interrupt = false);

    /**
     * Reset the interrupt flag to false so the motor
     * can be moved again
     */
    void reset();
}

This simple class provides two main methods setPeriod and move. The method setPeriod is used to set the time in milliseconds between two pulses sent to the motor to make in move in one or the other direction. The method move is used to move the motor of a given number of steps in a direction given by the sign of the parameter.

The methods stop and reset can be used to stop and restart the motor from an interruption serving routine. The stop method has a parameter to enable its use from a ISR, when true the method will stop the motor asynchronously using an internal flag.

The code of the StepperMotor is an Arduino library and need to be installed as .zip file from the Arduino IDE.

The following is an extract of the code of the class StepperMotor that shows how it works.

void StepperMotor::stop(bool stop)
{
  if (stop)
  {
    interrupt = true;
  }
  for (byte idx = 0; idx < PHASE_NUMBER; ++idx)
  {
    digitalWrite(motorPins[idx], LOW);
  }
}

void StepperMotor::moveOneStep(byte seqIdx)
{
  for (byte idx = 0; idx < PHASE_NUMBER; ++idx)
  {
    digitalWrite(motorPins[idx], stepSequence[seqIdx][idx]);
  }
 
  delay(period);
}

bool StepperMotor::moveOneStepForward()
{
  if (!interrupt)
  {
    stepIdx = incrementStep(stepIdx);
    moveOneStep(stepIdx);
  }
    
  return interrupt;
}

bool StepperMotor::moveStepsForward(int steps)
{
  for(int n = 0; n < steps; n++)
  {
    if (moveOneStepForward())
    {
      break;
    }
  }

  return interrupt;
}

The behavior of the stop method is very simple, if called while the method moveStepsForward is running it interrupts it as moveOneStepForward returns true and breaks the loop, stopping the motor movement.

In the following examples the connections are the following: pins 1, 2, 3, 4 are connected to the pins 19, 18, 17 and 16 of the Arduino board. The button is connected to the pin 2 of the Arduino which is set to INPUT_PULLUP so there is no need for a resistor.

Demo application: Reversing the rotation every time a button is pressed

The first demo application is using a button to start the rotation of the motor for a given number of steps, then a second click on the button starts the motor in the opposite direction and so on.

#include <StepperMotor.h>

// 4 pins of the stepper motor board
#define _PIN1 19
#define _PIN2 18
#define _PIN3 17
#define _PIN4 16

// Interruption on PIN2, push-button connected to pull-up
#define ITR_PIN 2
#define ITR_NB 0

volatile boolean forward = false;
volatile boolean start = false;
volatile boolean first = true;

StepperMotor stepper(_PIN1, _PIN2, _PIN3, _PIN4);

/**
 * This method is called on the interruption raised on the falling front of the PIN2
 * The start flag is used to avoid rebound front. Interruptions are disabled inside the 
 * interrupt vector method.
 * start is reset once it has been processed in the main loop() 
 */
void buttonPressed()
{
  if (!first)
  {
    if (!start)
    {
      forward = !forward;
      start = true;
    }
  }
  else
  {
    first = false;
  }
}

void setup()
{
  cli();
  stepper.setPeriod(5);
  pinMode(ITR_PIN, INPUT_PULLUP);
  attachInterrupt(0, buttonPressed, FALL);
  sei();
}

void loop()
{
  if (start)
  {
    if (forward)
    {
      stepper.move(2048);
      stepper.stop();
    }
    else
    {
      stepper.move(-2048);
      stepper.stop();
    }
    start = false;
  }
}

The ISR method is called when the button is pressed on the falling edge of the pulse. However it appears that when started the program always detects an interruption even if the button is not pressed. In order to avoid processing this interruption the flag first is used to discard this interruption. There is another issue with catching interruptions on a button pressed and it's the bouncing of the contact itself. In this simple example the flag start is used to avoid any bouncing pulse from the button.

Demo application: Stopping or starting the motor on a button pressed interruption

The second example is demonstrating the ability to stop the motor while the method move is running. Every time the button is pressed it stops or starts the motor depending on its current state. The interruption routine is suffering some time of the bouncing of the button which causes one press of the button to generate several falling edges that are caught by the interruption line of the chip.

I couldn't find a way to control that by software but a simple capacitor and some resistor would do the trick to avoid them. Because of that the current code may some times have an erratic behavior when the button is pressed.

#include <StepperMotor.h>

#define DELAY 2000

#define _PIN1 19
#define _PIN2 18
#define _PIN3 17
#define _PIN4 16

#define ITR_PIN 2
#define ITR_NB 0

volatile bool first = true;
volatile bool moveInterrupted = false;

StepperMotor stepper(_PIN1, _PIN2, _PIN3, _PIN4);

void buttonPressed()
{
  static volatile boolean toggle = false;

  if (!first)
  {
    if (toggle)
    {
      stepper.reset();
    }
    else
    {
      stepper.stop(true);
    }
 
    toggle = !toggle;
  }
  else
  {
    first = false;
  }
}

void setup()
{
  cli();
  stepper.setPeriod(10);
  pinMode(ITR_PIN, INPUT_PULLUP);
  attachInterrupt(0, buttonPressed, FALLING);
  sei();
}

void loop()
{
  moveInterrupted = stepper.move(2048);
}

Conclusion and Point of Interest

Those little experiments with the Arduino took me back 25 years ago a bit like a time machine! There is plenty of code and electronic circuits out there that use the Arduino boards, and have barely scratched the surface of this amazing little circuit. I have always preferred to use a computer to interact with the external world, unfortunately my job has been more on the software side although I have never forgotten what I originally studied, CPU boards connected to electronic components. I hope this simple article will plant the seed of learning more on how to interface a computer with the real world!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)