Can the Servo lib be made to control more than 12 servos per timer? Maybe!

Hey guys,

I was taking a look at the Servo() lib, wondering why it was limited to only 12 servos per timer, and though I'm not certain, it seems like there may be room for improvement. (And that is an understatement.)

First, let's discuss how servos work, and why 12 servos per timer is the magic number here.

Servos work by keeping track of the length of time between pulses on their signal line. To keep them in a particular position, every 20 milliseconds, you need to set their signal pin high for between 1 and 2ms. A pulse of 1.5ms, with a low period of 18.5ms, will keep the servo centered. These pulses need to be sent constantly or the servo will stop trying to center itself and move freely instead.

So how does the Servo() lib send these pulses? Well, it looks like it uses the interrupt to time the pulse for only one servo at a time. So, for servo one, it sets the servo's signal pin high, then sets the interrupt to trigger when it should go low again... up to 2ms later. It then moves on to the next servo and does the same thing. The limit on the number of servos comes from the amount of time you have between when you need to send pulses. And the Servo() lib seems to play fast and loose with the standards, allowing up to 24 milliseconds between pulses instead of the usual 20, which give enough time for 12 pulses up up to 2ms each before it needs to repeat the first.

This seems really inefficient to me however.

It seems to me that every 20ms, one could toggle all the servo pins high at once and then turn them off one by one as needed when a particular servo's time is up. Using some sorted arrays could make deciding on how long to wait to trigger the next interrupt very fast. In this way, one could control as many servos as they wanted from a single interrupt. I see no reason why you shouldn't be able to control 64 of them in this manner with just one timer interrupt.

Anyway I'm interested in hearing your thoughts on this. I'm not going to have time myself to test this theory out for a while, and I don't really need more than 12 servos myself, but if one were developing a spider-bot or something and one didn't want to waste their precious timers or use a MEGA, then the ability to control 20 on a standard Arduino might come in handy.

if this lib can control up to 20 servos,then it will be more easy to do a Biped Robot XD

Hi,
Here are two libraries, the first will get you 20 servos from 4 pins, the second will drive a servo from every Arduino UNO pin upto 18 Servos.

The interface to this one is ugly, but the philosophy is - if you asked me to do something you had a good reason. The ugly part is that it does not enforce a fixed refresh rate and so if you so not set one it will pulse your servos at much higher frequencies - some people want this ability -

If your interested in using either of them let me know, they are well tested, I consider them reliable but they could be better documented.

Here is the second library in action with some PPM reading code also included in the library -

Duane B
rcarduino.blogspot.com

If I'm not mistaken those libs of your require an additional chip to drive the servos. I'm talking about driving them with software.

You are mistaken, second library puts a servo on any pin, upto 20 pins - 18 is more pratical though as it allows you to keep serial comms - no additional hardware required.

For the other library, the additional chip costs about 30 cents, a bargain for so many servos from so few pins.

Duane B

It seems to me that every 20ms, one could toggle all the servo pins high at once and then turn them off one by one as needed when a particular servo's time is up.

That's how I'd do it.


Rob

That's how I'd do it.

No, you would not.

the reason it is not done this way is that you would need to loop through the servos at every pin change to see who should go next, with 20 servos that would be a lot of 16 bit operations.

alternativley you could try and sort the servos at the start of each frame but again lots of 16 bit operations.

The current servo library uses around 1% of arduino processing power for 12 servos, my library is more efficient but has a less friendly interface - I would only consider using it if you want those extra servos, a higher refresh rate or smoother RC signal interrupts.

Duane B

DuaneB:
the reason it is not done this way is that you would need to loop through the servos at every pin change to see who should go next, with 20 servos that would be a lot of 16 bit operations.

You are mistaken.

You see this?

That is a circuit I built, which has an array of 64 multiplexed LEDs. Nothing special about that you might think, and I wouldn't blame you since the video doesn't demonstrate it very well, but if you look closely, you may notice the LEDs that aren't brightly lit are displaying a gradient. That's pulse width modulation. On a multiplexed display. How did I achieve this? By sorting the LEDs so as I illuminated each column of the display I needed only check the time against the next LED to turn off.

alternativley you could try and sort the servos at the start of each frame but again lots of 16 bit operations.

There's no need to sort the servos each frame as long as you keep your list sorted. When you set a new angle, all you need to do is a quick binary search to the left or the right of where the previous position was in the list to figure out where to insert the value, and you're done. Yes, this requires a few operations. But setting the servo position doesn't need to be super fast. Servos can't respond within microseconds to a change in position anyway. Where speed is important is in the servo interrupt, and that would be super fast with the list already sorted for it.

Fair enough - as arduino is single threaded and you only need to re sort if a servo position changes, you can safely figure out the sort order with interrupts still enabled and then just disable them to do the update - should be smooth and simple.

Duane B

This seems really inefficient to me however.

The advantage of that is that it is inherently PPM, if you output the pwm pulses on the same pin.

The disadvantage is that those pwm pulses are not phase aligned and there are channel limitations.

It is not hard to write pwm output on any arbitrary number of channels. I think I provided one example somewhere. Basically you establish a counter that counts up to a the period. At each increment for the counter, you test for overflow and output pins; At the roll-over of the counter, you re/set all pins.

I doubt very much that you could get anything like ppm out of the standard servo library. Your welcome to prove me wrong with a demostration though.

Duane B

No, you would not.

Yes I would, but on a processor that has enough RAM to have a large lookup table of values to write to the port(s).


Rob

Hi Graynomad,

If you look at the response after mine from scswift, he is quite right, its trivial to sort the servos whenever the angle is updated and then just let the ISR CYcle through a pre sorted table.

The next problem is if you have 20 servos at very similar values, Do you stay blocking in the ISR to set all 20 of them ? What about if each is set to a value 1, 2, 3 or 4 microseconds more than the last ?

Its these boundary cases that will determine if the idea can work.

Duane B

just let the ISR CYcle through a pre sorted table.

Yep, sounds reasonable.

The next problem is if you have 20 servos at very similar values, Do you stay blocking in the ISR to set all 20 of them ?

What resolution are we talking?


Rob

5us makes the gears in the servo jiggle around, 10us is a noticable tick at the output shaft. An ISR is going to be around that mark so for 10 or more servos all within 50us of each other, the simultaneous servos approach is going to have a problem - my guess is thats the reason that current approaches are sequential rather than having a simultaneous rising edge.

Duane B

Thanks Duane, I'm thinking of doing a servo controller (maybe even 32 channel) in the next few months so the above will be useful.

I've seen controllers with 1uS resolution and wondered if that was really necessary, seems like it wouldn't do any harm.


Rob

Hi,

My libraries get upto 20 servos by having two banks of ten, one on OCR1A and the other on OCR1B. I had considered interlacing the two banks on a single compare register but could not come up with a satisfactory way of handling the case where two or more servos need to toggle within a few us of each other.

This is a real problem because you either need to write a more complex isr which blocks for longer, or you risk situations where the timer will advance past the next output compare value before you are able to set it and you loose the frame. Splitting two banks of servos over the two output compare registers solves this quickly and easily at a hardware level.

Duane B