Wilmer
Wilmer
. 16 min read

Drive 28BYJ-48 Stepper Motor with ULN2003 driver and the BeagleBone Black

Drive 28BYJ-48 Stepper Motor with ULN2003 driver and the BeagleBone Black

In this post, I show how to drive the stepper motor 28BYJ-48 with the driver ULN2003 which can drive a wide range of loads including solenoids, relay DC motors, LED display filament lamps and thermal printheads. In practice, this driver can be found in a popular green-coloured board that has been designed to make it easy to play and interact with the 28BYJ-48 as this and this another tutorial show.

The 28BYJ-48 is a stepper motor that moves in steps or “degree intervals”. It has an internal gear set that let it turn 2048 steps in each revolution in a mode that is called Full Step or 4096 steps in each revolution in a mode called Half Step.

These are theoretical values and depend on the gearbox used by the 28BYJ-48`s manufacturer. Sometimes, instead of 2048 steps, the real value is 2037 steps per revolution.

It is important to remember that the logic voltage for the BeagleBone is 3.3V. If the user provides a greater voltage, the BeagleBone could be damaged.

Circuit and components

The circuit can be seen in Figure 1. It consists of a UNL2003 driver module, 28BYJ48 stepper motor, and the BeagleBone.

circuit.png
Figure 1: Circuit to drive a 28BYJ48 stepper motor with the ULN2003 driver.

The components are:

  • 1 28BYJ-48 stepper motor 5V
  • 1 ULN2003 module driver
  • 1 Protoboard mini
  • Jumpers male-male to make the connections

The pins used to control the stepper motor are:

  • GPIO P8_12, P8_14, P8_16 and P8_18 to control the motor rotation speed and direction

Coding

First, four GPIO objects are declared and named IN1, IN2, IN3 and IN4.

1
2
3
4
5
// Declaring the pins for motor
GPIO IN1 (P8_12);
GPIO IN2 (P8_14);
GPIO IN3 (P8_16);
GPIO IN4 (P8_18);

These pins will be used to declare and initialize a StepperMotor object with the name of myStepper in this manner:

1
StepperMotor myStepper (IN1, IN2, IN3, IN4);

The StepperMotor object constructor definition is in a class named STEPPERMOTOR who is part of the whole library to use and program the BeagleBone Black with C++. The source files can be found here.

The StepperMotor object constructor is shown in the next listing:

1
2
3
4
5
6
7
// Overload constructor
StepperMotor( 
              GPIO, GPIO, GPIO, GPIO, 
              STEPPER_MODE controlMode = fullStep1Coil, 
              unsigned int stepsPerRevolution = 2048, 
              unsigned int maxSpeed = 500
            );

It requires seven input parameters, from which, the last three have default values avoiding the user to specify these if it does not want to.

  • The first four parameters are the GPIO pins to control the stepper motors.
  • The last three define the control mode and other movement parameters for the stepper motor:
    • The controlMode parameter defines the stepper motor’s control mode to one of the next modes:
      • fullStep1Coil: Full step with one coil activation. It is the default value
      • fullStep2Coils: Full step with two coil activation.
      • halfStep: Half step with two coil activation by nature.
      • driver: Another way, by example, using another custom driver.
    • The stepsPerRevolution defines the number of steps per revolution of the motor. It takes 2048 steps as the default value.
    • The maxSpeed defines the maximum rotation speed of the motor in steps/second. It takes 500 as the default value.

The StepperMotor object constructor declaration is shown in the next listing. It requires at least, four GPIO pins to control the stepper motor and initialize them as OUTPUT pins with the private method InitMotorPins(). Then, the correct number for the activation sequence steps is set up in the variable stepsPerMode according to the control mode that the user wants:

  • 4 steps for Full Step mode with 1 or 2 coil activation.
  • 8 steps for Half Step mode.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// Overload Constructor
StepperMotor::StepperMotor (GPIO newMotorPin1, 
                            GPIO newMotorPin2,
                            GPIO newMotorPin3,
                            GPIO newMotorPin4,
                            STEPPER_MODE newControlMode,
                            unsigned int newStepsPerRevolution,
                            unsigned int newMaxSpeed) :
                            motorPin1 (newMotorPin1),
                            motorPin2 (newMotorPin2),
                            motorPin3 (newMotorPin3),
                            motorPin4 (newMotorPin4),
                            controlMode (newControlMode),
                            stepsPerRevolution (newStepsPerRevolution),
                            maxSpeed (newMaxSpeed)
{
  InitMotorPins();
  stepsCounter = 0;
  currentStep = 0;

  std::string modeString; 
  switch (controlMode)
  {
    case fullStep1Coil:
      modeString = "Full step with 1 Coil";
      stepsPerMode = fullStep1CoilVector.size(); 
      break;
    case halfStep:
      modeString = "Half step";
      stepsPerMode = halfStepVector.size(); 
      break;
    case fullStep2Coils:
      modeString = "Full step with 2 coils"; 
      stepsPerMode = fullStep2CoilsVector.size(); 
      break;
    case driver:
      modeString = "Driver mode was chosen"; 
      break;
  }

  std::string message;
  message = "\nStepperMotor object with the next parameters / pins was created:\n" + 
            std::string("\tMotorPin1: ") + this->motorPin1.GetPinHeaderId() + 
            "\n" + 
            std::string("\tMotorPin2: ") + this->motorPin2.GetPinHeaderId() + 
            "\n" + 
            std::string("\tMotorPin3: ") + this->motorPin3.GetPinHeaderId() + 
            "\n" +
            std::string("\tMotorPin4: ") + this->motorPin4.GetPinHeaderId() + 
            "\n" +
            std::string("\tControl Mode: ") + modeString + "\n" +
            std::string("\tMax speed: ") + std::to_string(maxSpeed) + "\n\n";
  std::cout << RainbowText(message, "Light Gray");
}

/*
  Private method to initialize the Pins
*/
void StepperMotor::InitMotorPins()
{
  // Set the right modes for the pins
  motorPin1.SetMode(OUTPUT);
  motorPin2.SetMode(OUTPUT);
  motorPin3.SetMode(OUTPUT);
  motorPin4.SetMode(OUTPUT);
}

To turn the stepper motor declared as a StepperMotor object with the name of myStepper in this program, the method TurnBySteps(CW, 512) can be used, which specifies the rotation direction, CW in this case, and the number of steps to rotate the stepper motor, 512 in this case, which corresponds to 1/4-turn due that the 28BYJ-48 has 2048 steps per revolution.

1
myStepper.TurnBySteps(CW, 512);

The code of this method is shown here to illustrate how the movement of each step is done. The logic behind this is to activate each coil in the right order and sequence. The order determines if the stepper motor will rotate in the CCW direction starting the activation of the first terminal of the first coil or, in the other case, it will rotate in the CW direction if the activation starts in the second terminal of the second coil. The right sequence has to ensure that both terminals of both coils are activated sequentially to let the motor rotate.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*
  Public method to turn the motor by steps
  @param DIRECTION: The desired direction for the motor rotation
  @param unsigned int: The steps required
  @param unsigned int: The rotation's speed in steps / sec (0,maxSpeed]    
  @param bool: Flag to print / no print the messages on the console. Default value: <false>     
*/
void StepperMotor::TurnBySteps(
                                DIRECTION direction, 
                                unsigned int stepsRequired, 
                                unsigned int speed, 
                                bool printMessages
                              )
{
  // Check the speed limit value
  if (speed > maxSpeed)
    speed = maxSpeed;

  int coilStep {0};

  if (printMessages == true)
  {
    std::string message;
    if (direction == CW) 
      message = "Turning stepper motor CW ";
    else
      message = "Turning stepper motor CCW ";

    message +=
    std::to_string(stepsRequired) + " steps at " + 
    std::to_string(speed) + " steps/second\n";

    std::cout << RainbowText(message, "Light Gray");
  }

  // Turn 1 step in CW direction
  if (direction == CW)
  {
    for (int i = 0; i < stepsRequired; i++)
    {
      coilStep = stepsPerMode - 1 - (i % stepsPerMode);
      this->Turn1Step(coilStep, speed);
      
      // Update counters
      stepsCounter++;
      currentStep++;
    }
  }
  // Turn 1 step in CCW direction
  else if (direction == CCW)
  {
    for (int i = 0; i < stepsRequired; i++)
    {
      coilStep = i % stepsPerMode;
      this->Turn1Step(coilStep, speed);
      
      // Update counters
      stepsCounter++;
      currentStep--;
    }
  }
}

To know how many steps the stepper motor has executed, the next method can be used:

1
myStepper.GetStepsCounter();

In the same way, to know the actual position of the stepper motor axis in terms of number of steps, the next method can be used:

1
myStepper.GetCurrentStep()();

The complete code for this application is shown in the next listing together with its corresponding execution output.

28BYJ-48-ULN2003_1.1.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/******************************************************************************
28BYJ48-ULN2003_1.1.cpp
@wgaonar
03/04/2022
https://github.com/wgaonar/BeagleCPP

- Move the stepperMotor by steps in fullStep with 1 Coil mode

Class: STEPPERMOTOR
******************************************************************************/

#include <iostream>
#include "../../../Sources/STEPPERMOTOR.h"

using namespace std;

// Declaring the pins for motor
GPIO IN1 (P8_12);
GPIO IN2 (P8_14);
GPIO IN3 (P8_16);
GPIO IN4 (P8_18);

/*
  Declare the 28BYJ-48 stepper motor object with default parameters:
  full step with the activation of 1 coil, 
  default 2048 steps per revolution 
  and a default maximum speed of 500 steps/second
*/
StepperMotor myStepper (IN1, IN2, IN3, IN4);

int main()
{
  string message = "Main program starting here...";
  cout << RainbowText(message,"Blue", "White", "Bold") << endl;

  /* 
    Turn the stepper motor 1/4-turn in CW direction at 500 steps/second
  */
  myStepper.TurnBySteps(CW, 512);
  cout << "Steps executed by the motor: " << myStepper.GetStepsCounter() << endl;
  cout << "Actual position of the motor axis: " << myStepper.GetCurrentStep() << endl;

  /* 
    Turn the stepper motor 1/4-turn in CCW direction at 500 steps/second
  */
  myStepper.TurnBySteps(CCW, 512);
  cout << "Steps executed by the motor: " << myStepper.GetStepsCounter() << endl;
  cout << "Actual position of the motor axis: " << myStepper.GetCurrentStep() << endl;

  message = "Main program finishes here...";
  cout << RainbowText(message,"Blue", "White","Bold") << endl;

  return 0;
}

Se you in the next post.

Rating:
comments powered by Disqus