The 28BYJ-48 stepper motor with a ULN2003 driver board is readily and cheaply available on eBay. With nothing special in mind, other than curiosity, I bought one and had a quick play with it, using the standard Arduino Stepper library.
I managed to do little more than demonstrate that I could make the motor rotate, before I had to put it on one side because of other demands on my time..
Moving on several months to Christmas, my wife bought me a Lego Technic kit as a joke present - she may come to regret that joke ! As an ex-Meccano builder I was fascinated by the different approach needed for Lego Technic and soon became interested in motorising the kit. and possibly having Arduino control. Lego motors seemed a bit pricey so I thought about using the 28BYJ-48. A quick Google search brought up a number of postings showing how various motors had been "legoised", so I "legoised" my 28BYJ-48 - it's quite easy!
When I started to play with some of the example sketches from the standard Stepper library, I soon realised that they didn't drive the 28BYJ-48 properly, so I Googled again and came up with several links, including this excellent posting:
http://42bots.com/tutorials/28byj-48-stepper-motor-with-uln2003-driver-and-arduino-uno/
The author - Stan - has written a brilliant article, which explains everything perfectly and also provides the link to the AccelStepper library, developed and supported by Mike McCauley :
http://www.airspayce.com/mikem/arduino/AccelStepper/
The crucial things I learned from Stan's posting were:
- The 28BYJ-48 needs to be operated in half-step mode, ie an 8 pulse sequence - the standard Arduino Stepper library can't do this.
- The 28BYJ-48 coils need to be energised in the pin sequence 1,3,2,4, rather than the standard 1,2,3,4 sequence
Below is a simple sketch, based on one of the examples provided by Mike Mcauley and adapted by Stan and then by me, for the 28BYJ-48, to demonstrate some of the features of the AccelStepper library.
During Setup, the initial parameters of the motor (maximum speed, acceleration and (final)speed) are set up, together with an instruction to move the number of steps defined by integer variable endpoint ie 1024 or approximately 1/4 revolution, in a clockwise direction, because endpoint is positive.
(see Stan's article for further information about the 28BYJ-48 step angle and gearing, which results in 4076 steps for a full revolution, instead of the expected 4096 steps.)
The motor does not move until the command stepper1.run(); is executed in the main loop. At this point, dependent on the initial parameters of the motor and the time elapsed since the command was last executed, a number of steps are initiated. Each time through the main loop, the perceived position of the motor is tested and the motor is moved, until it reaches endpoint.
When the motor has reached endpoint, determined by if(stepper1.distanceToGo() ==0)
- the current motor position is output for confirmation that endpoint has been reached
- the current motor position is reset to 0
- the value of endpoint is negated (so the motor will move in the reverse direction approximately 1/4 revolution)
- the motor is instructed to move to endpoint
- The current motor position is output, to confirm that the motor has not yet moved and the current position is still 0.
#include <AccelStepper.h> #define HALFSTEP 8 #define motorPin1 8 // IN1 on ULN2003 ==> Blue on 28BYJ-48 #define motorPin2 9 // IN2 on ULN2004 ==> Pink on 28BYJ-48 #define motorPin3 10 // IN3 on ULN2003 ==> Yellow on 28BYJ-48 #define motorPin4 11 // IN4 on ULN2003 ==> Orange on 28BYJ-48 int endPoint = 1024; // Move this many steps - 1024 = approx 1/4 turn // NOTE: The sequence 1-3-2-4 is required for proper sequencing of 28BYJ-48 AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4); void setup() { Serial.begin(9600); Serial.println(stepper1.currentPosition()); delay(5000); stepper1.setMaxSpeed(1000.0); stepper1.setAcceleration(100.0); stepper1.setSpeed(200); stepper1.moveTo(endPoint); } void loop() { //Change direction at the limits if (stepper1.distanceToGo() == 0) { Serial.println(stepper1.currentPosition()); stepper1.setCurrentPosition(0); endPoint = -endPoint; stepper1.moveTo(endPoint); Serial.println(stepper1.currentPosition()); } stepper1.run(); }
A very common application for stepper motors is that of accurate and repeatable linear positioning. My Lego Technic kit included a short length of toothed rack and a mating pinion gear. Using my new found Lego Technic skills, I created a simple demonstration system which had the Arduino driving the stepper motor to move the rack backwards and forwards.
I soon realised that this was too simplistic - at power on, at the start of the sketch, the stepper motor is assumed to be at position 0, when, in reality, it could be anywhere, so the sketch needs to "home" the motor to a known physical position and then set that as position 0 in software. This can be achieved with a limit switch at one end of the rack travel, with the switch applying an interrupt to pin 2 (int0) of the Arduino.
Here is a simple sketch to demonstrate the principle of the "homing" process.
#include <AccelStepper.h> #define HALFSTEP 8 #define motorPin1 8 // IN1 on ULN2003 ==> Blue on 28BYJ-48 #define motorPin2 9 // IN2 on ULN2004 ==> Pink on 28BYJ-48 #define motorPin3 10 // IN3 on ULN2003 ==> Yellow on 28BYJ-48 #define motorPin4 11 // IN4 on ULN2003 ==> Orange on 28BYJ-48 int endPoint = 1024; // Move this many steps; 1024 = approx 1/4 turn // NOTE: The sequence 1-3-2-4 is required for proper sequencing of 28BYJ-48 AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4); volatile boolean stopNow = false; // flag for Interrupt Service routine int cycleCount = 0; // counter for endpoint cycles boolean homed = false; // flag to indicate when motor is homed void setup() { attachInterrupt(0, intService, LOW); // Enable interrupt 0, switch pulled low Serial.begin(9600); Serial.println(stepper1.currentPosition()); delay(5000); stepper1.setMaxSpeed(1000.0); stepper1.setAcceleration(100.0); stepper1.setSpeed(200); stepper1.moveTo(endPoint); } void loop() { if (stopNow) { detachInterrupt(0); // Interrupt not needed again (for the moment) Serial.print("Interrupted at "); Serial.println(stepper1.currentPosition()); stepper1.stop(); stepper1.setCurrentPosition(0); Serial.print("position established at home..."); Serial.println(stepper1.currentPosition()); stopNow = false; // Prevents repeated execution of the above code homed = true; } else if (stepper1.distanceToGo() == 0 && !homed) //executed repeatedly until we hit the limitswitch { Serial.println(stepper1.currentPosition()); stepper1.setCurrentPosition(0); stepper1.moveTo(endPoint); cycleCount ++; Serial.print("cycle count = "); Serial.println(cycleCount); Serial.println(stepper1.currentPosition()); } stepper1.run(); if(homed) { Serial.println("I am homed"); //Prints continually once homed } } // // Interrupt service routine for INT 0 // void intService() { stopNow = true; // Set flag to show Interrupt recognised and then stop the motor }
As the ideas for the application develops, it might be necessary to have a limitswitch at each end of the rack travel, with each switch activating a different interrupt, to prevent accidental overrun. Etc Etc
My next experiment might be to control the stepper motor - stop, start, forwards, backwards etc, via Bluetooth, from an Android App, developed with MIT App Inventor.
Top Comments