The Best Servo Tester Analog Circuit

Don’t get me wrong, serial servo controllers are great, but sometimes you just want a simple way to flex a servo (like, say, if you knew that your Robix servo gripper was out on a truck to be delivered to you later today). Something that doesn’t require sending serial bytes or finding two charged battery packs for an RF transmitter and receiver would be really nice right about now, so I’ve been doing a little searching.

You can spend about $150 on a fancy digital servo tester, like this one from Hitec, which is so expensive because it also serves as the programmer for their digital programmable servos. Looking at ServoCity, For $100 you can get a two-axis servo joystick, or for $40 you can lay your hands on a rotary potentiometer controlled dual servo driver. Convenient, but for that price I can get TWO Pololu micro serial servo controllers (my favorites!), and probably cover shipping (sorry ServoCity, nice try).

There are also these little guys which are ubiquitous on E-Bay:

You can “buy it now” for $4, or get one from an auction starting at $0.99, but you’ll have to pay another $9 for shipping from Hong Kong! It would almost be worth getting one to try out, but it turns out they only produce pulses in the standard 1ms-2ms range. Similar hobby kits abound, but they all seem to have the same limited range. I wanted something that could go far outside the standard servo pulse width, to push my servos to their limits.

An ATTiny25 could easily handle reading a pot and generating the pulses, but for something so basic I decided to try an analog solution instead. With a little searching you can find ~20 variations on how to use a 555 timer to generate servo pulses. They vary in complexity and part count, most use a transistor as an output inverter, and some even use two timers (the first as a one-shot to generate the actual pulses, and a second as a timer to trigger the first, keeping the period precisely constant…whatever!). By far the best one I found was in a document called Hobby Servo Fundamentals by Darren Sawicz (page 7):


Note: If you’re using Firefox, and your browser window isn’t huge enough, check the whole circuit.

It’s probably the simplest one out there, and that tricky diode makes all the difference. It uses only one capacitor, no transistors, and if you don’t already have all the parts lying around you can get them at Radio Shack for under $8.

With the given component values, this circuit has the standard 1ms-2ms pulse range, with a slightly long (but still totally usable) ~40ms low pulse in between. Kindly Mr. Sawicz also provides the equations that govern the high pulse range and low pulse time of his design (remember, 1ohm*1farad=1second):

THIGH = 0.693(R1 + R2)C
TLOW = 0.693(R3)C

Basically, you can pick out a nice pot for R2, and size the capacitor C1 to control the range of the pulse widths you want to produce. R1 then adds the minimum pulse width offset, and R3 controls the off time in between pulses. Darren also notes that you might want to use a trim-pot for R1, so you can tweak the center of your pulse range to perfection (probably a good idea, since variations of individual components can changes the signal characteristics).

For the R2 I used a nice 10Kohm slide potentiometer, but I only connected the wiper and one pole, rather than the self-parallel arrangement from the schematic. I figure this way if it gets grimy and looses contact, the circuit will just stop outputting pulses, rather than producing extreme pulses which will unexpectedly run the servo into its mechanical stop.

For C1 I ended up using three 0.1uf capacitors in parallel (what I had around), for what should have been a 2ms variable pulse width range. Looking at it on the scope though I had more like 3ms of range, so I eliminated R1 all together (again, good to use a trim-pot here, you can always adjust it down to nothing). That left me with a period adjustable from 0.1ms up to about 3ms, which is even more than I originally wanted.

I initially chose a 100Kohm resistor for R3, which should have given me about 20ms of low time, but ended up giving me more like 30ms. Servos worked with it just fine, so I wouldn’t have known the difference without a scope. I ended up using an 82Kohm resistor instead, which gave me the ~20ms low period I wanted. I’m not sure why the actual timing didn’t agree with the formulate, but I suspect the three little capacitors I dug up aren’t very tightly toleranced.

Once it worked on a breadboard I threw it all together on a 20-pin DIP prototyping board. In hindsight maybe I should have thought a little more about the layout before I started soldering components on, but I did get it done just in time for the UPS truck to pull up:

The rest of Hobby Servo Fundamentals was a nice read, and I think it would make a good primer for someone not familiar with the electromechanical workings of hobby servos or the PWM protocol that controls them (although I think the part on page 5 about the FCC mandated 40ms PPM period for radio transmitters is out of date, I know my Hitec Optic6 has a 22.5ms period). It also covers continuous rotation servo modification (page 9), although I prefer using a trim-pot or the original potentiometer to fine-tune the deadband location, rather than relying on the tolerance of a pair of fixed resistors.

Darren also offers a true electrical engineer’s solution to reversing the direction a servo rotates. His signal reverser circuit (page 8) uses a one-shot timer triggered by a servo control signal pulse to produce a parallel 3ms pulse, and an XOR gate to subtract the original pulse from it. Very slick, but I can offer a competing mechanical engineer’s solution: open up the servo and reverse the potentiometer poles and the motor leads. It takes all kinds I guess.

-Adam

I was actually looking to do exactly what you decided not to:
Use an attiny25 with 10k pot to drive a servo, also with a pushbutton to center the servo.
I am having trouble with the programming - Can you help me out with programming an ADC? I use AVR Studio 4. Also, anything that you know about the built in timers that would help me pulse the servo more easily would be great.

Thanks.

Hi Ray,

An Attiny25 is definitely capable of what you describe, but can you be a little more specific?

What have you tried so far, and what isn’t working the way you expect it to? Specific small problems (with complete wiring information and source code if applicable) are much more easily addressed in a forum.

-Adam

Thanks for your reply,
I know that the wiring works (I’m very good at electronics), but I have no idea how to program the tiny25 to perform an ADC. If you could provide a specific function that would perform an analog read on a specified pin, or point me to where I could find something of that sort… Also, I do a lot of work with the arduino, so perhaps you could tell me whether the analogRead function from the arduino source code is applicable here and whether it can be successfully “ported” over?

Below is a very simple interrupt-driven program I whipped up a few years ago to use an ATTiny25 running on it’s internal 8MHz oscillator as a battery monitor. It checks and averages readings from an analog input and depending on the average value it changes the color of a bi-color LED (green to red) and plays a tone on the buzzer.

/*Battery Monitor & Buzzer program
for ATTiny 25 monitoring 4 LiPo cells after a 1/4 voltage divider
Adam Borrell
10/25/07*/

#define F_CPU 8000000//CPU clock

#include <avr/io.h>
#include <avr/interrupt.h>

const int threshold[3]={13200,13100,12600};//Tuned Thresholds
long int voltage=0;
int i=0,j=0,state=0;
const int av=32;//number of ADC readings to average

void init(){
	DDRB=(1<<PB1)|(1<<PB3)|(1<<PB4);
	GTCCR|=(1<<COM1B0)|(1<<PWM1B);//set Timer1 PWM mode with B out & !B out
	OCR1B=255;//set LED to green
	TCCR1=(1<<CS10)|(1<<CS12);//set Timer1 to clock

	OCR0A=71;//sets tone frequency Hz=125000/X
	TCCR0A|=(1<<WGM01);//Set Timer0 CTC mode OCR0A top

	ADCSRA|=(1<<ADEN)|(1<<ADIE)|(1<<ADATE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//Enable ADC with free-running auto-trigger, system clock/128, interrupt enabled
	ADMUX|=(1<<MUX0);//Set ADC to ADC1 (PB2)

	sei();//enable global interrupts
	ADCSRA|=(1<<ADSC);//start conversion
}

ISR(ADC_vect){//ADC reading completed
	if(i<av){
		voltage+=ADC;
		i++;
	}else{
		voltage=voltage*20000;
		voltage=voltage/1024;
		voltage=voltage/av;

		if(voltage>threshold[0]){//tuned 13500
			TCCR0B&=~((1<<CS01)|(1<<CS00));//Timer0 off
			TCCR0A&=~(1<<COM0B0);//enable PB1 toggle on compare match
			PORTB&=!(1<<PB1);//Set PB1 low
			OCR1B=255;//set LED to green
			j=0;
			state=0;
		}else if(voltage<threshold[1]){//14V tuned 13000
			state=1;
		}

		if(state==1){
			if(voltage>threshold[2]){//13.5V tuned 12900
				if(j==0){
					TCCR0A|=(1<<COM0B0);//enable PB1 toggle on compare match
					TCCR0B|=(1<<CS01)|(1<<CS00);//Timer0 on
					OCR1B=200;//set LED to orange
				}else if(j>25){
					TCCR0B&=~((1<<CS01)|(1<<CS00));//Timer0 off
					TCCR0A&=~(1<<COM0B0);//enable PB1 toggle on compare match
					PORTB&=!(1<<PB1);//Set PB1 low
				}
				j++;
				if(j==400){
					j=0;
				}
			}else{
				TCCR0A|=(1<<COM0B0);//enable PB1 toggle on compare match
				TCCR0B|=(1<<CS01)|(1<<CS00);//Timer0 on
				OCR1B=0;//set LED to red
				j=0;
			}
		}

		voltage=0;
		i=0;
	}

}

int main(){
	init();
	while(1);
	return 0;
}

When you compile an Arduino script it is first compiled into C, then recompiled into AVR executable code, and you could track down and have a look at the temporary C file. Unfortunately differences between devices, especially between ATMega and ATTiny lines, mean that it is not likely that you could compile this code for an ATTiny with no changes (especially if the code makes use of hardware peripherals, the ADC for example). In the long run you’re much better off reading the device’s datasheet, which should include C examples, and figuring out how to write code for that specific device.

-Adam