Use the Adafruit PCA9685 with a Raspberry PI
Use the Adafruit PCA9685 with a Raspberry PI, in Java
to drive up to 16 servos
- Important: This document goes along with the code at https://github.com/OlivierLD/raspberry-pi4j-samples/.
- In the sources, refer to
AdafruitI2C
The Servo Driver we are talking about here is the Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685.
The original tutorial from Adafruit is available here. The language it uses is Python.
This board has an I2C (pronounce I "square" C) interface, that needs to be enabled on the Raspberry PI.
I2C stands for Inter-Integrated-Circuit.
Arduino & Adafruit provide python libraries that go along with their components. The code we present here is in big part an adaptation of this code.
Enable I2C on the Raspberry PI
We will show how to enable I2C on a Model B Raspberry PI. There is a lot of documentation on the web on how to do it on whatever version on the RasPI you have. We are not going to duplicate the web in this document...
We will need to edit several configuration files. Use the editor you prefer (nano
, vi
, gedit
, ...), as root (sudo
).
- Edit
/etc/modules
Make sure its content eventually looks like this:i2c-bcm2708 i2c-dev
Save your modifications, if you have done any. - Install some I2C Utilities (in python)
Prompt> sudo apt-get install python-smbus Prompt> sudo apt-get install i2c-tools
- Edit
/etc/modprobe.d/raspi-blacklist.conf
, comment the two lines it contents:
Before:blacklist spi-bcm2708 blacklist i2c-bcm2708
After:# blacklist spi-bcm2708 # blacklist i2c-bcm2708
Save your modifications, if you have done any. - By now (the PCA9685 needs to be wired for that), you should be able to run the
i2cdetect
utility, which will will use to know the address of the servo driver:Prompt> sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: 70 -- -- -- -- -- -- --
As seen above, the I2C addresses range from 0x03 to 0x77 (binary 0000011 to 1110111). This way, several boards can be used simultaneously from the RasPI, as long as their I2C addresses are different, even if the GPIO interface pins are connected to several different boards.
For example, if you have connected- A PCA9685 (servo driver)
- A BMP180 (Pressure, Altitude, Temperature)
- A LSM303 (Magnetometer & Accelerometer)
Prompt> sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- 19 -- -- -- -- 1e -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: 70 -- -- -- -- -- -- 77
where, in addition to the above,0x19
would be the magnetometer address (LSM303)0x1e
would be the accelerometer address (LSM303)0x77
would be the BMP180 address
Wiring
The connection setting is quite simple.
|
The Raspberry PI, connected to a PCA9685, with 2 servos. |
This diagram shows the name of the pins you want to use on the GPIO Connector of the Raspberry PI. As we said before, we need to use the pins 1, 3, 5, and 6.
Programming
You will need to know what address to talk to to reach the PCA9685. Run the i2cdetect
utility.
Prompt> sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: 70 -- -- -- -- -- -- --
We will use the 0x40
address to reach the PCA9685.
The Java code we are interested in is in the class named adafruiti2c.AdafruitPCA9685
. Check it out in the repository. We are going to comment only on the main
method of this class, which is a basic example. It assumes that 2 servos are connected to the PCA9685, on the 15 and 14 slots.
The PCA9685 has 16 such slots, numbered - on the board - from 0 to 15. See them on the picture at the top of this document. The slots we are talking about are the 16 columns of 3 holes drilled at the bottom of the board. Their number is printed on top of each column of 3 holes.
... public static void main(String[] args) { AdafruitPCA9685 servoBoard = new AdafruitPCA9685(); // 0x40 is the default address servoBoard.setPWMFreq(60); // Set frequency to 60 Hz int servoMin = 150; // Min pulse length out of 4096 int servoMax = 600; // Max pulse length out of 4096 final int CONTINUOUS_SERVO_CHANNEL = 14; final int STANDARD_SERVO_CHANNEL = 15; for (int i=0; i<10; i++) { System.out.println("i=" + i); servoBoard.setPWM(STANDARD_SERVO_CHANNEL, 0, servoMin); servoBoard.setPWM(CONTINUOUS_SERVO_CHANNEL, 0, servoMin); waitfor(1000); servoBoard.setPWM(STANDARD_SERVO_CHANNEL, 0, servoMax); servoBoard.setPWM(CONTINUOUS_SERVO_CHANNEL, 0, servoMax); waitfor(1000); } servoBoard.setPWM(CONTINUOUS_SERVO_CHANNEL, 0, 0); // Stop the continuous one System.out.println("Done with the demo."); } ...
This code loops 10 times and makes the servos go from a value of 150 to a value of 600, and vice versa. It waits for 1000 milliseconds between each message to the servos.
Notice the two int
variables, CONTINUOUS_SERVO_CHANNEL
and STANDARD_SERVO_CHANNEL
.
For this demo, the standard servo we use is the Standard servo - TowerPro SG-5010 - 5010.
As seen in its documentation, the servo rotates on 180°, 90° on each side. Position "0" (1.5ms pulse) is middle, "90" (~2ms pulse) is all the way to the right, "-90" (~1ms pulse) is all the way to the left.
We see we use a 60 Hz frequency. This means we will have 60 cycles per second. Each cycle contains 4096 ticks.
The value minServo
is 150.
The value maxServo
is 600.
A full cycle (of 4096 ticks) at 60 Hz will take 1 / 60 ~ 16 ms.
150 ticks would take 150 / (60 * 4096) seconds, which is ~ 0.61 ms.
600 ticks would take 600 / (60 * 4096) seconds, which is ~ 2.44 ms.
More generically
TickTime = 1 / (freq * 4096)
.PulseTime = TickTime * nbTicks
.
It is quite easy - and convenient - to put those formulas in a spreadsheet.
Based on those formulas, we should have:
Pulse Width in ms | Nb Ticks |
---|---|
1 | 246 |
1.5 | 369 |
2 | 492 |
The values given in the Adafruit tutorial (150 & 600) happens to be a bit different of what the documentation of the servo says..., but they actually work fine, and this is the ones we use in the code:
Pulse Width in ms | Nb Ticks |
---|---|
1 | 150 |
1.5 | 375 |
2 | 600 |
Running
We run the code from a script named servo
:
#!/bin/bash PI4J_HOME=/home/pi/pi4j/pi4j-distribution/target/distro-contents CP=./classes CP=$CP:$PI4J_HOME/lib/pi4j-core.jar sudo java -cp $CP adafruiti2c.AdafruitPCA9685
Prompt> ./servo
And here is the result
Prompt> ./servo Connected to bus. OK. Connected to device. OK. Setting PWM frequency to 60 Hz Estimated pre-scale: 100.72526 Final pre-scale: 101.0 i=0 i=1 i=2 i=3 i=4 i=5 i=6 i=7 i=8 i=9 Done with the demo Prompt>The console output |
And what next?
Drive the Raspberry PI, on its own cart...
Fuel the Raspberry PI with a solar panel, oriented by servos to face the sun for greater efficiency.
Something to be aware of...
I followed the PWM instructions, came up with several spreadsheets for the calculations, and... it just did not work correctly.
Both standard and continuous servos are supposed to be triggered with pulses having width ranging from 1 to 2 ms. As seen above, at 60 Hz, that makes 246 to 492 ticks.
In reality, after many manual trials, for a frequence of 60 Hz, I used:
For the Standard Servo | Between 122 & 615 ticks |
For the Continuous Servo | Between 340 & 410 ticks, stop at 375 |
Those values are suprisingly different from the theorical ones..., but they work.
See in the code the classes named adafruiti2c.samples.DemoContinuous
and adafruiti2c.samples.DemoStandard
.
Those are the ones I will use as bases for robotic projects.