Sixteen at once
All sixteen channels sweeping together, from one sketch. The two things that decide whether it works are both about current: the first move sends every servo to the middle at once from wherever it happened to stop, so the sketch starts them four at a time; and sixteen servos need a supply sized for sixteen.
Why the first move is the dangerous one
At switch-on nothing knows where the servos are. The first command sends every one of them to the middle from wherever it stopped last, and a servo with a long way to go drives its motor hard until it gets there. Sixteen starting in the same instant add their currents together.
So the sketch starts them four at a time, a quarter of a second apart. By the time the second group starts, the first has mostly arrived. The peak is set by four servos starting rather than sixteen, for one extra second of start-up.
Then small steps
After that, the sweep moves every servo 10 µs every 20 ms. Each step is a tiny move, so no servo ever has far to go. One end to the other is 100 steps, a few seconds.
The supply
Sixteen SG90s are about 3.2 A while moving and about 10 A all stalled (Sizing the supply). A 5 V, 3 A USB-C adapter runs this sketch with a few small servos plugged in. For all sixteen, put a supply sized for the stall total on the screw terminal, 6 V at most.
Keep the microcontroller on its own USB cable, and share only GND with the servo supply.
What it prints
started 0 to 3
started 0 to 7
started 0 to 11
started 0 to 15
sweepingThe four groups arrive a quarter of a second apart, then every servo sweeps together.
For more boards, a board plugged in while the sketch runs, or the MicroPython driver, see the examples in the Servo-and-Motor repository.
The code
Every servo is first sent to 1500 µs in groups of four, a quarter of a second apart. Then all sixteen sweep together between 1000 and 2000 µs in 10 µs steps, so no single step is a large move.
// Sixteen at once: every channel sweeps 1000-2000 us.
//
// Wiring ESP32-S3 DevKitC Arduino Uno
// GND -> GND GND
// VCC -> 3V3 5V
// SDA -> GPIO 8 A4
// SCL -> GPIO 9 A5
// OE, V+ not connected not connected
// Servo supply on the screw terminal or USB-C, sized
// for every servo stalled at once. Never over 6 V.
//
// Arduino IDE: Tools > Board > ESP32S3 Dev Module,
// or Arduino Uno.
// ESP32-S3: Tools > USB CDC On Boot > Enabled.
// Serial Monitor at 115200.
// Library: Adafruit PWM Servo Driver Library.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
const uint32_t OSC_HZ = 25000000; // measure yours
const uint8_t CHANNELS = 16;
const uint8_t GROUP = 4; // started together
const uint16_t GROUP_GAP = 250; // ms between groups
const uint16_t END_A = 1000; // us
const uint16_t END_B = 2000; // us
const uint16_t MID = 1500; // us
const uint16_t STEP = 10; // us per step
const uint16_t STEP_MS = 20; // one servo period
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);
uint16_t pos = MID;
int dir = 1;
void setup() {
Serial.begin(115200);
delay(1000);
#if defined(ESP32)
Wire.begin(8, 9); // SDA, SCL
#else
Wire.begin(); // A4, A5 on an Uno
#endif
if (!pwm.begin()) {
Serial.println("No PCA9685 at 0x40.");
while (true) delay(1000);
}
pwm.setOscillatorFrequency(OSC_HZ);
pwm.setPWMFreq(50);
// The first move is the big one: stagger it.
for (uint8_t ch = 0; ch < CHANNELS; ch++) {
pwm.writeMicroseconds(ch, MID);
if (ch % GROUP == GROUP - 1) {
Serial.print("started 0 to ");
Serial.println(ch);
delay(GROUP_GAP);
}
}
Serial.println("sweeping");
}
void loop() {
pos += dir * STEP;
if (pos >= END_B || pos <= END_A) dir = -dir;
for (uint8_t ch = 0; ch < CHANNELS; ch++) {
pwm.writeMicroseconds(ch, pos);
}
delay(STEP_MS);
}GROUP and GROUP_GAP set the stagger; STEP and STEP_MS set the sweep speed. With fewer servos plugged in, the sketch still writes all sixteen channels, and an empty channel costs nothing.
When it does not work
The supply shut down or sagged when the first groups moved. Watch the blue V+ LED during start-up: if it dims or blinks, the supply is too small. Use a bigger one on the screw terminal, or fewer servos.
The supply is sagging under sixteen motors at once. Slow the sweep by making STEP smaller or STEP_MS larger, which lowers the current each step asks for, and check the supply against Sizing the supply.
Those servos stop short of 1000 or 2000 µs. Give them their own limits: keep an END_A and END_B per channel in two arrays and clamp each channel's position to its own pair.
The sketch stopped after the first group. Open the Serial Monitor: if it never prints sweeping, the board probably reset during start-up because the servo current reached the microcontroller's supply. Check V+ is not wired to the microcontroller.
Two LEDs and a scan find almost every fault. Start from what you can see.
When nothing moves →Edit this page — content/books/pca9685/sixteen-at-once.mdx
Questions about this product
See what other owners have asked, and read their solutions.
PCA9685 16-Channel Servo Driver Board, USB-C Powered
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.