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
- A Linux distro installed onto the Raspberry Pi (i.e. Raspbian stretch lite)
- A C/C++ compiler (gcc/g++)
- cmake
- git
- The WiringPi library
- The StepperMotor library.
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 // // 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 // // 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 // // 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.
Hi, you need a correction in src/StepperMotor.cpp line 94 to convert unsigned int to int as abs only take int in parameter.
Otherwise your post was really helpful, thank you.
Hi, I’m sorry for the big delay. Thank you for the hint. Do you refer to the “angle” variable, right?
I’m not able to test the code with the Raspberry Pi in this moment, anyway I’ll try to check it soon.