High rpm from 28byj 5V stepper

Hi, im looking for some pointers from some of those more experienced with the C+ coding side than myself.
I have some steppers that I am controlling from analogue input and need them to rotate / track faster. I have read that higher speeds can be achieved by applying a higher voltage (~12Vdc) and only stepping the motor in half-steps but cannot replicate this myself. I have been trying to figure SBrights code / library but as I am still very new to C+ I am finding it hard to follow. I have also seen some other variants using 8v but this code is also hard for me to decipher.
Please can someone help me here or give me some pointers as to where I may be able to optimise my code? I am running the motor from a dedicated variable DC supply so anywhere from 5v - 12v is possible.

Many thanks.

int Pin0 = 10; 
int Pin1 = 11; 
int Pin2 = 12; 
int Pin3 = 13; 
int _step = 0; 
boolean dir = true;
int motorSpeed = 0;     //variable which I have tried to vary
int potPin = 2; 	//pot connected to A2
int potValue = 0; 	
const int X = 10, Ydeadzone = 3; // slack for pot values
void setup() 
{ 
 pinMode(Pin0, OUTPUT);  
 pinMode(Pin1, OUTPUT);  
 pinMode(Pin2, OUTPUT);  
 pinMode(Pin3, OUTPUT);  
 Serial.begin(9600);
} 
void loop(){

  potValue = analogRead(potPin);    
  Serial.println(potValue);          
  if (potValue < 442 +- X){              //~center for my pot
    motorSpeed = (potValue/15 + 5);  //pot mapped to motor but dont seem to get any different speeds by changing these
    clockwise();                     
  }
  else {                             
    motorSpeed = ((906-potValue)/15 + 5); 
    counterclockwise(); 
  }
}

void clockwise()
{
switch(_step){ 
   case 0: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, HIGH); 
   break;  
   case 1: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, HIGH); 
     digitalWrite(Pin3, HIGH); 
   break;  
   case 2: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, HIGH); 
     digitalWrite(Pin3, LOW); 
   break;  
   case 3: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, HIGH); 
     digitalWrite(Pin2, HIGH); 
     digitalWrite(Pin3, LOW); 
   break;  
   case 4: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, HIGH); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, LOW); 
   break;  
   case 5: 
     digitalWrite(Pin0, HIGH);  
     digitalWrite(Pin1, HIGH); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, LOW); 
   break;  
     case 6: 
     digitalWrite(Pin0, HIGH);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, LOW); 
   break;  
   case 7: 
     digitalWrite(Pin0, HIGH);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, HIGH); 
   break;
   default: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, LOW); 
   break;  
 }
 if(dir){ 
   _step++; 
 }else{ 
   _step--; 
 } 
 if(_step>7){ 
   _step=0; 
 } 
 if(_step<0){ 
   _step=7; 
 } 
 delay(1); 
}


void counterclockwise ()
{
 switch(_step){
 case 0: 
     digitalWrite(Pin0, HIGH);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, HIGH); 
   break;  
case 1: 
     digitalWrite(Pin0, HIGH);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, LOW); 
   break;   
case 2: 
     digitalWrite(Pin0, HIGH);  
     digitalWrite(Pin1, HIGH); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, LOW); 
   break;
case 3: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, HIGH); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, LOW); 
   break;  
case 4: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, HIGH); 
     digitalWrite(Pin2, HIGH); 
     digitalWrite(Pin3, LOW); 
   break;
case 5: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, HIGH); 
     digitalWrite(Pin3, LOW); 
   break; 
case 6: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, HIGH); 
     digitalWrite(Pin3, HIGH); 
   break;
case 7: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, HIGH); 
   break;
default: 
     digitalWrite(Pin0, LOW);  
     digitalWrite(Pin1, LOW); 
     digitalWrite(Pin2, LOW); 
     digitalWrite(Pin3, LOW); 
   break;   
 }
 if(dir){ 
   _step++; 
 }else{ 
   _step--; 
 } 
 if(_step>7){ 
   _step=0; 
 } 
 if(_step<0){ 
   _step=7; 
 } 
 delay(1); 
}

Does the program you have actually work ?

There are several things you could do to make it faster.

You current seem to do an analog read and then move one step either clockwise or anticlockwise. You speed variable seems to be irrelevant.

If you do an analog read, and the required position is quite a long way away from the current position, then you could do 5 , or 10, or 50 steps towards the required position, without stopping to make another fairly pointless and slightly time-consuming analog reading between each step. That would be one way to speed it up a bit.

It is not clear to me, whether your analog reading is an indication of speed, or an indication of the position you want to step towards.

It is also not clear, what the dir variable is for, when you have two separate functions for clockwise and counterclockwise.

And you can take the delay( ) function call out of the program.

michinyon:
Does the program you have actually work ?

Yep rotation cw and ccw depending on analogue input value.

michinyon:
If you do an analog read, and the required position is quite a long way away from the current position, then you could do 5 , or 10, or 50 steps towards the required position, without stopping to make another fairly pointless and slightly time-consuming analog reading between each step. That would be one way to speed it up a bit.

Thanks that would be cool but how would I construct that argument in C+? I dont know enough to be able to move the motor like that. Would I maybe map the analogue value from mid-point to maximum, to the clockwise steps from 0 to 7? How would this work for counterclockwise movement which would then also need to be mapped?

michinyon:
It is also not clear, what the dir variable is for, when you have two separate functions for clockwise and counterclockwise.

And you can take the delay( ) function call out of the program.

I used the "dir" boolean to know if the direction called turned true or otherwise. Could anyone tell me another way of doing this? The program doesn't work without it.
Delay function removed.

To pick up on your original question ...

You can't drive the motor at a higher voltage unless you are using a stepper motor driver board that allows you to limit the max current in the motor coils - for example the Pololu A4988.

Microstepping won't be faster - it is just more precise and it comes at the expense of reduced torque.

If your code is not fast enough to make the motor miss steps then the problem lies in your code.

...R

You can add a resistance to limit current when running it at 12V. Spec sheet says 5V and 50 ohm coils so that's .1A. At 12V you'd need to add a 70 ohm resistor. It would need to be a 1 watt resistor.

You can add a resistance to limit current when running it at 12V. Spec sheet says 5V and 50 ohm coils so that's .1A. At 12V you'd need to add a 70 ohm resistor. It would need to be a 1 watt resistor.

This is the way we did it in the old days. It has the advantage of giving 12V across the coils when they are first energized, therefore more initial torque.

Robin2:
You can't drive the motor at a higher voltage unless you are using a stepper motor driver board that allows you to limit the max current in the motor coils - for example the Pololu A4988.

Yep it is driven from a ULN2003 board that came with it. Same as most people on here use.

Robin2:
If your code is not fast enough to make the motor miss steps then the problem lies in your code.

That was exactly my original problem / question and what I have been asking for help with. If someone could give me some pointers that would be great

Chagrin:
You can add a resistance to limit current when running it at 12V. Spec sheet says 5V and 50 ohm coils so that's .1A. At 12V you'd need to add a 70 ohm resistor. It would need to be a 1 watt resistor.

Ok thanks, will look into that but it still doesn't help me optimise my code as others have managed.

All those digitalWrite()s make my eyes water. I have no idea if they provide the correct magnetizing sequence - but I will assume they do.

It seems that the value in "motorspeed" is never used. Perhaps that's for later.

It seems that it goes through either a clockwise or counterclockwise step every iteration of loop() and there is a delay of 1 millsec in both clockwise and counterclockwise.

That should give about 1000 steps per second. How fast is the motor turning?

You could temporarily comment out the analogRead stuff and the serial.print stuff to see if that makes things faster - but I doubt if it will.

I am beginning to wonder if the magnetizing sequence is correct. And that is why I use a "proper" stepper driver so I don't have to worry about that.

...R

Robin2:
All those digitalWrite()s make my eyes water. I have no idea if they provide the correct magnetizing sequence - but I will assume they do.

That should give about 1000 steps per second. How fast is the motor turning?

You could temporarily comment out the analogRead stuff and the serial.print stuff to see if that makes things faster - but I doubt if it will.

Well the motor turns and certainly looks like its right. It is in fact the same sequence others have used when achieving higher rpms.

I havent measured actual rpm but I have achieved much higher speeds using the #AccellStepper library so have a relative speed to compare with.

Removing analogue read etc doesnt speed anything up.

michinyon:
If you do an analog read, and the required position is quite a long way away from the current position, then you could do 5 , or 10, or 50 steps towards the required position, without stopping to make another fairly pointless and slightly time-consuming analog reading between each step. That would be one way to speed it up a bit.

This is exactly what I was hoping for but not sure how to achieve it. The only way I can think of doing it is to give each "cw instance" (_step 0 - 7) a value of 1 and then somehow map the analogue value of the pot and use it to move the corresponding number of steps with a limit. So if my max pot value is ~900, make this the same as ~20 "instances" of the clockwise routine. But Im not sure if this will move any faster just give it a target value to move towards...

Fundamentally that motor is a slow one as the winding impedance is high - this means the
inductance will limit the rate of current change which limits how fast you can sequence it.

However you can increase the performance with constant-current drive (such as has
been mentioned with the resistor and higher voltage supply. Since its a 5 wire unipolar
motor then chopper-drivers aren't an option, so the only other techique to improve
speed is to use a constant-current source at a high voltage (better than just the
resistor as the full voltage is available until current reaches the programmed level).

So a constant current circuit programmed to 0.1A and running from 12 or 24V might be
good.

Some constant current circuit ideas (though I haven't examined them closely):
http://www.talkingelectronics.com/projects/200TrCcts/200TrCcts.html#15

GreyE30:
I havent measured actual rpm but I have achieved much higher speeds using the #AccellStepper library so have a relative speed to compare with.

This is the important bit. If the existing physical arrangement runs faster with AccelStepper then all the advice about voltages is beside the point.

What is AccelStepper doing better than your code? One way is to examine the AccelStepper code.

If I had to place a small bet I would say that something is wrong with your magnetizing code and (for example) it is only moving on every second or fourth step.

One test would be to put a long delay (2 or 3 seconds) at the end of each step so that you could see if each one has an effect.

...R

also benchmark you loop code with the folowing code :

//Declare these in the global variables part
unsinged long startTime;
unsinged long stopTime;


//Put This in the loop
startTime = millis();

/* RUN SOME  LOOP CODE */

stopTime = millis();
stopTime = stopTime - startTime;
Serial.println(stopTime );

You might want to put your serial baudrate at 115200 to make sure you are not waiting on serial communication.

I'm not into stepper motors but full stepping involves just 4 sequences as far as i know:
http://zone.ni.com/devzone/cda/ph/p/id/247

Robin2:
This is the important bit. If the existing physical arrangement runs faster with AccelStepper then all the advice about voltages is beside the point.

What is AccelStepper doing better than your code? One way is to examine the AccelStepper code.

Right, I have done some testing / timing of this motor using different working codes. I will post the results below.
For each test I have used the same 6V supply and ULN driver board. From what I have read there are different ways of sequencing these motors and each yields different results. There is some useful information in the following threads.

http://forum.arduino.cc/index.php?topic=85335.0

http://forum.arduino.cc/index.php?topic=183926.0

deleenheir:
You might want to put your serial baudrate at 115200 to make sure you are not waiting on serial communication.
I'm not into stepper motors but full stepping involves just 4 sequences as far as i know:
http://zone.ni.com/devzone/cda/ph/p/id/247

Changing this has made the single biggest difference so far, thank you...

As per my comments above regarding sequencing

Ok so here are three different codes I have trialed with this motor and corresponding results / problems / comments.
Constants throughout have been voltage (6V) and driver board. Also all run from an Uno.

1st - Using AccellStepper - Achieved ~20rpm

This is fine but I cannot get the motor to run in reverse at all. Has anyone got any experience with this?

#include <AccelStepper.h>

#define motorPin5  8     // Blue   - 28BYJ48 pin 1
#define motorPin6  10     // Pink   - 28BYJ48 pin 2
#define motorPin7  9    // Yellow - 28BYJ48 pin 3
#define motorPin8  11    // Orange - 28BYJ48 pin 4
                        // Red    - 28BYJ48 pin 5 (VCC)
#define FULLSTEP 4

AccelStepper stepper2(FULLSTEP, motorPin5, motorPin7, motorPin6, motorPin8);

#define HORIZ_IN A0

const int Xdeadzone = 3;

void setup(){
  stepper2.setMaxSpeed(1000);
  Serial.begin(115200);
  stepper2.moveTo(907);
}

void loop()
{
  int X = analogRead(HORIZ_IN);
 if (X >= (443 + Xdeadzone))
  { 
   stepper2.moveTo(X);
   stepper2.setSpeed(700); 
   stepper2.run();
  }
}

2nd - Using a borrowed version from the link above - Achieved ~15rpm

Havent tried this in reverse but looks straight forward enough and only energises 1 coil at a time! Same sequence as mine.

int motorPin1 = 8;
int motorPin2 = 9;
int motorPin3 = 10;
int motorPin4 = 11;

int delayTime = 2;

void setup() {
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
}

void loop() {
  digitalWrite(motorPin1, HIGH);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  delay(delayTime);
  digitalWrite(motorPin1, LOW);
  digitalWrite(motorPin2, HIGH);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  delay(delayTime);
  digitalWrite(motorPin1, LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, HIGH);
  digitalWrite(motorPin4, LOW);
  delay(delayTime);
  digitalWrite(motorPin1, LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, HIGH);
  delay(delayTime);
}

3rd - My code with increased Baud rate - Achieved ~12rpm

int Pin0 = 10; 
int Pin1 = 11; 
int Pin2 = 12; 
int Pin3 = 13; 
int _step = 0; 
boolean dir = true;// gre
int motorSpeed = 0;     //variable to set stepper speed
int potPin = 2; 	//potentiometer connected to A2
int potValue = 0; 	//variable to read A0 input
const int X = 10, Ydeadzone = 3; // slack for pot values
void setup() 
{ 
 pinMode(Pin0, OUTPUT);  
 pinMode(Pin1, OUTPUT);  
 pinMode(Pin2, OUTPUT);  
 pinMode(Pin3, OUTPUT);  
 Serial.begin(115200);
} 
void loop(){
  potValue = analogRead(potPin);     // read the value of the potentiometer
  Serial.println(potValue);          // View full range from 0 - 1024 in Serial Monitor
  if (potValue < 800 + X){               // 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 = ((906-potValue)/15 + 5); //scale potValue for motor speed
    counterclockwise(); //go the the cw rotation function

So there it is, nothing concrete other than my code seems slow.

According to other posts linked in this thread other people have achieved ~35rpm. I would be really interested to know how they did this or have some pointers as to what I could do to achieve the same.
TBH I would be happy with ~20rpm using the AccellStepper code if I could run the motor / sequence in reverse. I do not know how to do this however so any help would again be appreciated.

What are the differences (if any) if you get each code to do, say, one step every 2 seconds?

I haven't read all of this document but it looks to have a lot of useful info.

http://www.st.com/st-web-ui/static/active/en/resource/technical/document/application_note/CD00003774.pdf

...R

Robin2:
What are the differences (if any) if you get each code to do, say, one step every 2 seconds?

Im not sure what youre looking for by doing this. If I call it to move one step every two seconds then it will only move 1 step? Or are you trying to look at any difference in step size?

I suspect that your code won't cause it to move a full step with every call to the step function because I suspect the magnetizing instructions may have errors. You will only be able to test this with a step sequence that is slow enough to see and think about - for example to manually count steps for a quarter revolution.

Did you find any corroboration for your system in the doc I linked to? From a very quick glance it looks as if there should only be 4 magnetizing steps for normal full-step motion.

...R