# 28BYJ-48 5-Volt Stepper

No. It is a very minor advantage of simplicity. Certainly I will use yours if there is any difference in performance. Will let you know what I have measured.

I was getting about 7RPM using 1,2,3,4 sequence in library. 15RPM with decent torque using 1,3,2,4 in library. 35RPM with your code and more torque! I can't even stop it with my finger. The improvement is like night and day. You have to increase the speed gradually during ~10ms to achieve 35RPM. Thanks!

sbright33 - Thank you for reporting your test results. 8 is better than 4, after all.

The 8 step is called a half step, on alternate steps one winding is powered down. One note, you would be best to use bitmasks and port writes so that all motor field changes happen in unison whereas your code has them happening sequentially.

Yes, I considered trying the bitmasks but was a bit lazy when I wrote the code.

It’s a work in progress…
Will comment more when it’s finished.
Questions?

``````// This Arduino example demonstrates bidirectional operation of a
// 28BYJ-48, which is readily available on eBay, using a ULN2003
// interface board to drive the stepper. The 28BYJ-48 motor is a 4-
// phase, 8-beat motor, geared down by a factor of 64. One bipolar
// winding is on motor pins 1 & 3 and the other on motor pins 2 & 4.
// Refer to the manufacturer's documentation of  Changzhou Fulling
// Motor Co., Ltd., among others.  The step angle is 5.625/64 and the
// operating Frequency is 100pps. Current draw is 92mA.

const int motorPin1 = 4;	// Blue   - 28BYJ48 pin 1
const int motorPin2 = 5;	// Pink   - 28BYJ48 pin 2
const int motorPin3 = 6;	// Yellow - 28BYJ48 pin 3
const int motorPin4 = 7;	// Orange - 28BYJ48 pin 4
// Red    - 28BYJ48 pin 5 VCC
int motorSpeed=3000;            // set stepper speed
float err=0;
void setup() {
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
pinMode(motorPin3, OUTPUT);
pinMode(motorPin4, OUTPUT);
//Serial.begin(115200);
}
void loop(){
//long m=millis();
//Serial.println(millis()-m);

if(random(4))
degrpm(random(2),80*(random(8)+1),3200);
else delay(500);

//degrpmslowCool(1,long(360)*100*5,5*60);
//degrpm(1,4500,3000);delay(1000);
}
void rev(boolean bcw, long revo) {
long step2=revo*64*8-12; //not exactly right?
int rpm100=500;
if(bcw) cwss(12,rpm100); //ramp up speed
else   ccwss(12,rpm100);
for(long i=0;i<step2;i++)
if(bcw)cw(); else ccw();
off();
}
void degrpmslowCool(boolean bcw, long deg100, int rph100) {
//ccw only, more torque, less current, less heat
motorSpeed=1200;
int step2=deg100*64*8/360/100; //rounded down
int d=long(351500)/2/rph100; //div 2? see stepper.xls
for(int i=0;i<step2;i++) {
ccw4st1(); off(); delay(d); //cools while off
ccw4st2(); off(); delay(d);
} //for
off();
}
void degrpmslowHot(boolean bcw, long deg100, int rph100) {
//ccw only, more torque
motorSpeed=1200;
int step2=deg100*64*8/360/100; //rounded down
int d=long(351500)/2/rph100; //div 2? see stepper.xls
for(int i=0;i<step2;i++) {
ccw4st1(); delay(d);
ccw4st2(); delay(d);
} //for
off();
}
void degrpmslow2(boolean bcw, long deg100, int rph100) {
//ccw only, more torque, less current, less heat
motorSpeed=1200;
int step2=deg100*64*8/360/100; //rounded down
int d=long(351500)/2/rph100; //div 2? see stepper.xls
for(int i=0;i<step2;i++) {
ccw4st1(); off(); delay(d/4); st1(); delay(d/2); off(); delay(d/4);
ccw4st2(); off(); delay(d/4); st2(); delay(d/2); off(); delay(d/4);
} //for
off();
}
void degrpm(boolean bcw, long deg100, int rpm100) {
//max 64 turns or 23,000 deg or 2,300,000 deg100 long is bigger
//max 3500 rpm100 with 12v
int step2=deg100*64*8/360/100; //rounded down
if(rpm100<50)rpm100=50; //minimum should use degrpmslow()
rpm100=long(1463600)/rpm100-20;  //see stepper.xls
if(bcw) cwss(step2,rpm100);
else   ccwss(step2,rpm100);

float movedeg=float(step2)*360/64/8;  //float library adds 2K size to sketch
//Serial.println(movedeg);
//Serial.println(movedeg-(float)deg100/100);  //moved too little only?
//ccw4st1 can help this
err+=(movedeg-(float)deg100/100);
if(err<-1) {
motorSpeed=1200;
if(bcw) cw(); else ccw();
err+=(float(360)/64/8);
//Serial.print("err=");Serial.println(err);
} //if err
off();
}
void ccwss(int steps, int speed) {
//900 self starting 5v motor 5v ps
//800 most of 100% torque
//700 less torque
//600 almost none
//5v motor 12v supply:
//700 self starting
//400 decent torque
//6.6v 20RPM
//5V 10RPM strong
//if(speed<700) steps-=10;
//ramp up speed
motorSpeed=3000; if(speed<1200) {ccw(); ccw(); steps-=2;}
motorSpeed=1200; if(speed<1200) {ccw(); ccw(); steps-=2;}
motorSpeed=800;  if(speed<800 ) {ccw(); ccw(); steps-=2;}
motorSpeed=700;  if(speed<700 ) for(int i=0;i<4;i++) {ccw(); steps--;}
motorSpeed=speed; for(int i=0;i<steps;i++) ccw();  //64*8 is 1 rev
}
void cwss(int steps, int speed) {
motorSpeed=3000; if(speed<1200) {cw(); cw(); steps-=2;}
motorSpeed=1200; if(speed<1200) {cw(); cw(); steps-=2;}
motorSpeed=800;  if(speed<800 ) {cw(); cw(); steps-=2;}
motorSpeed=700;  if(speed<700 ) for(int i=0;i<4;i++) {cw(); steps--;}
motorSpeed=speed; for(int i=0;i<steps;i++) cw();  //64*8 is 1 rev
}
void ccw4st1(){
// 1
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
delayMicroseconds(motorSpeed);
// 2
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
delayMicroseconds(motorSpeed);
// 3
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
delayMicroseconds(motorSpeed);
// 4
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin4, LOW);
delayMicroseconds(motorSpeed);
}
void st1(){
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin4, LOW);
}
void ccw4st2(){
// 5
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin4, LOW);
delayMicroseconds(motorSpeed);
// 6
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin4, HIGH);
delayMicroseconds(motorSpeed);
// 7
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, HIGH);
delayMicroseconds(motorSpeed);
// 8
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, HIGH);
delayMicroseconds(motorSpeed);
}
void st2(){
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, HIGH);
}
void off(){
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
}
//////////////////////////////////////////////////////////////////////////////
//set pins to ULN2003 high in sequence from 1 to 4
void ccw (){
// 1
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
delayMicroseconds(motorSpeed);
// 2
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
delayMicroseconds(motorSpeed);
// 3
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
delayMicroseconds(motorSpeed);
// 4
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin4, LOW);
delayMicroseconds(motorSpeed);
// 5
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin4, LOW);
delayMicroseconds(motorSpeed);
// 6
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin4, HIGH);
delayMicroseconds(motorSpeed);
// 7
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, HIGH);
delayMicroseconds(motorSpeed);
// 8
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, HIGH);
delayMicroseconds(motorSpeed);
}
//////////////////////////////////////////////////////////////////////////////
//set pins to ULN2003 high in sequence from 4 to 1
void cw(){
// 1
digitalWrite(motorPin4, HIGH);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin1, LOW);
delayMicroseconds(motorSpeed);
// 2
digitalWrite(motorPin4, HIGH);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin1, LOW);
delayMicroseconds(motorSpeed);
// 3
digitalWrite(motorPin4, LOW);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin1, LOW);
delayMicroseconds(motorSpeed);
// 4
digitalWrite(motorPin4, LOW);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin1, LOW);
delayMicroseconds(motorSpeed);
// 5
digitalWrite(motorPin4, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin1, LOW);
delayMicroseconds(motorSpeed);
// 6
digitalWrite(motorPin4, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin1, HIGH);
delayMicroseconds(motorSpeed);
// 7
digitalWrite(motorPin4, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin1, HIGH);
delayMicroseconds(motorSpeed);
// 8
digitalWrite(motorPin4, HIGH);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin1, HIGH);
delayMicroseconds(motorSpeed);
}
``````

New version.
You can easily microstep with move(1) and degrpm8()

``````#define dw digitalWrite
#define dm delayMicroseconds

const int mp1 = 4;	// Blue   - 28BYJ48 pin 1
const int mp2 = 5;	// Pink   - 28BYJ48 pin 2
const int mp3 = 6;	// Yellow - 28BYJ48 pin 3
const int mp4 = 7;	// Orange - 28BYJ48 pin 4
// Red    - 28BYJ48 pin 5 VCC
int motorSpeed=3000;            // set stepper speed
int stepnum=0;
float err=0;

void setup() {
pinMode(mp1, OUTPUT);
pinMode(mp2, OUTPUT);
pinMode(mp3, OUTPUT);
pinMode(mp4, OUTPUT);
//Serial.begin(115200);
}
void loop(){
//long m=millis();
//Serial.println(millis()-m);
//if(random(4)) degrpm8(random(2),9*(random(20)+2),3200); else delay(100);
//degrpmslowCool4(1,long(360)*100*5,10*60);
//for(int i=0;i<90;i++) {degrpm(1,400,800);delay(50);}
//for(int i=0;i<8;i++) degrpm8(0,4500,2400);
//revRestart(1,2,2000,4);
//does not work 5v for(int i=0;i<500;i++){move(1); off(); delay(1);}
//for(int i=0;i<500;i++){move(1); off(); delay(1);} //<5ms ok w 12v Cool4 better
degrpm(1,180*100,2000);
//ramp(1,2000);
off();delay(500);
}
void ramp(boolean bcw, int rpm100) {
//past 1200 it ramps in cwss() also that's bad
for(int i=50;i<1200;i+=100) //1200 or rpm100 for <1200
degrpmEZ(bcw,900,i); //<900deg ignores speed
revRestart(bcw,2,rpm100,1);
}
void revRestart(boolean bcw, long revo, int rpm100, int xinrev) {
//xinrev small only
//in case it gets stuck due to torque load spike
for(long i=0;i<revo*xinrev;i++)
degrpm(bcw,36000/xinrev,rpm100);
}
void rev(boolean bcw, long revo) {
long step2=revo*64*8-12; //not exactly right?
int rpm100=500;
if(bcw) cwss(12,rpm100); //ramp up speed
else   ccwss(12,rpm100);
for(long i=0;i<step2;i++)
if(bcw)cw(); else ccw();
off();
}
void degrpmslowCool4(boolean bcw, long deg100, int rph100) {
//ccw only, more torque, less current, less heat, most efficient code for maH
//but 4x as jerky
motorSpeed=1000;
int step2=deg100*64*8/360/100/2; //rounded down
int d=long(351500)*2/rph100; //div 2? see stepper.xls was /2 now *2
for(int i=0;i<step2;i++) {
ccw(); ccw(); off(); delay(d);
} //for
off();
}
void degrpmslowCool(boolean bcw, long deg100, int rph100) {
//ccw only, more torque, less current, less heat
motorSpeed=1500; //why 1500 needed?
int step2=deg100*64*8/360/100; //rounded down
int d=long(351500)/2/rph100; //div 2? see stepper.xls
for(int i=0;i<step2;i++) {
ccw4st1(); off(); delay(d); //cools while off
ccw4st2(); off(); delay(d);
} //for
off();
}
void degrpmslowHot(boolean bcw, long deg100, int rph100) {
//ccw only, more torque, hot w 12v
motorSpeed=1000;
int step2=deg100*64*8/360/100; //rounded down
int d=long(351500)/2/rph100; //div 2? see stepper.xls
for(int i=0;i<step2;i++) {
ccw4st1(); delay(d);
ccw4st2(); delay(d);
} //for
off();
}
void degrpmslow2(boolean bcw, long deg100, int rph100) {
//ccw only, more torque, less current, less heat, 50% duty cycle
//compromise Hot,Cool
motorSpeed=1000; //12v
int step2=deg100*64*8/360/100; //rounded down
int d=long(351500)/2/rph100; //div 2? see stepper.xls
for(int i=0;i<step2;i++) {
ccw4st1(); off(); delay(d/4); st1(); delay(d/2); off(); delay(d/4);
ccw4st2(); off(); delay(d/4); st2(); delay(d/2); off(); delay(d/4);
} //for
off();
}
void degrpmEZ(boolean bcw, long deg100, int rpm100) {
//max 64 turns or 23,000 deg or 2,300,000 deg100 long is bigger
//max 3500 rpm100 with 12v
int step2=deg100*64*8/360/100; //rounded down
if(rpm100<50)rpm100=50; //minimum should use degrpmslow()
rpm100=long(1463600)/rpm100-20;  //see stepper.xls
if(bcw) cwss(step2,rpm100);
else   ccwss(step2,rpm100);
}
void degrpm(boolean bcw, long deg100, int rpm100) {
//max 64 turns or 23,000 deg or 2,300,000 deg100 long is bigger
//max 3500 rpm100 with 12v
int step2=deg100*64*8/360/100; //rounded down
if(rpm100<50)rpm100=50; //minimum should use degrpmslow()
rpm100=long(1463600)/rpm100-20;  //see stepper.xls
if(bcw) cwss(step2,rpm100);
else   ccwss(step2,rpm100);

//with this code you can step by 2.00 deg 180x will be 360+-1
//0.50 deg 720x works to 360
//even though step size is >0.50 or 0.72?
float movedeg=float(step2)*360/64/8;  //float library adds 2K size to sketch
//Serial.println(movedeg);
//Serial.println(movedeg-(float)deg100/100);  //moved too little only?
//ccw4st1 can help this
err+=(movedeg-(float)deg100/100);
if(err<-1) {
motorSpeed=1200;
if(bcw) cw(); else ccw();
err+=(float(360)/64/8);
//Serial.print("err=");Serial.println(err);
} //if err
//soft stop moves further than it should
//if(bcw) cwss(15,2000);
//else   ccwss(15,2000);
delay(10); //so it stops and holds before off in loop
//off();
}
void degrpm8(boolean bcw, long deg100, int rpm100) {
//max 8 turns
//max <<3500 rpm100 with 12v cuz no ramping ~17RPM
const int min2start=1200; //700 12v, 1200 5v
int step2=deg100*64*64/360/100; //rounded down
if(rpm100<50)rpm100=50; //minimum should use degrpmslow()
rpm100=long(1463600)/rpm100-20;  //see stepper.xls same SAME
motorSpeed=rpm100;
if(motorSpeed<min2start)motorSpeed=min2start;
for(int i=0;i<step2;i++) {
if(bcw)stepnum++; else stepnum--;
st07();
}
//was if(bcw)cwss(step2,rpm100); else ccwss(step2,rpm100);
//does not adjust for err like degrpm cuz step 0.088 or 1/11 deg
float movedeg=float(step2)*360/64/64;  //float library adds 2K size to sketch
//Serial.println(movedeg);
err+=(movedeg-(float)deg100/100);
//Serial.println(err);
//if(err<-1) {motorSpeed=1200; if(bcw) cw(); else ccw(); err+=(float(360)/64/8);}
//off();
}
void move(boolean bcw){
motorSpeed=1200;
if(bcw)stepnum++; else stepnum--;
st07();
}
void ccwss(int steps, int speed) {
//900 self starting 5v motor 5v ps
//800 most of 100% torque
//700 less torque
//600 almost none
//5v motor 12v supply:
//700 self starting
//400 decent torque
//6.6v 20RPM
//8.3v load 8.5v float 6x NiMh
//20RPM strong 28RPM weak
//15RPM 100% torque
//does not overheat
//5V 10RPM strong
//5V 20RPM weak
//5V 24RPM no torque
//12V 20RPM self start 35 max speed
//if(speed<700) steps-=10;
//ramp up speed
motorSpeed=1000; //<12 steps, 800 for 12v
if(steps>=12) {
motorSpeed=3000; if(speed<1200) {ccw(); ccw(); steps-=2;}
motorSpeed=1200; if(speed<1200) {ccw(); ccw(); steps-=2;}
motorSpeed=800;  if(speed<800 ) {ccw(); ccw(); steps-=2;}
motorSpeed=700;  if(speed<700 ) for(int i=0;i<4;i++) {ccw(); steps--;}
motorSpeed=speed;
} //if
for(int i=0;i<steps;i++) ccw();  //64*8 is 1 rev
}
void cwss(int steps, int speed) {
motorSpeed=1000; //<12 steps, 800 for 12v
if(steps>=12) {
motorSpeed=3000; if(speed<1200) {cw(); cw(); steps-=2;}
motorSpeed=1200; if(speed<1200) {cw(); cw(); steps-=2;}
motorSpeed=800;  if(speed<800 ) {cw(); cw(); steps-=2;}
motorSpeed=700;  if(speed<700 ) for(int i=0;i<4;i++) {cw(); steps--;}
motorSpeed=speed;
} //if
for(int i=0;i<steps;i++) cw();  //64*8 is 1 rev
}
void ccw4st1(){
// 1
dw(mp1, HIGH);
dw(mp2, LOW);
dw(mp3, LOW);
dw(mp4, LOW);
dm(motorSpeed);
// 2
dw(mp1, HIGH);
dw(mp2, HIGH);
dw(mp3, LOW);
dw(mp4, LOW);
dm(motorSpeed);
// 3
dw(mp1, LOW);
dw(mp2, HIGH);
dw(mp3, LOW);
dw(mp4, LOW);
dm(motorSpeed);
// 4
dw(mp1, LOW);
dw(mp2, HIGH);
dw(mp3, HIGH);
dw(mp4, LOW);
dm(motorSpeed);
}
void st1(){
dw(mp1, LOW);
dw(mp2, HIGH);
dw(mp3, HIGH);
dw(mp4, LOW);
}
void ccw4st2(){
// 5
dw(mp1, LOW);
dw(mp2, LOW);
dw(mp3, HIGH);
dw(mp4, LOW);
dm(motorSpeed);
// 6
dw(mp1, LOW);
dw(mp2, LOW);
dw(mp3, HIGH);
dw(mp4, HIGH);
dm(motorSpeed);
// 7
dw(mp1, LOW);
dw(mp2, LOW);
dw(mp3, LOW);
dw(mp4, HIGH);
dm(motorSpeed);
// 8
dw(mp1, HIGH);
dw(mp2, LOW);
dw(mp3, LOW);
dw(mp4, HIGH);
dm(motorSpeed);
}
void st2(){
dw(mp1, HIGH);
dw(mp2, LOW);
dw(mp3, LOW);
dw(mp4, HIGH);
}
void off(){
dw(mp1, LOW);
dw(mp2, LOW);
dw(mp3, LOW);
dw(mp4, LOW);
}
void st07(){
if(stepnum==-1)stepnum=7;
if(stepnum==8)stepnum=0;
switch(stepnum){
case 0:
dw(mp4, HIGH);
dw(mp3, LOW);
dw(mp2, LOW);
dw(mp1, LOW);
dm(motorSpeed);
break;
case 1:
dw(mp4, HIGH);
dw(mp3, HIGH);
dw(mp2, LOW);
dw(mp1, LOW);
dm(motorSpeed);
break;
case 2:
dw(mp4, LOW);
dw(mp3, HIGH);
dw(mp2, LOW);
dw(mp1, LOW);
dm(motorSpeed);
break;
case 3:
dw(mp4, LOW);
dw(mp3, HIGH);
dw(mp2, HIGH);
dw(mp1, LOW);
dm(motorSpeed);
break;
case 4:
dw(mp4, LOW);
dw(mp3, LOW);
dw(mp2, HIGH);
dw(mp1, LOW);
dm(motorSpeed);
break;
case 5:
dw(mp4, LOW);
dw(mp3, LOW);
dw(mp2, HIGH);
dw(mp1, HIGH);
dm(motorSpeed);
break;
case 6:
dw(mp4, LOW);
dw(mp3, LOW);
dw(mp2, LOW);
dw(mp1, HIGH);
dm(motorSpeed);
break;
case 7:
dw(mp4, HIGH);
dw(mp3, LOW);
dw(mp2, LOW);
dw(mp1, HIGH);
dm(motorSpeed);
break;
}
}
``````

I haven’t tried either of your sketches yet but you have been busy. Given your comments about heat and 12-volt operation apparently you are putting a lot of stress testing with your stepper(s).

What is the expected life of a stepper? I have discovered that 8v 6xNiMh or 12v is fine so long as you turn off the power when it's not moving. Or between steps when it's moving slowly. The 64:1 gear ratio keeps most external torque from changing the current phase in the sequence when the power is off. My functions prove this concept. Notice my parameters in the function calls look like this: 4500 is 45 degrees. 4525 is 45.25 degrees. 3050 is 30.50 RPM. The slow functions take RPHour. Just multiply RPM*60 if you prefer to use that instead. You can even step by 2.00 degress for example without microstepping. 1/2 stepping? 1/8 stepping? The 8-step size is not a whole interval of 2.00, but you can step 180 times and it will be 360 degrees of rotation anyway. degrpm() supports ramping. Or just use degrpm8() if 1/11th of a degree is good enough for you. The only thing to add is speed ramping to achieve the highest RPM in degrpm8. I figure it's not important because we're only moving a short distance anyway.

Does anyone have code to translate an arbitrary angle to X and Y motors? In other words 45 degrees would mean moving each motor 1 microstep at a time alternating evenly between them. 0 degrees is all X. 90 degrees all Y. I don't want to move one motor 30 steps and the other 40 that would look jaggy. Arctan gives you the ratio, but I need more than that.

I have added the ability to measure a torque load and stop when it reaches the end of pulling a string for example. Also Serial.printing turns or degrees as it is running. Increased resolution to +-1/22 degrees in degrpm8().

sbright33 - your sketch posted on January 13, 2012, 06:51:43 PM is missing declarations for cw() and ccw().

Also, has your testing with microsecond delays determined an optimal delay in microseconds after doing the stepper write?

I rewrote my original stepper test to utilize direct port manipulation. I hesitated in doing this until I was positive that writes to PORTB were not going to mess up the crystal on PB6 & PB7 - it does not. DO NOT TRY THIS SKETCH unless your setup is identical to the sketch. The PORTB writes only write to Digital pins 8-to-13. If you use different pins then you MUST rewrite the sketch. Read the other warning in the sketch. This sketch only been tested on an Arduino NANO.

``````// WARNING: USE AT YOUR OWN RISK!!!
// Do not use this script if your Arduino doesn't use a Atmega328 or Atmega168
// It is only been tested on an Arduino NANO
// This Arduino example demonstrates bidirectional operation of a
// 28BYJ-48, which is readily available on eBay, using a ULN2003
// interface board to drive the stepper.
//////////////////////////////
// The Atmega328p chips used on the Arduino board have three ports.
// We are interested in port B:
// We are using Arduino Digital bits 8-11, which map to Atmega328p PB0-PB3
// B (digital pin 8 to 13)
// NOTE: as tested by anescient, there's no harm in writing to PORTB as long as DDRB[6:7] are 0
////////////////////////////////////////////////
// The speed and direction of the stepper motor is determined
// by adjusting a 1k-ohm potentiometer connected to Arduino pin A2.
// When the potentiometer is rotated fully counterclockwise, the motor
// will rotate at full counterclockwise speed. As the potentiometer is
// rotated clockwise, the motor will continue to slow down until is
// reaches its minimum speed at the the potentiometer's midpoint value .
// Once the potentiometer crosses its midpoint, the motor will reverse
// direction. As the potentiometer is rotated further clockwise, the speed
// of the motor will increase until it reaches its full clockwise rotation
// speed when the potentiometer has been rotated fully clockwise.
////////////////////////////////////////////////

//declare variables for the motor pins
int motorPin1 = 8;	// Blue   - 28BYJ48 pin 1
int motorPin2 = 9;	// Pink   - 28BYJ48 pin 2
int motorPin3 = 10;	// Yellow - 28BYJ48 pin 3
int motorPin4 = 11;	// Orange - 28BYJ48 pin 4
// Red    - 28BYJ48 pin 5 (VCC)

int motorSpeed = 0;     //variable to set stepper speed
int potPin = 2; 	//potentiometer connected to A2
int potValue = 0; 	//variable to read A0 input

//////////////////////////////////////////////////////////////////////////////
void setup() {
//declare the motor pins as outputs
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
pinMode(motorPin3, OUTPUT);
pinMode(motorPin4, OUTPUT);
Serial.begin(9600);
}

//////////////////////////////////////////////////////////////////////////////
void loop(){

Serial.println(potValue);          // View full range from 0 - 1024 in Serial Monitor
if (potValue < 535){               // if potentiometer reads 0 to 535 do this
motorSpeed = (potValue/15 + 5);  //scale potValue to be useful for motor
clockwise();                     //go to the ccw rotation function
}
else {                             //value of the potentiometer is 512 - 1024
motorSpeed = ((1024-potValue)/15 + 5); //scale potValue for motor speed
counterclockwise(); //go the the cw rotation function
}
}

//////////////////////////////////////////////////////////////////////////////
//set pins to ULN2003 high in sequence from 1 to 4
//delay "motorSpeed" between each pin setting (to determine speed)

void counterclockwise (){
// 1
PORTB = 0b0001;
delay(motorSpeed);
// 2
PORTB = 0b0011;
delay(motorSpeed);
// 3
PORTB = 0b0010;
delay(motorSpeed);
// 4
PORTB = 0b0110;
delay(motorSpeed);
// 5
PORTB = 0b0100;
delay(motorSpeed);
// 6
PORTB = 0b1100;
delay(motorSpeed);
// 7
PORTB = 0b1000;
delay(motorSpeed);
// 8
PORTB = 0b1001;
delay(motorSpeed);
}

//////////////////////////////////////////////////////////////////////////////
//set pins to ULN2003 high in sequence from 4 to 1
//delay "motorSpeed" between each pin setting (to determine speed)

void clockwise(){
// 1
PORTB = 0b1000;
delay(motorSpeed);
// 2
PORTB = 0b1100;
delay(motorSpeed);
// 3
PORTB = 0b0100;
delay(motorSpeed);
// 4
PORTB = 0b0110;
delay(motorSpeed);
// 5
PORTB = 0b0010;
delay(motorSpeed);
// 6
PORTB = 0b0011;
delay(motorSpeed);
// 7
PORTB = 0b0001;
delay(motorSpeed);
// 8
PORTB = 0b1001;
delay(motorSpeed);
}
``````

Now if I can determine the optimal “motorspeed” value then I can optimize the stepper’s functionality.

I couldn't fit cw and ccw() in the 10k code box limits of the forum. It is included in the previous version, as well as the original version. 400us works if you ramp up the speed first. Or just call degrpm(35rpm) it does that for you. All the relevant speeds are listed in the comments. I don't see the advantage of PORTB? DigitalWrite is only microseconds apart which is not significant compared to 400 or 1200us. Can you go faster than me? My sketch is compatible with most Arduinos having the relevant ports which you specify at the top.

PORTB's advantage is probably moot, since we are throwing in delays anyway. The only advantage is to eliminate those microseconds of delay when setting two ports simultaneously. However, the difference is not trivial - is about 95 times faster, according to John Boxall's blog: http://tronixstuff.wordpress.com/tag/portb/

However, using PORTx commands introduce a lot of risk, reduces portability and readability and should be avoided unless truly needed.

I just wanted to see if I could get it working and I will NOT be using PORTx for a stepper application.

My point being that sequential turn off of motor windings is the same as a movement command, ergo there is a probability of either a backstep or a forward step as you release control.

As you move from 1 step to the next you are only changing 1 winding at a time. So if it begins to move before the 4th winding is not changed, all the better! Then there is a delay of 1000x. When you come to an immediate stop, there is no changes to worry about. When you turn off the power in my code, the order could be important. I have not handled this case yet. You can choose which order to do this in. If 1-4 then it could in theory move backward. If 4-1 then maybe forward. It could not move forward if there is some torque against it. With the gear ratio of 64:1 nor will it move backwards. Even if it did move a step 1/11th degree, you're coming to a stop anyway. When you start up again it will be as if it didn't slip backwards. It should not effect torque or top speed. Agree?

For those of you interested in greater technical detail on controlling a stepper motor, take a look at Atmel Corp's Application Note titled "AVR446: Linear speed control of stepper motor". Lots of math there for you number crunchers. it is an PDF located at: http://www.atmel.com/dyn/resources/prod_documents/doc8017.pdf

I was able to get the Arduino stepper library to perform better for the 28BYJ-48, but, of course, the Arduino stepper library is limited and does not support acceleration/deceleration. Here is working Arduino stepper library code for the 28BYJ-48:

``````/*
Derived from YourDuino.com Example Software Sketch
Small Stepper Motor and Driver, by:
terry@yourduino.com
*/

#include <Stepper.h>
//declare variables for the motor pins
int motorPin1 = 8;	// Blue   - 28BYJ48 pin 1
int motorPin2 = 9;	// Pink   - 28BYJ48 pin 2
int motorPin3 = 10;	// Yellow - 28BYJ48 pin 3
int motorPin4 = 11;	// Orange - 28BYJ48 pin 4
// Red    - 28BYJ48 pin 5 (VCC)

#define STEPS  64   //Number of steps per revolution

//The pin connections need to be 4 pins connected
// to Motor Driver In1, In2, In3, In4  and then the pins entered
// here in the sequence 1-3-2-4 for proper sequencing of 28BYJ48
Stepper small_stepper(STEPS, motorPin1, motorPin3, motorPin2, motorPin4);

int  Steps2Take;

void setup()   /*----( SETUP: RUNS ONCE )----*/
{
small_stepper.setSpeed(200);
}/*--(end setup )---*/

void loop()
{
// sweep 1 turn each way
small_stepper.setSpeed(200);
Steps2Take  =  2048;  // Rotate CW
small_stepper.step(Steps2Take);
delay(2000);

small_stepper.setSpeed(200);  // 200 a good max speed??
Steps2Take  =  -2048;  // Rotate CCW
small_stepper.step(Steps2Take);
delay(2000);

}
``````

Most recently, I have been experimenting with the powerful accelstepper library (http://www.open.com.au/mikem/arduino/AccelStepper). It supports acceleration/deceleration and much more. I posted a short demo video of a 28BYJ-48 being driven by the accelstepper library in full-step mode. It actually is more impressive in half-step mode but I didn’t make a video of that. The video is at: http://youtu.be/1F3240hCHE4

The sketch used in the video is shown below:

``````// accellsteppertest.ino
// Runs one stepper forwards and backwards, accelerating and decelerating
// at the limits. Derived from example code by Mike McCauley
// Set for 28BYJ-48 stepper

#include <AccelStepper.h>

#define FULLSTEP 4
#define HALFSTEP 8

//declare variables for the motor pins
int motorPin1 = 8;	// Blue   - 28BYJ48 pin 1
int motorPin2 = 9;	// Pink   - 28BYJ48 pin 2
int motorPin3 = 10;	// Yellow - 28BYJ48 pin 3
int motorPin4 = 11;	// Orange - 28BYJ48 pin 4
// Red    - 28BYJ48 pin 5 (VCC)

// The sequence 1-3-2-4 required for proper sequencing of 28BYJ48
AccelStepper stepper2(FULLSTEP, motorPin1, motorPin3, motorPin2, motorPin4);

void setup()
{
stepper2.setMaxSpeed(1000.0);
stepper2.setAcceleration(50.0);
stepper2.setSpeed(200);
stepper2.moveTo(2048);
}

void loop()
{
//Change direction at the limits
if (stepper2.distanceToGo() == 0) {
stepper2.moveTo(-stepper2.currentPosition());
}
stepper2.run();
}
``````

The accelstepper library has many useful features. Check it out!

I find that library to be difficult to understand for a beginner. Maybe I'm biased because I wrote mine. I've added some of the useful features mine was missing. I can detect the torque load or change!

Hello,

I just bought the 28BYJ48 stepper motor and will try these codes.
Just so that I will not burn my Arduino Nano, how is the wiring done?
I have also ULN2003 driver.

Thanks