MicroBahner/MobaTools

Stepper rotates in different direction seemingly randomly (how to force rotation direction?)

Closed this issue · 8 comments

Hello, I am using your library to control a stepper motor. While testing I encountered an issue that I've not been able to solve so far: instead of always moving CCW, some movements are CW. Is there a function to set the movement's direction? I could only find it in stepper.rotate, but I need to set specific angle values (with stepper.write).

Can you show a sketch that demonstrates the problem? The direction of movement is defined by the difference of the actual position and the target position. Be aware, that a stepper cannot exactly reach every angle because of its stepping resolution. So stepper.write may sometimes be an approximation only.

Down here are the two functions I've written.

void moveToAngleAbs(MoToStepper & stepper, float angle){
  stepper.setSpeedSteps(MOVESPEED);
  stepper.write(angle);
}

void moveToAngleRel(MoToStepper & stepper, float angle){
  long pos = stepper.read() + angle;
  stepper.setSpeedSteps(MOVESPEED);
  stepper.write(pos);
}

The loop sets the stepper to move from 120, to 250 then to 360.

With moveToAngleAbs it goes from 120 to 250 to 360 CCW, then goes back to 120 CW. I suspect because 360 < 120, it moves the other way around. This is not allowed in my use case, it must always rotate in the same direction.

With moveToAngleRel it always moves CCW, but I'm afraid
long pos = stepper.read() + angle;
will overflow sooner or later, since this motor will run indefinitely.

The angle in stepper.write is not limited to 360°. So if you go from 360 to 120 it will move backwards. Or you have to go to 480°.
Angles are converted to steps internally and this will overflow after 2,147,483,647 steps. How many turns that are, depends on the steps/rev.
What you can do is execute a stepper.setZero() after the stepper reached 360°. Then it will start over with 0° at every turn.

Which is exactly what moveToAngleRel does, adding 120 to 360 and it works without fail.
With my current configuration, it would overflow after 536.870 revolutions, which means about 35 days of uptime before it overflows. Not enough for the use case I have in mind.

While I could call setZero, I can only call it once I reached the endstop. Currently I'm doing something like this

moveToAngleRel(stepper, 100);
while(stepper.moving());
stepper.setSpeed(MOVESPEED/2);
stepper.rotate(1);
while (!digitalRead(endstopPin));
delay(20);
stepper.stop();
stepper.setZero();

but the motor is running at pretty high speed and does not stop gracefully at the endstop.

Hmm ... I'm not quite shure about your last comment.
Executing stepper.setZero at 360° has nothing to do with homing and limit switches. You can execute stepper.setZero at any time. And if you do it when the stepper reaches 360°, the next move to 120° will go in the same direction, because it starts again at 0°. This is not the same as your moveToAngleRel becaus the internal stepcounter is reset. So you can do that endless without overflowing.

Of course it's not a library limit, it's in my project's use case. The zero position MUST always be at the endstop, which means I need to get to the endstop and then call setZero. This would make moveToAngleRel obsolete, as every move would then become relative to 0 (the endstop).

Say the motor skips a couple of steps, 360 won't be 360 anymore. I need to be sure the endstop has been touched.

EDIT: noticed an error in my code

moveToAngleRel(stepper, 100);
while(stepper.moving());
stepper.setSpeedSteps(MOVESPEED/2); //was stepper.setSpeed()
stepper.rotate(1);
while (!digitalRead(endstopPin));
delay(20);
stepper.stop();
stepper.setZero();

That wrong call caused the speed to be off and the motor would either overshoot or undershoot at the endstop. Changing the code to

moveToAngleRel(stepper, 100);
while (stepper.moving())
  ;
home(stepper, endstopPin);

where the home function is

bool home(MoToStepper & stepper, uint8_t endstopPin, int speed = HOMESPEED, unsigned long timeout_ms = 20000) {
  unsigned long start = millis();
  bool complete = true;
  stepper.setSpeedSteps(speed);
  stepper.rotate(1);

  while (!digitalRead(endstopPin) && (complete = (millis() - start <= timeout_ms)))
    ;          
  delay(70);
  stepper.stop();
  stepper.setZero();
  return complete;
}

made the motor stop gracefully and in a repeatable manner, while also resetting the zero position at each 360 rotation

Does it now work the way you want it to? Can we close the issue?

Yes, I'll close it now