Sunday, November 3, 2013

Open Source Servo Tester

Inspiration

I was watching a Radio Control (RC) review podcast, where they showed off a neat little servo tester. You could plug in power, then plug a servo into it, and switch between three modes: go to neutral (so you can adjust your servo so whatever it controlled was at neutral), sweep back and forth (for testing to make sure it's not going to die an early death), and follow the knob on the the tester. Add a button to change modes and a couple of LEDs to indicate mode, and you're done.

My first thought was "Cool. And handy. I should buy one of those." The one being reviewed was about $9, but you can get similar items on Amazon (and presumably eBay) shipped to you for as little as $3. Options include multiple outputs and some ESC test modes if you want to spend more money.

While contemplating which one to order, I looked down at the stack of Arduino hardware on my desk, and realized that 1) I had all the hardware I needed to make a servo tester, 2) the hardware and code would be an evening's worth of work and 3) it would be mildly interesting. Furthermore, since I had the source, I could add more options and have as many as six servo outputs as needed.

The hardware

The hardware for the tester side is pretty straightforward: A potentiometer of some sort, a button, and a couple of LEDs. Since my protoshields have a spare button, a pair of LEDs, and a breadboard to hold the potentiometer, dropping one of those on a spare Uno and plugging in the potentionmeter was most of the hardware setup.

Potentiometer

As always, the potentiometer is wired to power and ground on two sides. The signal goes to analog pin 0, which is defined as POT in the source code.

Button

The button on a protoshield just shorts to ground, so all that's required for it is to plug into pin 8, defined in the source as BUTTON.

LEDs

The two LEDs on the protoshield are connected to pins 12 and 13, defined as LED1 and LED2 in the source. Since there's already an LED on pin 13 on most Arduinos, that might save you an LED if you decide not to use a protoshield.

The Servo

The servo must also be connected to both power and ground. The signal line is tied to pin 9, defined as SERVO in the source. Any PWM-capable digital pin can be used.

Connect the servo signals up to three adjacent rows on the breadboard, then connect the male header pins on the same three rows. You then just plug your servo into the header pins. You can even plug in more than one set of header pins to drive multiple servos.

The software

I think there are only three things of note in the software:

  1. The BUTTON pin mode is INPUT_PULLUP, as required by the protoshield button shorting to ground.
  2. The funcs array is a list of pointers to int func(void), and is how the operating mode is selected. To add a new operating mode, write an int func(void) that returns the appropriate value, and add it to the funcs array. THe LED display will be odd, though.
  3. setLeds is called to set the display mode of the LEDs. The value it's passed starts at one, and should have at least one LED on if the device is getting power.
#include <Servo.h> 

#define POT 0
#define BUTTON  8

#define SERVO   9
#define LED1    12
#define LED2    13

#define DELAY   50

Servo myservo; 

void setup() { 
  myservo.attach(SERVO) ;
  pinMode(BUTTON, INPUT_PULLUP) ;
  pinMode(LED1, OUTPUT) ;
  pinMode(LED2, OUTPUT) ;
} 

int neutral() {
  return 90 ;
}

int readPot() {
  return map(analogRead(POT), 0, 1023, 0, 180) ;
}

int sweep() {
  static int pos = 0 ;
  static int dir = 1 ;

  pos = pos + dir ;
  if (dir == 1 && pos > 180) {
    pos = 179 ;
    dir = -1 ;
  } else if (dir == -1 && pos < 0) {
    pos = 1 ;
    dir = 1 ;
  }
  return pos ;
}

int (*funcs[])(void) = { neutral, readPot, sweep } ;


void
setLeds(int value) {
  value += 1 ;
  digitalWrite(LED1, value & 1) ;
  digitalWrite(LED2, value & 2) ;
}

void loop() {
  static int buttonValue = 0 ;
  static int oldState = HIGH ;
  static int state ;

  if ((state = digitalRead(BUTTON)) == LOW && oldState != LOW) {
    buttonValue = (buttonValue + 1) % (sizeof funcs / sizeof *funcs) ;
    setLeds(buttonValue) ;
  }
  oldState = state ;

  myservo.write(funcs[buttonValue]()) ;
  delay(DELAY) ;
}

Usage

Apply power to the Arduino, and it resets, turns on the first LED, and sets the servo to neutral.

Click the button once, and the second LED goes on and the first one off, and the servo will now follow the potentiometer.

Click the button a second time, both LEDs go on, and the Arduino will start sweeping the servo from 0 to 180 and back.

Click a third time, you go back to neutral and only the first LED on.

Conclusion

To be fair, this is probably only useful to people at the extremes of the hobby. Even the least expensive Arduino board costs more than a cheap tester, and it's probably harder to use an inexpensive home-built tester than a proprietary one. If you don't need a servo tester very often - which puts you at one extreme - and tend to have spare Arduino boards around so throwing a servo tester together occasionally isn't a problem, this could be a win. If you are a heavy-duty user of servos, and can really use the ability to test six (or more, with a larger Arduino) servo outputs and create custom test modes - which to me puts you at the other extreme - then this could also be a win. Being at the first extreme, I'll count this as a win since I've already used it to test a couple of servos.