Closed Proton23 closed 4 years ago
@Proton23 what circuit are you using to drive the stepper? That stepper 28BYJ-48 is specified by its data sheet to have 50ohm coils. You cannot directly drive it from an ESP32. When driving the 5v/50ohm coil you need at least 100mA of current.
Your digitalRead()
confusion is reverse EMF. The motor is also a generator. When you step forward with one coil you induce a current in the other coils. To prove this to yourself. remove the stepper from the circuit. isolate/disconnect all wires from the stepper. Rotate the motor shaft, feel the opposing torque. Then connect all of motor wires together(short out the motor). Then rotate the motor shaft, you will have to use much more force to rotate the motor shaft.
You need to use some type of stepper motor driver circuit LM2003. On this Components101 webpage they describe how to use it. Halfway down the page in the section "How to use 28-BYJ48 Stepper Motor" they describe the pulse sequence needed to drive the motor. Your single coil at a time is not a valid sequence:
void Motor::oneStepForward() {
digitalWrite(STEPPER_PIN_1, step_number_right == 0 ? HIGH : LOW);
digitalWrite(STEPPER_PIN_2, step_number_right == 1 ? HIGH : LOW);
digitalWrite(STEPPER_PIN_3, step_number_right == 2 ? HIGH : LOW);
digitalWrite(STEPPER_PIN_4, step_number_right == 3 ? HIGH : LOW);
Serial.println(digitalRead(STEPPER_PIN_1));
Serial.println(digitalRead(STEPPER_PIN_2));
Serial.println(digitalRead(STEPPER_PIN_3));
Serial.println(digitalRead(STEPPER_PIN_4));
step_number_right++;
if (step_number_right > 3) {
step_number_right = 0;
}
Serial.println(5);
}
That unipolar stepper needs an eight step sequence.
const uin8_t STEPSEQ[8] = {14,12,13,9,11,3,7,6}; // binary encode bit0 = stepper coil1. 0..7 for right, 7..0 for left.
uint8_t step_number = 0;
const uint8_t COILPINS[4] ={STEPPER_PIN_1,STEPPER_PIN_2,STEPPER_PIN_3,STEPPER_PIN_4];
void Motor::Step( uint8_t direction) { // 0==right, 1 == left
if(direction == 0 ) {
if(step_number > 6 ) step_number = 0;
else step_number ++;
}
else {
if (step_number ==0 ) step_number = 7;
else step_number--;
}
for(uint8_t a = 0; a <4; a++){ // a = coil pin
digitalWrite(COILPINS[a], STEPSEQ[step_number]& (1<<a));
/*
'1<< a' does a binary left shift of 1 'a' places, so:
if a==0, 1<<0 = 1 or 0b0001
if a==1, 1<<1 = 2 or 0b0010
if a==2, 1<<2 = 4 or 0b0100
if a==3, 1<<3 = 8 or 0b1000
'&' anding with STEPSEQ[] produces a '1'(HIGH) if the matching bit is set for that coil/step combo.
*/
}
}
Chuck.
Hello Chuck,
thanks for your quick reply. I forgot to mention that I use uln2003 as driver. My Setup contains also a photo sensor but not is not in use to determine the error. All tutorials about the 28BYJ-48 I found, used 4 steps and everything works fine when it's in the ino-file. I don't get why the sequence is invalid only when the code is in an object class.
Nevertheless I will give it a try this evening and let you know. Thank you very much.
Best regards Antonio
Hello Chuck, it work's but actually it rises more questions than it answers, at least for me. Is there a way to make as fast as with 4 steps?
{14,12,13,9,11,3,7,6}; // binary encode bit0 = stepper coil1. 0..7 for right, 7..0 for left.
I don't understand how you got those numbers
if(step_number > 6 ) step_number = 0;
Is the intentional or a typo? Is 7 not making more sense? If it's intentional, it's something I also don't get.
My main problem is
STEPSEQ[step_number]& (1<<a)
Let's say step_number is 2, then STEPSEQ[2] is 13 a == 0 -> STEPSEQ[step_number] & (1 << a) -> 1 a == 1 -> STEPSEQ[step_number] & (1 << a) -> 0 a == 2 -> STEPSEQ[step_number] & (1 << a) -> 4 a == 3 -> STEPSEQ[step_number] & (1 << a) -> 8
Let's say step_number is 1, then STEPSEQ[2] is 12 a == 0 -> STEPSEQ[step_number] & (1 << a) -> 0 a == 1 -> STEPSEQ[step_number] & (1 << a) -> 0 a == 2 -> STEPSEQ[step_number] & (1 << a) -> 4 a == 3 -> STEPSEQ[step_number] & (1 << a) -> 8
and I have no idea why.
Thanks in advance.
Best regards Antonio
@Proton23
{14,12,13,9,11,3,7,6}; // binary encode bit0 = stepper coil1. 0..7 for right, 7..0 for left.
I don't understand how you got those numbers
from this webpage components101 28byj-48 about half way down the page is this table:
Red is the common line, the others are the poles.
if you look at the column "Step 1" from top to bottom is the sequence 0 1 1 1. If convert that to a binary value, assign each row a value starting at 1
, so 1,2,4,8. (0 1)+(1 2)+(1 4)+(1 8) = 14.
if(step_number > 6 ) step_number = 0;
Is the intentional or a typo? Is 7 not making more sense? If it's intentional, it's something I also don't get.
I want to limit step number to the range of 0..7. When I increment it I want 0 to follow 7, and I am kinda paranoid. If I use this code:
if (step_number ==7) step_number = 0;
else step_number++;
it will work fine, as long a step_number
never gets set above 7
. else bad things happen, I am using step_number
as a index into an array of byte. The compiler assumes I know what I am doing. I defined the STEPSEQ[]
as an array[0..7]. if I tell it to access byte 8, it will! The compiler does not do range checking, It will just return the byte at the requested location, whatever it is.
if( step_number > 6) step_number=0;
else step_number++'
will never let step_number get higher than 7.
main problem is
STEPSEQ[step_number]& (1<<a)
Let's say step_number is 2, then STEPSEQ[2] is 13 a == 0 -> STEPSEQ[step_number] & (1 << a) -> 1 a == 1 -> STEPSEQ[step_number] & (1 << a) -> 0 a == 2 -> STEPSEQ[step_number] & (1 << a) -> 4 a == 3 -> STEPSEQ[step_number] & (1 << a) -> 8
the calculation gives me either zero or non_zero, digitalWrite(pin,value)
sets the output LOW
if value
is zero, else it sets output HIGH
The speed is controlled by the rate you call step()
. If you call step()
too often the motor will miss step.
I would design the motor subsystem to support move_to
and move_rate
.
I would use a timer()
callback to step()
. set the timer()
to activate every n
milliseconds. Inside the callback the logic is simple:
timer example page
// untested / uncompiled. use at your own risk :)
// volatile because `I am accessing/changing the value inside an interrupt.
volatile int currentPOS=0;
volatile int newPOS=0;
volatile int stepDirection=0;
volatile int step_number=0;
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR onTimer() {
portENTER_CRITICAL_ISR(&timerMux);
int _newPos = newPos;
int _currentPos = currentPos;
int _stepDirection = stepDirection;
portEXIT_CRITICAL_ISR(&timerMux);
if( _newPos != _currentPos) {
step(_stepDirection);
portENTER_CRITICAL_ISR(&timerMux);
if(currentPos == _currentPos) { // hasn't been changed by another thread
if(_stepDirection == 0) currentPos++;
else currentPos--;
}
portEXIT_CRITICAL_ISR(&timerMux);
}
else { // at newPos, so shutoff timer.
timerAlarmDisable(timer);
}
}
void setup() {
Serial.begin(115200);
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 2000, true); // 500hz, every 2000 microSeconds
timerAlarmEnable(timer);
}
void loop() {
if(signal){ // whatever signal or button or .. you use to control when/where to step
portENTER_CRITICAL(&timerMux);
newPos = ?
stepDirection =?
if(newPos != currentPos){
timerAlarmEnable(timer); // enable timer to do steps every 2000us until currentPos==newPos
}
portEXIT_CRITICAL(&timerMux);
}
Chuck.
Hello Chuck,
after describing how you got the numbers everything started to make sense. Thank you so much for explaining that to me.
Because I don't like to use code I don't understand, so I will have to take a look into it before deciding if I want to use it. Will the 8-step way with timer be as fast as the 4-step way?
The only thing I still don't get is: Why is the 4-step way working in the ino file but not in the class?
Best regards Antonio
@Proton23
Will the 8-step way with timer be as fast as the 4-step way?
Depends on how fast you call step()
, the ESP32 functions at 240MHz, it can call the simple Step()
function MILLIONS of times per second. The motor is a physical device, its datasheet lists its frequency as 100Hz (one hundred steps per second)
The only thing I still don't get is: Why is the 4-step way working in the ino file but not in the class?
I don't know, but since I believe your activation sequence is invalid, I am not going to spend any time figuring out why it inconsistently works.
Chuck.
Hello Chuck, I took a look into the code, but it's seems like overkill to me and I don't know if I really got it figured out totally. So I'm going to stick to your first version, that should be enough for my purpose. I also set the delay from 2 to 1 and now it seems to be as fast as the 4-step way.
Thanks a lot for helping me out.
Best regards Antonio
[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.
[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.
Hardware:
Board: ESP32 DevKit V1 Core Installation/update date: Don't know where to look IDE name: Soebler (Eclipse) Flash Frequency: 80Mhz PSRAM enabled: Don't know Upload Speed: 115200 Computer OS: Ubuntu
Description:
I want to turn a 28BYJ-48 stepper motor. The first sketch works.
first Sketch:
The second one with an object oriented approach does NOT work
second Sketch:
Motor.h:
Motor.cpp:
the output of the first sketch is something like 1 0 0 0
0 1 0 0
and so on
output of the second sketch is 1 1 1 1
1 1 1 1
and so on But I don't understand what is wrong with this code. Any help is highly appreciated.
Thanks in advance.
Best regards Proton