Arduino library 路 Motion

Adafruit PWM Servo Driver: sixteen servos from one PCA9685

The PCA9685 is a 16-channel, 12-bit PWM chip on I2C, sold on the blue Adafruit breakout and countless copies. The Adafruit PWM Servo Driver library talks to it, so two Arduino pins (SDA and SCL) can hold sixteen servos or dim sixteen LEDs, and the board keeps every pulse going on its own while the sketch does something else.

Built in on Mokxi: #include <Adafruit_PWMServoDriver.h> and it builds, with nothing to install. Free, and no account needed.

What Adafruit PWM Servo Driver Library does

Each channel counts 0 to 4095 through one PWM period. For servos you set the frequency to 50 Hz, which makes a period 20 ms, so one count is about 4.9 microseconds and a 1.5 ms center pulse is about 307 counts. Tutorials call the end points SERVOMIN and SERVOMAX (often 150 and 600) and tune them for each servo.

The chip鈥檚 internal oscillator is nominally 25 MHz but real ones run a few percent off, which is why widths can come out slightly wrong. setOscillatorFrequency() lets you correct it once you have measured a board. Servo power goes into the board鈥檚 separate V+ terminal, never through the Arduino.

Library Manager nameAdafruit PWM Servo Driver Library
AuthorAdafruit
Include#include <Adafruit_PWMServoDriver.h>
Boardsevery board Mokxi compiles for but ATtiny85
In the simulatorSimulated: it runs against the part the way it does on the bench.
Parts it drivesServo driver, PCA9685 16-channel

Install it

It builds on every board Mokxi compiles for but ATtiny85.

In Mokxi it is built in: #include <Adafruit_PWMServoDriver.h> and the sketch compiles, with no download, no zip and no Library Manager.

On a real board, install "Adafruit PWM Servo Driver Library" by Adafruit from the Arduino IDE鈥檚 Library Manager (Sketch, Include Library, Manage Libraries), or add Adafruit PWM Servo Driver Library to lib_deps in PlatformIO. Mokxi follows the calls of release 3.0.2, so a sketch written here builds there too.

ESP32-C3 路 Arduino Uno 路 Arduino Nano 路 Arduino Mega 2560 路 Arduino Leonardo 路 SparkFun Pro Micro 路 Arduino Micro 路 ESP32-C6 路 Raspberry Pi Pico 路 Raspberry Pi Pico W 路 Seeed XIAO SAMD21 路 STM32 路 STM32 Nucleo-F411RE 路 STM32 Blue Pill 路 micro:bit V2 路 Arduino Uno R4 路 ESP32 DevKit 路 ESP32-S3-DevKitC-1 路 ESP8266 NodeMCU 路 Arduino Pro Mini 3.3 V 路 Seeed XIAO ESP32-C3 路 ESP32-C3 SuperMini 路 Seeed XIAO ESP32-C6 路 ESP32-DevKitC V4 路 LOLIN32 Lite 路 Wemos D1 R32 路 Cheap Yellow Display 路 TTGO T-Display 路 Seeed XIAO ESP32-S3 路 Arduino Nano ESP32 路 Arduino Nano RP2040 Connect 路 Seeed XIAO RP2040 路 Waveshare RP2040-Zero 路 Arduino Uno R4 WiFi

The calls you will use

In the order a sketch meets them.

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

A board at the default address 0x40. Pass another address, such as Adafruit_PWMServoDriver(0x41), for a second board with A0 bridged.

pwm.begin()

Starts I2C, wakes the chip from sleep and sets the default 1000 Hz frequency.

pwm.setOscillatorFrequency(27000000)

Tells the library what the chip鈥檚 oscillator really runs at, so writeMicroseconds() and the frame period come out right on that board.

pwm.setPWMFreq(50)

Sets the PWM frequency for all sixteen channels at once: 50 Hz for analog servos, up to about 1600 Hz for LEDs.

pwm.setPWM(channel, 0, pulse)

Sets when channel 0 to 15 turns on and off within the 4096-count period. On at 0 and off at the pulse length is how servo sketches use it.

for (uint16_t pulse = SERVOMIN; pulse < SERVOMAX; pulse++) {
  pwm.setPWM(0, 0, pulse);
}

pwm.writeMicroseconds(1, 1500)

Sets a channel by pulse width instead of counts, worked out from the frequency and oscillator setting.

pwm.setPin(15, 2048)

A duty cycle from 0 to 4095 on one pin, the call for dimming an LED.

pwm.sleep()

Stops the oscillator and every output; wakeup() starts them again.

Example: Pick and place arm on a PCA9685

On the Arduino Uno R3. It runs in the browser exactly as printed.

sketch.ino 路 Arduino Uno R3
// 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();

// 544 to 2400 us for 0 to 180 degrees: the Arduino Servo library's window,
// which the servos in the arm answer to.
const int MIN_US = 544;
const int MAX_US = 2400;

// 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);
}

Open it in the editor, wired and ready to run 路 Pick and place arm on a PCA9685: the project page

Common errors and how to fix them

The servos do not move but the board鈥檚 LED is on

The servos need power on the V+ screw terminal (5 to 6 V from a supply that can deliver an amp or more). The VCC pin only powers the chip鈥檚 logic, not the servos.

Servos buzz at the ends or travel less than 180 degrees

SERVOMIN and SERVOMAX do not match your servo. Change them a few counts at a time until each end is reached without straining, or use writeMicroseconds() with widths from the servo鈥檚 data sheet.

Nothing responds, and an I2C scanner finds no 0x40

Check SDA and SCL go to the board鈥檚 I2C pins, that VCC has 3.3 or 5 V, and that the address jumpers are as you expect. A board with A0 bridged answers at 0x41.

Projects that use Adafruit PWM Servo Driver Library

Built-in projects that include it. Each page has the full sketch and opens in the editor, already wired.

Try Adafruit PWM Servo Driver Library without installing anything

Open the example, change the sketch, and keep your version in a free account.