When positioning Stepper Motors; identifying the Zero position is critical.
The broad overview
A stepper motor is controlled by specifying the number of steps and the direction of movement to an MCU to drive the motor's polar coils. Unlike servos, stepper motors are not position-aware and they do not return to a fixed starting position when powered-on. So, a workaround is required to rotate the stepper shaft to a preset angle upon restart. In this article, I specify a Zero point for the motor shaft using optical technology and start from there to reach the intended position.
Introduction
When I considered joining wall clocks with my IoT home network, I was astonished by the sheer amount of innovative projects automating clock mechanics with stepper motors and MCUs. But there was always the misfortune of restarting the clock after power restore or MCU reboot. User intervention was normally required in such a case to re-adjust the time setting otherwise the clock would resume incrementing time from where it last stopped.
In order to automate the recovery and re-adjustment of the clock hands, or whatever mechanism driven by the stepper, I had to introduce motor position sensing to my project. Despite the existence of position sensing techniques and products for motor shafts, I was constrained by the cost and complexity of the adopted solution especially when my project is based on cheap steppers such as the 28BYJ-48.
The most elaborate technique I came across was optical based in which a slotted wheel is attached to the shaft and infrared beams would pass through those slots to hit light sensors on the other side of the wheel.
Monitoring the status of the sensors by the MCU would determine the shaft position and even its speed of rotation.
Combining the optical parts can be found in off-the-shelf products such as the opto-coupler U-type switches.
A conventional solution would pass a section of the slotted wheel inside the switch slot in order to detect a specific position of the motor's shaft. In general terms, the detection of different positions of the shaft will require distributing the switch radially around the wheel and this would potentially clutter the motor mechanism.
In order to keep things simple, I adopted the strategy of rotating the motor shaft to a fixed starting position, called the Zero point, upon MCU restart then moving it to the
required position. When considering the wall clock case, the Zero point would be moving the clock hands, namely the hours and minutes, to the 12:00 position. If the clock is wirelessly hooked to the Internet, the current time can then be adjusted based on NTP accessed data.
In this project, I use Arduino UNO to control the positioning of a 28BYJ-48 uni-polar geared motor. Two push buttons are provided for steering the motor: one for pushing the motor back counterclockwise to the Zero position; and the second for rotating the motor one complete revolution clockwise. I shall elaborate on the Zeroing of the motor position in the next section.
The motor required an external 5.0VDC source and this demanded special handling due to some constraints I shall discuss later on regarding motor heating and UNO's limited power driving capability.
The motor has 4 poles, 2 for each coil (marked 2/4-1/3 in the image shown here) that produce the EMF (Electro-Magnetic-Force) necessary to rotate its spindle in addition to the power terminal (marked 5 in Figure) positioned in such a way to make this motor a Unipolar.
Bipolar steppers on the contrary do not have the power terminal, only the 4 edge poles of the two coils (marked A1A2, B1B2 as shown here). However, the Bipolars are more efficient compared to Unipolars in terms of power utilisation since they energise the whole coils rather than half of them during stepping. Despite the wiring differences, coding both types of steppers is similar. Driving the stepper poles using the UNO's GPIOs requires interfacing them with external drivers to avoid overloading the UNO and to protect it from back EMF generated by the coils. Rather than using an off-the-shelf stepper driver module, I used a Darlington array of the ULN2003 7-driver chip for that purpose in order to simplify my design.
The Planned Zero Point Positioning
In place of the slotted wheel I mentioned earlier, I fixed a plastic gear to the motor shaft with a blanking dark strip marking its Zero position since the body of the gear is transparent to IR.
The optical fixtures are composed of a 5mm LED that transmits IR light to an IS0203 Laser Receiver.
The two components are separated by around 1cm to allow the Zero gear to slide in between. The reader would notice the black jackets covering both components (shown here).
This plays a crucial role in limiting the amount of IR reaching the receiver. I made a tiny hole in the receiver's jacket in front of its lens to narrow its aperture. I also decreased the driving current of the IR LED to around 12.0mA instead of 80.0mA using a 330Ω/1W resistor in order to reduce the sensitivity of the optical fixture to match the small Zero strip.
Since the optical fixture operates at 5.0VDC level, I had to convert its output level to 3.3VDC to match the UNO's standards. This can be done using two methods: either using a voltage divider or using a bi-directional logic level converter. I did implement both for the sake of experimenting but the first suits slow changing signals (as with geared motors) and is not recommended for fast rotating motors.
The wiring diagram shown here illustrates the wiring of the project components. A voltage divider composed of R4 and R5 of 1kΩ and 2kΩ respectively achieved the logic level conversion of the IR sensor's output which is then fed to D2 pin of the UNO. The Adafruit converter has been employed as well to do the level conversion and its output is fed to D3 of the UNO. In my code, I can switch the UNO's pins that utilise either method.
In order to validate the operation of this Zeroing fixture, I mount the motor, by hand, with its positioning gear fitted between the IR combination (as shown here), and I press the Align button. The motor then steps counterclockwise till the black strip of the Zeroing gear gets in between the IR LED and the IR sensor at the bottom edge of the gear and aligned with the light sensor's aperture. The motor stops at that point and I can then leave the motor away to examine its normal rotation by clicking the Rotate button. The serial monitor of the Arduino IDE displays the following printout in response to the previous procedure:
08:33:35.665 -> Rotation one revolution clockwise!
08:33:45.686 -> Motor aligned to Zero position!
The project’s wiring diagram with Uno and breadboard shows you how to connect the main project components. This circuit assembly is further wired to the motor's power driver circuitry discussed in the following section.
The Motor Power Driver
One common observation about stepper motors is that they get warmer or even hotter even when they are idle. When designing a stepper application that requires continuous stepping or being in standby, we need to account for the heating problem. In my project, I got the MCU involved in the solution where I instruct the MCU to turn off the 5.0VDC that powers the stepper whenever it is not required to rotate, and regain power to it prior to stepping.
This concept proved effective in my planned wall clock project in which the minutes hand motor steps every minute for a few milliseconds and stays idle afterwards. The same concept applies even more for the hours hand motor. So I used D5 of the UNO to control the powering of the stepper by feeding it to a power switching circuit.
I implemented the motor power control circuit shown here based on the 5VDC switching circuit published in ELECTRICAL ENGINEERING. The circuit is simple and contains 2 transistors and 3 resistors. I used a BD140 bipolar NPN transistor (Q1) to drive the motor for its ability to sink up to 1.5A enough to drive the stepper. The U1 transistor is the popular 2N3904 NPN that drives Q1 whenever UNO's D5 is set to HIGH. I noticed that Q1 gets hotter when driving the motor for extended periods, so I attached to it a sticky heatsink intended for Raspberry Pi to be on the safe side.
Project Code
I credit the simplicity of writing the code for this project (see Snippet shown here) to the Stepper library maintained by Arduino.
With the absence of the library, the user would have to manipulate the UNO's pins wired to the motor's coils in a specific order and the sketch footprint would then be much larger.
So I first installed the library and declared the motor instance which specifies the number of steps the motor's axis would rotate for a complete revolution (2048 for the 28BYJ-48), and the 4 UNO's pins connected to motor's terminals 4/2/3/1 which are D8 to D11 respectively. In the setup() function, I have assigned UNO's pin 6 for the Rotate button and pin 7 for the Align button.
The INPUT_PULLUP parameter is necessary since both buttons provide floating lines to those pins in the idle state and the internal pull-up resistors of the UNO make sure a 3.3V is present on those pins till a button press grounds them. Controlling the motor power is achieved using D5 of the UNO. When this pin is set to HIGH, the power circuit is activated and the motor is ready for stepping. When the pin is reset to LOW, the motor is deactivated.
The loop() section of the sketch is composed of 2 main segments: one for Zeroing the motor position, and another for rotating the motor resembling the main purpose of the user application. In each segment, the motor power is activated then a stepping statement is executed followed by a motor deactivation statement. I made the Zeroing to proceed in counterclockwise (the minus sign of the step count) one step at a time for accurate positioning. The full revolution on the contrary uses the full step count of the motor (that is the 2048) in the clockwise direction. Since both segments are executed upon pressing the relevant button, I put a conditional statement in each segment that checks the status of its button. The reader can notice the conventional use of the millis() function for debouncing key presses in each segment so a button press should last for more than 500 ms to be effective, otherwise discarded. The reader can identify the motor related library statements being highlighted in Yellow and those related to light sensing and motor control being highlighted in Green.
#include <Stepper.h>
const int stepsPerRevolution = 2048;
// specific for 28BYJ-48
long trig = 0; // debounce var
// initialize the stepper library on
// pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);
void setup() {
pinMode(2, INPUT); // light sensor feed
pinMode(5, OUTPUT); // motor 5vdc switch
pinMode(6, INPUT_PULLUP); // button for running
// motor clockwise
pinMode(7, INPUT_PULLUP); // button for align
// motor to Zero
digitalWrite(5, LOW); // switch motor off
myStepper.setSpeed(13); // max for non-step loss
// initialize the serial port:
Serial.begin(9600);
}
void loop() {
long debounce = millis() - trig;
if (digitalRead(7) == 0 && debounce > 500) {
while (digitalRead(2) == 0) {
digitalWrite(5, HIGH); // switch motor on
myStepper.step(-1);
delay(10);
}
Serial.println("Motor aligned to Zero position!");
digitalWrite(5, LOW); // switch motor off
}
while (digitalRead(6) == 0 && debounce > 500) {
digitalWrite(5, HIGH); // switch motor on
myStepper.step(stepsPerRevolution);
Serial.println("Rotation one revolution clockwise!");
digitalWrite(5, LOW); // switch motor off
trig = millis();
debounce = 0;
}
}
Final Remarks
This project demonstrates one approach for setting a Zeroing position of a stepper motor axis to which the motor may be reset for accurate positioning following MCU reboot.
I experimented with the motor to reveal a few alarming and serious observations that required enhancements to the project to guarantee sustained and accurate operation of the motor. So adding a motor power switching circuit and managing the activation of the motor within the code were implemented for general stepper applications.