Driving continuous-rotatation-modded servos...

So, relatively close to where I live, is opening a new hacker space, The Hacker Dojo. I went to Their open house, bearing food, a handfull of completely empty Freeduino PCBs (mostly of the 0603 SMT version that has never quite seen the light of day, and probably won’t. But it did work), and a few misgivings about whether anyone in that particular hacker community would even know what an Arduino was. Whatever; I had other things to do too.

I need not have feared; the PCB was happily accepted and shown around, and one of the other attendees (“Tim”) piped up: “Did you bring any WORKING arduinos? I have a servo motor (one of those $3.50 micro servos) that I’ve just modded for continuous rotation, and I need to see if the Arduino can work with it, but I didn’t bring my actual arduino…” Well, of course I had a real arduino (and laptop with the IDE), and it seemed a FINE project to spend some time on at such an event.

After some minor struggling with the fact that this particular laptop had never had an actual arduino connected to it, and needed the FTDI drivers installed, we quickly had my arduino running BLINK. A quick raid on the (infant but growing) HW lab at the dojo yielded an unmodified full-sized servo plus appropriate jumpers, and in moments we had the SWEEP demo program moving the servo arm around. (Then we paused for the Welcoming Speeches.)

The same unmodified SWEEP did in fact work just fine to drive the MODIFIED servo in both directions; one way for part of the sweep and the other way for another part. The BIG question was whether we could make it STOP by finding and sending the appropriate middle value pulse. SWEEP was modified to run slower, and to print out the values it was using, so we could track when the servo seemed to stop. The middle value in this case was “about 80.” Given THAT info, we narrowed the loop further, trying to find an exact “stopped” value…

This however, failed. On this particular servo, the best we could do was to have the motor move VERY SLOWLY in one direction or the other. While this raised interesting possibilities for continuous rotation at multiple speeds, it didn’t solve the “stop” problem. Noting that the servo didn’t seem to move at all when there was NO signal, we decided to see if we could use a digitalWrite() to set STOP, and return to servo.write when we wanted it to move again. This didn’t quite work either; once PWM was fully stopped (digitalWrite turns off PWM on the appropriate pin), servo.write is insufficient to turn it back on. However, servo.attach is simple code that doesn’t do anything non-repeatable, and adding that back in to the forward/backward code gave us an example that did everything we wanted. Here it is. (This is also an example of how you can read from the PC-side (serial) without having to WAIT for an available character…)

// cont_servo by WestfW
//    Based on the "Sweep" example program
//    by BARRAGAN <http://barraganstudio.com> 

#include <Servo.h> 

Servo myservo;  // create servo object to control a servo 
// a maximum of eight servo objects can be created 

void setup() 
{
  pinMode(9, OUTPUT);
  digitalWrite(9,0);
  delay(1000);

  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
  Serial.begin(9600);
} 


void loop() 
{ 

  switch(Serial.read()) {
  case -1: // -1 is the normal case where there is no data.  Just keep going
    break;
  case 'f':
  case 'F': // Forward
    myservo.attach(9);  // Possibly restart the servo PWM
    myservo.write(50);  // a number much less than center
    break;
  case 'B':  // Backward
  case 'b':
    myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
    myservo.write(120); // a number much higher than center
    break;
  case 's':  // Stop
  case 'S':
    digitalWrite(9,0);  // Note that digitalWrite will turn OFF any PWM.
    break;
  }
  delay(1000);                       // waits 15ms for the servo to reach the position 
}

Hi westfw,

I'm trying to do exaclty what you describe in your post. I need to find a way of "killing" the pwm in order to stop the rotation of a continuous (modified) servo (It's a Futaba S148). I tried your code and work's fine when I send 'F' and 'B' commands but nothing happens when I send 'S'. I don't have an oscilloscope to check the pwm signal but using a LED I noticed that after sending 'S' the servo pin (9) is not LOW because the led keeps turned on. Any suggestions?? Thanks!

What does "nothing happens" mean? What LED? Can you send a diagram or photo of how you have things hooked up?

The first thing I'd do to debug this is add a "Serial.print("stopping\n");" before the digitalWrite(), so that you confirm the code is actually going there...

Are you powering the servo from the arduino's 5V output? Since you're using a full-sized servo rather than the micro version I did the initial experiments with, one possibility is that you're using too much power and causing the arduino to reset periodically (another Serial.print into setup())

I don't think a servo is guaranteed to stop when there is no signal on the pin. I mean, it sounds like reasonable default behavior in the original application(s), and it worked on the servo I was using, but it is possible that it won't always work with all servos...

Did you leave the origional pot connected to the servo, or did you replace it with a pair of resistors? The pots are really hard to keep properly adjusted. Replacing the prigional pot with a 15 turn trim pot may also help. My continous rotation servo generally stops when there is no PWM supplied to it, but it will not try to hold its current position.

Why not just "detach" to stop the servo?

Thanks for the replies.

What does “nothing happens” mean? What LED?

It means that the servo keeps moving even when I send ‘S’. It doesn`t stop.

What LED?

A LED that I connected to pin 9 to test if when I send an ‘s’ the pwm signal is turned off (didn’t happened, the LED keeps turned on). This LED test showed me that the digitalWrite(9, 0) was not working.

The first thing I’d do to debug this is add a “Serial.print(“stopping\n”);” before the digitalWrite(), so that you confirm the code is actually going there…

Didn’t do that. I agree that it’s the way of start debuging.

Are you powering the servo from the arduino’s 5V output? Since you’re using a full-sized servo rather than the micro version I did the initial experiments with, one possibility is that you’re using too much power and causing the arduino to reset periodically (another Serial.print into setup())

Yes, I’m powering from arduino’s 5v output. Power is not a problem because I have used this servo previously whith no problems and I always powered it from arduino.

I don’t think a servo is guaranteed to stop when there is no signal on the pin. I mean, it sounds like reasonable default behavior in the original application(s), and it worked on the servo I was using, but it is possible that it won’t always work with all servos…

I agree, the servo is not ment to work properly in this conditions; it wasn’t made for being used like this. But fortunately it works!

Did you leave the origional pot connected to the servo, or did you replace it with a pair of resistors? The pots are really hard to keep properly adjusted. Replacing the prigional pot with a 15 turn trim pot may also help. My continous rotation servo generally stops when there is no PWM supplied to it, but it will not try to hold its current position.

I left the pot. Then I generated a pwm signal of 1,5 ms duty cycle and adjusted the pot untill the servo stopped. Replacing the original pot for a turn trim pot would be nice; the original pot can be adjusted but not very accurately.

Why not just “detach” to stop the servo?

Because this servo is only a part of a system and it’s not an option to require someone to disconnect the servo every time it has to stop.

Last night I decided to try without the servo library, just generate the pwm signals (using digitalWrite) that I need to turn forward and backwards the servo and put the signal LOW for stopping, as westfw suggested. That worked fine.

I observed that the servo’s speed is quite different when moving forward and backwards. What I say is that a pwm signal which has a duty cycle 0,1 ms greater that the center position (1,5 ms), corresponds to a forward rotational speed very different that the produced by a signal which has a duty cycle 0,1 ms smaller that the “center position”. I was not expecting this sort of “non lineal¡rity”, but in this case it does not affect my project.

For acheiving accuracy you will have to try different combinations of puse widths and amout of cycles of the pwm signal that you provide to the servo. Hope this helps. Here is the code that I’m using right now (sorry but comments are in spanish). It’s based on westfw’s. Thank for your help!

int angulo = 7; //es el tiempo que mantengo la señal => determina el ángulo que se mueve el servo (el "paso"). No tiene el mismo efecto para los dos lados
int alejamiento = 200; //es el alejamiento del "punto cero" = 1500 us

void setup()
{

  pinMode(9, OUTPUT);
  Serial.begin(9600);
  digitalWrite(9, 0);
   
}

void Antihorario(){
for (int i=0; i <= angulo; i++){
  
   digitalWrite(9, 1);
   delayMicroseconds(1500+alejamiento); //tiempo que el pulso está en alto
   digitalWrite(9, 0);
   delayMicroseconds(20000-(1500+alejamiento)); //tiempo que el pulso está en bajo
 } 
}

void Horario(){
for (int i=0; i <= angulo; i++){
  
   digitalWrite(9, 1);
   delayMicroseconds(1500-alejamiento); //tiempo que el pulso está en alto
   digitalWrite(9, 0);
   delayMicroseconds(20000-(1500-alejamiento)); //tiempo que el pulso está en bajo
 }   
}

void Frenar(){
for (int i=0; i <= 1; i++){
  
   digitalWrite(9, 1);
   delayMicroseconds(1500); //tiempo que el pulso está en alto
   digitalWrite(9, 0);
   delayMicroseconds(18500); //tiempo que el pulso está en bajo
 } 
}

void loop()
{

  if (Serial.available()>0)
   {
  
  switch(Serial.read()) {
    
  case 'a':
  Antihorario();   
  Frenar();
  break; 
  
  case 'h':
  Horario();
  Frenar();
  break;  
  }  
}
}

Because this servo is only a part of a system and it's not an option to require someone to disconnect the servo every time it has to stop.

This I don't understand - I didn't say "disconnect", I said "detach" which is one of the library methods. Simply re-attach when it has to start - make it part of the motion methods. What's the problem?

Sorry Groove, I missundertood you.

Looking at the description of the detach() method it seems that it would do the job. Thanks.

No worries - did you look at the writeMicroseconds method in the class, to allow finer control of pulse widths?

Yes, I used it in my code.

I just use an analogWrite command to drive my continuous rotation servos and don't use the servo library. To stop them I just use a:

digital.Write(servoPin,LOW)

linusb, standard hobby servos should not be driven from the output of analogWrite. I posted the results of some tests in this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1249178408/29#29

what servo are you using?

I'm using hitec 311's. I'm nit getting any strange behavior and noises are ok. I'll mess with this week and change the code to using the servo library and see how that goes.