I thought it might be fun to play around with some servo motors. I never really did anything with them before, and they could come in handy for building mechanical things.
My experiment basically has two controls. When you turn the potentiometer, the left servo moves to the corresponding position. When you press or hold the left button, the right servo turns to the left. The center button moves it to the right. Fun stuff!
While the Raspberry Pi has two PWM outputs that could control the servos directly, I got an Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface to make it easy to control up to 16. Plus, it has nice header pins that fit standard servo motor connectors.
It just requires a little soldering to mount the headers and a 5VDC power supply to power the servos. Quick and easy.
Adafruit has a nice tutorial on how to set everything up too. You may need to enable I 2C support in Linux, as by default it's off in Raspbian distributions, but it's easy to enable.
I got a couple micro servos and a continuous rotation servo from Adafruit to play with, too.
I knew that they used servo motors in things like RC boats and airplanes to control things like rudder, flaps, etc. but I didn't really know how they worked. This tutorial helped.
The basic idea is that they generally have limited range (say, 180° of rotation), offer relatively precise positioning, and can hold that position (with some limitations).
There are only three wires to a servo motor, however, and two of them are power (5 to 6 VDC and ground). The third wire is used to communicate position.
It uses pulse-width modulation. You need to send the servo a pulse 50-60 times a second to keep or change its position. The width of the high part of the pulse determines the angle. For example, 1 millisecond would be 0°, 1.5 ms. would be 90°, and 2 ms. would be 180°. The rotation amounts vary depending on the servo, but 1 ms. = full left and 2 ms. = full right, whatever that might be.
The reason for this wacky encoding method is that it's easy to time division multiplex these signals very across a single radio frequency link. It allowed early RC planes and such to implement the controls entirely with inexpensive discrete components, without even any microcontrollers!
On the breadboard above the leftmost thing is the PWM driver and the thing next to it, with the big ribbon cable, is the Pi Cobbler, that allows access to the Raspberry Pi I/O pins.
The PWM driver just needs connections to power (3.3V), ground and the SDA and SCL lines on the Pi Cobbler board for the I 2C interface. Also, an external 5 VDC power supply, and connections to the servo motors.
The next thing on the breadboard is an MCP3008, 8-channel, 12-bit, SPI interface Analog to Digital Converter (ADC). The Raspberry Pi doesn't include any analog inputs, but it's easy to add them with a MCP3008.
Pin 1 CH0 - Connect to the center tap of a potentiometer
Pin 9 DGND - Connect to ground
Pin 10 CS - Connect to Pi Cobbler CE0 (GPIO8)
Pin 11 D IN - Connect to Pi Cobbler MOSI (GPIO10)
Pin 12 D OUT - Connect to Pi Cobbler MISO (GPIO9)
Pin 13 CLK - Connect to Pi Cobbler SCKL (GPIO11)
Pin 14 AGND - Connect to ground
Pin 15 V REF - Connect to 3.3V
Pin 16 V DD - Connect to 3.3V
The newer Raspbian distros include kernel SPI support, but the Adafruit example doesn't use it, and for what I was doing I didn't need high performance, so I just use the Adafruit Python sample code that bit bangs the SPI for the MCP3008.
The next things on the breadboard are LEDs, one red and one green. The red is connected to GPIO22 and green to GPIO23. The short lead on the LED connects to ground via a 330 ohm resistor. The LEDs and resistors are included in the Adafruit Raspberry Pi starter kit. The software included here doesn't use them, however.
There is a 10K linear potentiometer. One side connects to ground, the other to 3.3V, and the center tap connects to the ADC. The MCP3008 is a 12-bit ADC, so it returns values from 0 to 1023 in the software. This particular pot fits nicely in the breadboard.
Lastly, I put 3 microswitches, also included in the Adafruit kit. The Raspberry Pi includes built-in software support for enabling weak pull-up or pull-down resistors on input lines, but as far as I can tell, this is not implemented in the Python library. WiringPi supports it. Anyway, I didn't want to go digging into the Python library code, so I just put 10K pull-down resistors on the switch inputs. I set them up as:
That's all the hardware, the rest is all fun in software!
The potentiometer and ADC are linked to one of the servos in software. When you turn the pot to left, the servo moves to the left. To the right, the servo moves to the right. It's pretty cool, and just a few lines of code!
pot = mcp3008.readadc(POT_ADC_CHAN, SPICLK, SPIMOSI, SPIMISO, SPICS) if lastpot != pot: # Pot range 0 - 1023, scale to SERVO_MIN to SERVO_MAX # Also subtract pot from 1023 otherwise it rotates backward val = (1023 - pot) * (SERVO_MAX - SERVO_MIN) / 1024 + SERVO_MIN pwm.setPWM(0, 0, val) lastpot = pot
I linked the other servo to the left and center switches. When you hit the left button, it moves left. When you hit the right button it moves right. And you can hold down the button.
if GPIO.input(LEFT_SW) == 0: if servo1 > SERVO_MIN: servo1 = servo1 - 1 pwm.setPWM(1, 0, servo1)
I haven't done anything with the right switch or the LEDs, but that's just a simple matter of software...
Here's the full source (tar/gzip archive, 5K). It's just a bit of test code that I threw together, so it's not cleaned up and prettified. Execute ServoTest.py in Python.
I should point out that I think you could probably make a potentiometer control a servo motor using a 555 timer IC and a few resistors and capacitors (maybe a diode too) to do the PWM. But that is not nearly as fun!