I am using the <Servo.h>
library on my Arduino Uno for driving an MG996R servo motor. What I am looking for is a way to make an 'emergency stop' while the servo is rotating (servo.write(...)
). Unfortunately there is no stop()
method. Does anyone have any suggestions on how I could achieve this?
servo.detach()?
Please post the code, in code tags.
May depend on what it is you want:
a) if you want the servo to go limp, you could probably do what @DaveX mentioned
and do servo.detach().
b) if you instead you want it to stop and hold at current position, you might try something
like:
myServo.writeMicroseconds(myServo.readMicroseconds());
typed on fly so probably bugs...
The idea is to ask the servo code for it's current position and tell it to stop there.
Probably in this case won't do much good, as these servos and this library is pretty primitive, so the read will simply return your last write position...
Instead you probably need something in your sketch that detects the need for the stop, and
while in the logical stop condition, have your code not output any new positions:
(write or writeMicroseconds)
I'm fairly sure that's the case. So you'll need to arrange that you move the servo in the smallest possible steps and check before every move for emergency stop.
You might find a stop function in a more sophisticated library.
However, common advice is that you don't do emergency stop in software anyway - hardware is preferred in the form of a big red cutout switch
I tried servo.detach()
, but that does not work. The servo.write()
blocks and the servo.detach()
does not take effect until servo.write()
has terminated. I tried your suggestion — thanks for that — and included a test in a sketch I made to have more control over the rotation speed.
void servo_run(int angle, int delta) { // move MG996R's shaft to angle°
int incr = 1;
int curr_angle = servo.read();
if (curr_angle > angle) incr = -1;
// from to curr_angle° to angle° in steps of 1 degree
for (int pos = curr_angle; pos != angle; pos += incr) {
if (digitalRead(panicPin)) return;
servo.write(pos);
delay(delta);
}
}
Servo.write() does not block.
If you try:
myservo.write(0);
delay(500);
myservo.write(180);
myservo.write(0);
myservo.write(180);
myservo.write(0);
myservo.write(180);
myservo.write(0);
myservo.write(180);
myservo.write(0);
It won't block until it reaches 180° and 0°, swinging to and fro 3x, it will retarget instantly without reaching it target and stay at 0°.
Whether the servo goes to sleep when a servo.detach() happens depends on the actual servo.
If you need an actual emergency stop, cutting the power to the servo is the best solution. Limit switch or maybe a high-side switch on the servo power line.
Take a look at this, try stuff out in Serial.
https://wokwi.com/projects/413996154742768641
Reminder: never use Arduino +5V pin to power a servo as depicted in the linked Wokwi.
Based on Adafruit's example here
https://learn.adafruit.com/multi-tasking-the-arduino-part-1?view=all
I could be wrong, but I don't think the Servo code hangs in the write method? I believe it simply maps the angle to microseconds and updates a table item with the new value.
And the Servo code knows nothing about what position the servo is actually in.
In that, if your servo is currently at 0 degrees and you do a servo.write(90);
It simply outputs the pulsewidth in microseconds to that servo that is computed from
the angle. And the normal Analog servos will try jump to that position as fast as they can, but for example if they run into an obstacle at let's say 45 degrees, there is no way for the servo library to know that, and if you do a servo.read() it will return 90...
A long time ago (> 12 years), I hacked up my own version of the servo library
( Arduino_Phoenix_Parts/ServoEx at master · KurtE/Arduino_Phoenix_Parts · GitHub)
Which emulated the Lynxmotion Servo Controller (SSC-32) in that you could tell multiple servos the new positions you want them to move to, and how long you want this new group move to take, and it computed how much each servo should move per servo cycle, such that they all arrived at their new positions at the right time... Since then, when I do something using servos, I now use smarter servos like ones from Robotis (Dynamixel)...
As mentioned by others, cutting the power to the servo is probably the best thing.
You might also be able to do it by doing something like having a hardware buffer the UNO pin and the servo for the signal, with an enable pin, and control if the signals are sent or not to the servo. However, what this might do, depends on which servos you use. That is simple analog servos will go limp if they don't receive a signal. Some digital servos, will continue to hold their last position...
Thanks for all your suggestions! One thing I also learned from one of the links is that I should avoid using delay()
. but use millis()
instead. And about cutting the power: I might also use a relay for that, I guess.