A simple C++ library to drive a stepper motor using the Raspberry Pi

About two years ago I wrote a simple C++ class to drive a small stepper motor using the Raspberry Pi.

Recently I’ve done the last change to the code.

In this post I will try to describe in a simple way how to use the library, also with the help of some examples.

Hardware requirements

  • A Raspberry Pi (models 1B and 3B, in my case)
  • One (or two) stepper motor(s)
  • A stepper motor driver
  • Some wires for the connections

 

The model of the stepper motors I used is the 28BYJ48. They cannot be connected directly to the GPIO header, so we need to put a driver between the Raspberry Pi and the motors, one for each motor.

 

 

The driver needed, which is essentially composed by a certain number of NPN BJTs grouped in pairs and connected in darlington configuration, is the ULN2003APG.

Software requirements

Software installation

First of all you need to install an operating system on your Raspberry Pi. I usually prefer a minimal installation in order to add the packages I need afterwards, so I opted for the lite version of Raspbian.

Once you have downloaded and extracted the image, a quick way to flash it to your SD card on Linux is the one here below (do it as root):

dd if=/home/your_user/Downloads/name_of_your_image.img of=/dev/your_SD_device_node

The SD device node is usually something like /dev/mmcblk0.

For more details on the installation process you can read the documentation.

________

Now we are ready to switch from the PC to the Raspberry Pi.

Let’s start with the installation of cmake and git with the following commands:

sudo apt-get install cmake
sudo apt-get install git-core

The instructions to obtain and install the WiringPi library can be found here.

Finally, the last thing to do is to download and compile the Stepper motor library:

# Download the code
git clone https://crish4cks@bitbucket.org/crish4cks/steppermotor.git

# Build it!
cd steppermotor/ 
mkdir build && cd build
cmake .. 
make

Wire connections

The following tables show the connections between the GPIO pins (on Raspberry Pi 1B, board revision 000f and on Raspberry Pi 3B, board revision a22082) and the four inputs of each stepper motor driver:

BCM GPIO pin Driver input [sm1]
17 IN 1
18 IN 2
27 IN 3
22 IN 4

 

BCM GPIO pin Driver input [sm2]
23 IN 1
24 IN 2
25 IN 3
4 IN 4

 

  • +5V source: physical pins 2 (for sm1) and 4 (for sm2) of the header
  • GND: physical pins 6 (for sm1) and 25 (for sm2) of the header.

Then, connecting the stepper motors to the outputs of the drivers is very simple because there is only a small connector to plug in.

StepperMotor library features

  • A setter method to run the motor according to direction, angle and speed provided by the user
  • 5 speed modes allowed: from 20% to 100% of the maximum speed
  • The possibility to set a maximum angle threshold (in absolute value) for the motor rotation
  • Some getter methods to provide the state of the motor.

Examples of usage

In the following sections I will briefly describe three examples of usage of the library.

For each example there are a figure describing the sequence of the movements executed by the stepper motor(s) and the respective source code. In these figures, the movements of a stepper motor are represented by red dashed lines on a unitary circumference.

The initial position of the motors is always taken on the point (x,y) = (0,1) and all the quantities are used with this point as reference.

For example, moving from (x,y) = (0,1) to (x,y) =(0.707,0.707) means to have described a positive angle of 45 degrees. Instead, moving from (x,y) = (0,1) to (x,y) = (-1,0) means to have described a negative angle of 90 degrees.

In order to run the executables you have to launch the command ./Test_N, where N is the number of the test (1, 2 or 3).

Test_1.cpp

This first test runs only one stepper motor. Here below you can see the sequence of the operations and the source code, as I said previously.

Test_1.cpp: sequence of the operations

// Test_1.cpp
//
// A simple test to drive the stepper motor.
//

#include <wiringPi.h>
#include "StepperMotor.hpp"

int main() {
    
    // wiringPi initialization
    wiringPiSetup();

    // StepperMotor object declaration
    StepperMotor sm;

    // RPi GPIO | WiringPi
    // -------------------
    // GPIO 17  |    0
    // GPIO 18  |    1
    // GPIO 27  |    2
    // GPIO 22  |    3
    sm.setGPIOutputs(0, 1, 2, 3);

    // NOTE: Before starting, the current position of the
    // stepper motor corresponds to 0 degrees

    // Rotate of 90 degrees clockwise at 100% of speed
    sm.run(1, 90, 100);

    // Sleep for 2 seconds
    sm.wait(2000);

    // Rotate of 90 degrees counterclockwise at 80% of speed
    sm.run(-1, 90, 80);

    // Sleep for 1 second
    sm.wait(1000);

    // Set a threshold of 90 degrees
    sm.setThreshold(90);

    // Rotate of 180 degrees clockwise at 100% of speed
    // It stops at +90 degrees (because of the threshold)
    sm.run(1, 180, 100);

    // Sleep for 1 second
    sm.wait(1000);

    // Rotate of 270 degrees counterclockwise at 60% of speed
    // It stops at -90 degrees (because of the threshold)
    sm.run(-1, 270, 60);

    return 0;
}

Test_2.cpp

In this example, a further stepper motor has been added. This time the two stepper motors run sequentially, one after the other (sm1, then sm2, then sm1 and so on).

Test_2.cpp: sequence of the operations

// Test_2.cpp
//
// Similar to Test_1.cpp, but with two stepper motors
// running sequentially.
//

#include <wiringPi.h>
#include "StepperMotor.hpp"

int main() {
    
    // wiringPi initialization
    wiringPiSetup();
    
    // Declaration of two StepperMotor objects
    StepperMotor sm1, sm2;

    // RPi GPIO | WiringPi
    // -------------------
    // GPIO 17  |    0
    // GPIO 18  |    1
    // GPIO 27  |    2
    // GPIO 22  |    3
    sm1.setGPIOutputs(0, 1, 2, 3);
    
    // RPi GPIO | WiringPi
    // -------------------
    // GPIO 23  |    4
    // GPIO 24  |    5
    // GPIO 25  |    6
    // GPIO 4   |    7
    sm2.setGPIOutputs(4, 5, 6, 7);

    // NOTE: Before starting, the current position of both
    // the stepper motors corresponds to 0 degrees

    // Rotate of 90 degrees clockwise at 100% of speed
    sm1.run(1, 90, 100);
    sm2.run(1, 90, 100);
    
    // Sleep for 2 seconds (2 x 1 second)
    sm1.wait(1000);
    sm2.wait(1000);
    
    // Rotate of 90 degrees counterclockwise at 80% of speed
    sm1.run(-1, 90, 80);
    sm2.run(-1, 90, 80);
    
    // Sleep for 1 second (2 x 500 milliseconds)
    sm1.wait(500);
    sm2.wait(500);
    
    // Set a threshold of 90 degrees for sm1
    sm1.setThreshold(90);
    
    // Set a threshold of 45 degrees for sm2
    sm2.setThreshold(45);

    // Rotate of 180 degrees clockwise at 100% of speed
    // sm1 stops at +90 degrees (because of the threshold)
    // sm2 stops at +45 degrees (because of the threshold)
    sm1.run(1, 180, 100);
    sm2.run(1, 180, 100);
    
    // Sleep for 1 second
    sm1.wait(500);
    sm2.wait(500);
    
    // Rotate of 270 degrees counterclockwise at 60% of speed
    // sm1 stops at -90 degrees (because of the threshold)
    // sm2 stops at -45 degrees (because of the threshold)
    sm1.run(-1, 270, 60);
    sm2.run(-1, 270, 60);
    
    return 0;
}

Test_3.cpp

In this last example I’ve made use of the pthread library to allow the two stepper motors to run in parallel.

Test_3.cpp: sequence of the operations

// Test_3.cpp
//
// Similar to Test_1.cpp, but with two stepper motors
// running in parallel (multithread version).
//

#include <stdlib.h>
#include <pthread.h>
#include <wiringPi.h>
#include "StepperMotor.hpp"

// These functions contain the operations which
// have to be performed by the two stepper motors
void * runStepperMotor1(void *arg);
void * runStepperMotor2(void *arg);

int main() {

    // wiringPi initialization
    wiringPiSetup();

    // Declaration of two thread identifiers
    pthread_t tsm1, tsm2;

    // Declaration of two StepperMotor objects
    StepperMotor sm1, sm2;

    // Declaration of two StepperMotor object pointers
    StepperMotor *psm1, *psm2;

    // Memory allocation
    psm1 = (StepperMotor *) malloc(sizeof(StepperMotor));
    psm2 = (StepperMotor *) malloc(sizeof(StepperMotor));

    // Make a copy of the objects references
    psm1 = &sm1;
    psm2 = &sm2;

    // Launch the threads passing the two pointers as arguments
    pthread_create(&tsm1, NULL, &runStepperMotor1, (void *) psm1);
    pthread_create(&tsm2, NULL, &runStepperMotor2, (void *) psm2);

    // Wait until the threads terminate
    pthread_join(tsm1, NULL);
    pthread_join(tsm2, NULL);

    // Keep alive the main() until all the threads are done
    pthread_exit(NULL);
}


void * runStepperMotor1(void *arg) {

    // Retrieve the argument (copy the reference)
    StepperMotor *psm1 = (StepperMotor *) arg;

    // RPi GPIO | WiringPi
    // -------------------
    // GPIO 17  |    0
    // GPIO 18  |    1
    // GPIO 27  |    2
    // GPIO 22  |    3
    psm1->setGPIOutputs(0, 1, 2, 3);

    // NOTE: Before starting, the current position of both
    // the stepper motors corresponds to 0 degrees

    // Rotate of 90 degrees clockwise at 100% of speed
    psm1->run(1, 90, 100);

    // Sleep for 2 seconds
    psm1->wait(2000);

    // Rotate of 90 degrees counterclockwise at 80% of speed
    psm1->run(-1, 90, 80);

    // Sleep for 1 second
    psm1->wait(1000);

    // Set a threshold of 90 degrees
    psm1->setThreshold(90);

    // Rotate of 180 degrees clockwise at 100% of speed
    // It stops at +90 degrees (because of the threshold)
    psm1->run(1, 180, 100);

    // Sleep for 1 second
    psm1->wait(1000);

    // Rotate of 270 degrees counterclockwise at 60% of speed
    // It stops at -90 degrees (because of the threshold)
    psm1->run(-1, 270, 60);

    // return NULL;
    pthread_exit(NULL);
}


void * runStepperMotor2(void *arg) {

    // Retrieve the argument (copy the reference)
    StepperMotor *psm2 = (StepperMotor *) arg;

    // RPi GPIO | WiringPi
    // -------------------
    // GPIO 23  |    4
    // GPIO 24  |    5
    // GPIO 25  |    6
    // GPIO 4   |    7
    psm2->setGPIOutputs(4, 5, 6, 7);

    // NOTE: Before starting, the current position of both
    // the stepper motors corresponds to 0 degrees

    // Rotate of 90 degrees counterclockwise at 100% of speed
    psm2->run(-1, 90, 100);

    // Sleep for 2 seconds
    psm2->wait(2000);

    // Rotate of 90 degrees clockwise at 80% of speed
    psm2->run(1, 90, 80);

    // Sleep for 1 second
    psm2->wait(1000);

    // Set a threshold of 90 degrees
    psm2->setThreshold(90);

    // Rotate of 180 degrees counterclockwise at 100% of speed
    // It stops at -90 degrees (because of the threshold)
    psm2->run(-1, 180, 100);

    // Sleep for 1 second
    psm2->wait(1000);

    // Rotate of 270 degrees clockwise at 60% of speed
    // It stops at +90 degrees (because of the threshold)
    psm2->run(1, 270, 60);

    // return NULL;
    pthread_exit(NULL);
}

Finally a short video demonstration: the sm1 is the one on the right, the sm2 the one on the left.

If I have time, I will test the library also with the Raspberry Pi 3B.

The whole code has been successfully tested also with the Raspberry Pi 3 model B.

Since the 3B model has a multi-core processor, it would be interesting to modify the last test (Test_3.cpp) in order to assign each of the two threads to a different core.

Some improvements

A thing that could be done is to implement a different use of the run() method. It could be activated by asynchronous events such as pressing a push button or receiving an HTTP POST request. In this way, only two parameters would be necessary, direction and speed.

We would be able to start and stop the stepper motor whenever we want and run it continuously, without specifing the angle.

 

Did you like this post? Share it! Email this to someone
email
Share on Google+
Google+
Tweet about this on Twitter
Twitter
Share on Facebook
Facebook
Share on LinkedIn
Linkedin

Leave a Reply