Pick and place arm on a PCA9685, on an Arduino Uno
A four-servo arm on a PCA9685 servo board with the Adafruit library: it picks a part up on one side, sets it down on the other and goes home, over and over. The whole build, an Arduino Uno and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
AdvancedRuns in your browser. Free, and no account needed.
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Pick and place arm on a PCA9685: a four-servo arm on the 16-channel servo
// board, driven with the Adafruit PWM Servo Driver library.
//
// PCA9685 SDA to A4, SCL to A5, VCC to the Uno's 5V, GND to GND
// V+ from a separate 5 V supply for the servos
// arm BASE on channel 0, SHOULDER 1, ELBOW 2, GRIP 3
//
// The board makes the servo pulses itself, fifty times a second on every
// channel, so the Uno only speaks when a joint should move: two wires for up
// to sixteen servos, and no pulse timing in the sketch at all.
//
// A move is a list of four angles. glide() walks every joint there together
// in small steps, which is how a real arm avoids slamming its gears and
// swinging what it carries.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// An SG90 wants 1000 to 2000 us for 0 to 180 degrees.
const int MIN_US = 1000;
const int MAX_US = 2000;
// Channels 0 to 3 are the base, shoulder, elbow and claw, in that order.
const int OPEN = 110, SHUT = 25;
int now[4] = {90, 90, 90, OPEN};
void setAngle(int channel, int degrees) {
pwm.writeMicroseconds(channel, map(degrees, 0, 180, MIN_US, MAX_US));
}
void glide(const char *what, int base, int shoulder, int elbow, int grip) {
Serial.println(what);
int target[4] = {base, shoulder, elbow, grip};
for (int step = 1; step <= 25; step++) {
for (int j = 0; j < 4; j++) {
setAngle(j, now[j] + (target[j] - now[j]) * step / 25);
}
delay(30);
}
for (int j = 0; j < 4; j++) now[j] = target[j];
delay(250);
}
void setup() {
Serial.begin(115200);
if (!pwm.begin()) {
Serial.println("no PCA9685 at 0x40: check SDA, SCL and VCC");
}
pwm.setPWMFreq(50);
for (int j = 0; j < 4; j++) setAngle(j, now[j]);
Serial.println("pick and place arm: ready");
}
void loop() {
glide("over the part", 150, 90, 90, OPEN);
glide("reach down", 150, 130, 60, OPEN);
glide("grip", 150, 130, 60, SHUT);
glide("lift", 150, 90, 100, SHUT);
glide("swing across", 30, 90, 100, SHUT);
glide("lower", 30, 130, 60, SHUT);
glide("let go", 30, 130, 60, OPEN);
glide("home", 90, 90, 90, OPEN);
delay(500);
}
Parts list
5 parts, plus the jumper wires. Every one is in the editor's parts bin.
How it is wired
11 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.
- Ground: Arduino Uno R3 pin GND; Servo driver, PCA9685 16-channel pin GND
- Arduino Uno R3 pin 5V; Servo driver, PCA9685 16-channel pin VCC
- Arduino Uno R3 pin A4; Servo driver, PCA9685 16-channel pin SDA
- Arduino Uno R3 pin A5; Servo driver, PCA9685 16-channel pin SCL
- 5 V: Servo driver, PCA9685 16-channel pin V+
- Servo driver, PCA9685 16-channel pin PWM0; Robot arm, 4 servos pin BASE
- Servo driver, PCA9685 16-channel pin PWM1; Robot arm, 4 servos pin SHOULDER
- Servo driver, PCA9685 16-channel pin PWM2; Robot arm, 4 servos pin ELBOW
- Servo driver, PCA9685 16-channel pin PWM3; Robot arm, 4 servos pin GRIP
- 5 V: Robot arm, 4 servos pin VCC
- Ground: Robot arm, 4 servos pin GND
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.