Monday, 17 October 2011

Interesting servo hack

After reading a few other articles about hacking servos to make them rotate continuously, I noted quite a few people suggested disconnecting the rotary pot and replacing with a voltage divider made up of 2 x 2k resistors (in a sort-of Y shape)

Our servo is much too small to fit extra components inside it, but without these, it doesn't seem to work. It spins in one direction, by sending a repeating 1ms pulse every 20ms, but increase the pulse length to 2ms and instead of running in reverse, the servo just judders and makes a nasty noise.
(if the servo is "centred" at 1.5ms, a pulse of 1ms tells it to move 90 degrees in one direction, and a pulse of 2ms tells it to move 90 degrees from centre in the other direction. By removing the pot input, the servo never knows where the head is so should continue to move).

Removing the pot seems to have caused the problem.
To simulate the pot, we soldered a piece of wire from the board where it was connected to the potentiometer (we should have snipped the end nearest the pot, not nearest the board and we could have re-used the bit of wire without extra soldering!) and connected to a voltage divider on the breadboard, made up of two 2K pots

This sort-of solved the problem, but not quite.
The servo no longer judders, but it's not not right. With a pulse width of 1ms, the servo runs at full speed in one direction. A pulse width of 2ms makes it run in the same direction, only much slower.
Maybe this is because we're using power and ground on either side of our voltage divider, rather than taking them off the internal pot (but then to get at those wires would require completely dismantling the servo!)

Now we could play about with the resistor values, or maybe even replace them with an exterior potentiometer (with the wire from the servo connected to the wiper and each end connected to power and ground) but here's a thought....

Pulling the signal on the wire to ground causes the servo to run at full speed in one direction.
Pulling it to power (5v in our case) causes the servo to run at full speed in the other direction.
So now we have a binary method of moving the servos either forwards or backwards - simply connect this new wire to an output pin and pull it high to drive the servo in one direction, and pull it low to drive it the other way.

interestingly, we did have to make some changes to the servo control values to get the servo to work consistently without juddering. When pulling the signal pin high, we set the servo signal length to zero. When pulling the signal pin low, we set the servo signal length to 2ms. This resulted in the servo running at full speed in opposite directions. (using a signal length of 1ms and pulling low occasionally caused juddering so we ditched it!)

The downside to this approach, of course, is that we don't have a way of programmatically stopping the servos from turning. For our little robots project, we'll live with this restriction.
It's a fine balance between coming up with a project that can be completed inside a couple of hours but still demonstrates "hardware hacking" and spending all day perfecting servo controls which probably wouldn't get used!

Anyway, this is to document how we hacked our micro servos to allow us to create little tiny robots, instead of the big monstrosities normally associated with continuous rotation servos ;-)

For anyone interested, here's the Oshonsoft BASIC PIC code for our servo test board:

Define CONF_WORD = 0x3f18

       Dim servolen As Word

       Config PORTB = Input
       Config PORTA = Output
       OPTION_REG.7 = 0 'pull-ups on PORT inputs
       While PORTB.0 <> 0
              'do nothing: we have to press the button to start

       'button controls direction
       'connect the extra wire from the servo
       'to pin PORTA.1
       If PORTB.0 = 0 Then
              High PORTA.1
              servolen = 0
              Low PORTA.1
              servolen = 2000
       'servo control signal
       'for our hacked servo, signal length doesn't matter
       High PORTA.0
       WaitUs servolen
       Low PORTA.0

       WaitMs 15

Goto loop