Motors
PCA9685 PWM driver with ESP32
Sixteen servos on two wires is the point of this board, and it is the practical answer on a display board with only an I2C connector left. The PCA9685 generates all the PWM itself. After the setup, the ESP32 sends only new positions, which leaves it free for the display.
Power is the main trap. VCC is the chip's logic supply and goes to 3.3 V. V+ is a separate rail that feeds only the servos, normally 5 to 6 V from a supply that can handle them. Join the grounds and nothing else. Do not power a bank of servos from USB or the ESP32 regulator: when several start together, the voltage drops and the display board resets. Never connect the servo supply to the 3V3 pin. As a first step, add up stall currents, not running currents.
All 16 channels share one frequency, because the chip has a single prescaler. At 50 Hz for servos, you cannot also run LED dimming at another rate on the same chip. The frequency comes from 25,000,000 / (4096 × 50) − 1, which gives a prescale of 121. One step is about 4.9 µs, and pulses of about 150 to 600 counts cover a typical servo, so check yours and start in the middle to avoid forcing it against its end stop. The default address is 0x40, changed with solder jumpers, and that also clashes with an INA219 or INA226 at 0x40, so scan first. OE is pulled low on most boards. Driving it high switches all outputs off.

Specifications
| Channels | 16 |
|---|---|
| Resolution | 12-bit |
| Logic supply (VCC) | 3.3 V |
| Servo supply (V+) | Separate 5–6 V |
| I2C address | 0x40 default, solder jumpers |
| Frequency | One for all channels |
| Servo pulse | About 150–600 counts at 50 Hz |
Power and logic levels
3.3 V: safe to connect directly
| Supply | VCC 3.3 V, V+ 5–6 V separate |
|---|---|
| Signal level | I2C at 3.3 V |
| On an ESP32 | V+ must never touch an ESP32 pin. |
ESP32 pins, including those on a display board's connectors, work at 3.3 V and are not 5 V tolerant. Modules differ, so if in doubt, measure the signal before you connect it.
Wiring
| PCA9685 PWM driver | Connect to | Note |
|---|---|---|
| VIN / VCC | 3V3 | |
| GND | GND | |
| SDA | GPIO 8 | Any free pin, set in Wire.begin() |
| SCL | GPIO 9 | Any free pin, set in Wire.begin() |
| V+ | External 5–6 V | Never the 3V3 pin |
| GND | Join with servo supply |
Arduino code
#include <Wire.h>
const int SDA_PIN = 8, SCL_PIN = 9;
const uint8_t ADDR = 0x40;
void writeReg(uint8_t reg, uint8_t value) {
Wire.beginTransmission(ADDR);
Wire.write(reg); Wire.write(value);
Wire.endTransmission();
}
void setPulse(uint8_t channel, uint16_t off) {
Wire.beginTransmission(ADDR);
Wire.write(0x06 + 4 * channel); // LEDn_ON_L
Wire.write(0); Wire.write(0);
Wire.write(off & 0xFF); Wire.write(off >> 8);
Wire.endTransmission();
}
void setup() {
Wire.begin(SDA_PIN, SCL_PIN);
writeReg(0x00, 0x10); // sleep, needed to set the prescaler
writeReg(0xFE, 121); // 50 Hz
writeReg(0x00, 0x20); // wake, auto-increment
delay(1);
writeReg(0x00, 0xA0); // restart
}
void loop() {
setPulse(0, 300); delay(1000);
setPulse(0, 400); delay(1000);
}
No extra library needed. Compiled for ESP32-S3 with arduino-esp32 3.x. Not yet tested by us on a physical sensor. Change the pins to match your board.
On a display
Read the value in your sketch and pass it to a label, or send it over MQTT and show it with an MQTT value in the editor.
Design a display for PCA9685 PWM driverBoards with a ready connector
Only boards whose connectors we have surveyed. Others may still work on free GPIOs.
| Elecrow CrowPanel ESP32 HMI 2.4" | I2C port: SDA 22, SCL 21Not shared with the touch controller (it is on SPI) — free for sensors |
| Elecrow CrowPanel ESP32 HMI 2.8" | I2C port: SDA 22, SCL 21Not shared with the touch controller (it is on SPI) — free for sensors |
| Elecrow CrowPanel ESP32 HMI 3.5" | I2C port: SDA 22, SCL 21Not shared with the touch controller (it is on SPI) — free for sensors |
| Elecrow CrowPanel ESP32-S3 HMI 5.0" | I2C port: SDA 19, SCL 20Same I2C bus as the GT911 touch (0x14 / 0x5D) and, on V3.0 boards, the PCA9557 touch-reset expander (0x18) |
| Elecrow CrowPanel ESP32-S3 HMI 7.0" | I2C port: SDA 19, SCL 20Same I2C bus as the GT911 touch (0x14 / 0x5D) and, on V3.0 boards, the PCA9557 touch-reset expander (0x18) |
| LilyGO T-Display-S3-Pro 2.33" | QWIIC RIGHT: SDA 5, SCL 6I2C only — same bus as the touch, the PMU (SY6970) and the light sensor |
| M5Stack AtomS3 0.85" | Bottom I2C pins: SDA 38, SCL 39Same I2C bus as the built-in IMU (MPU6886, 0x68) |
| Makerfabs MaTouch ESP32-S3 Rotary IPS 2.1 Round | Mabee I2C (J1): SDA 17, SCL 18Same I2C bus as the touch controller (CST826) |
| Makerfabs MaTouch ESP32-S3 Parallel TFT 4.3" | Mabee I2C: SDA 17, SCL 18Same I2C bus as the touch controller (GT911) |
| Makerfabs MaTouch ESP32-S3 Parallel TFT with Touch 5.0" | Mabee I2C: SDA 17, SCL 18Same I2C bus as the touch controller (GT911) |
| Makerfabs MaTouch ESP32-S3 Parallel TFT with Touch 7.0" | Mabee I2C: SDA 17, SCL 18Same I2C bus as the touch controller (GT911) |
| Sunton ESP32-4827S043R/C 4.3" | I2C header (P3): SDA 19, SCL 20Same I2C bus as the GT911 touch controller. P3 also carries GPIO 17 / 18. |
| Sunton ESP32-8048S043C 4.3" | I2C header (P3): SDA 19, SCL 20Same I2C bus as the GT911 touch controller. P3 also carries GPIO 17 / 18. |
| Sunton ESP32-8048S050C 5.0" | I2C header (P3): SDA 19, SCL 20Same I2C bus as the GT911 touch controller. P3 also carries GPIO 17 / 18. |
| Sunton ESP32-8048S070C 7.0" | I2C header (P3): SDA 19, SCL 20Same I2C bus as the GT911 touch controller. P3 also carries GPIO 17 / 18. |
| Waveshare ESP32-S3 Touch LCD 2.1" round | I2C connector: SDA 15, SCL 7Same I2C bus as the touch controller, the IO expander, the RTC and the IMU (addresses 0x15/0x5D, 0x20, 0x51, 0x6B, 0x7E taken) |
| Waveshare ESP32-S3 Touch LCD 2.8" | I2C connector: SDA 11, SCL 10Same I2C bus as the RTC and the IMU (0x51, 0x6B, 0x7E taken) — the touch has its own bus |
| Waveshare ESP32-S3 Touch LCD 2.8" Type B | I2C connector: SDA 15, SCL 7Same I2C bus as the touch controller, the IO expander, the RTC and the IMU (addresses 0x15/0x5D, 0x20, 0x51, 0x6B, 0x7E taken) |
| Waveshare ESP32-S3-Touch-LCD-2.8C | I2C connector: SDA 15, SCL 7Same I2C bus as the touch controller, the IO expander, the RTC and the IMU (addresses 0x15/0x5D, 0x20, 0x51, 0x6B, 0x7E taken) |
| Waveshare ESP32-S3-LCD-7B | I2C connector (H1): SDA 8, SCL 9Same I2C bus as the touch controller (GT911) and the IO expander (CH422G) |
Good to know
- Use a separate supply for the servos.
- All channels share one frequency.
- Check 0x40 for clashes with INA sensors.
- Find each servo's end stops carefully.
What it is good for
- Robot arms and hexapods
- Boards with no free PWM pins
- Banks of LEDs at one frequency
FAQ
Can I power servos from the ESP32?
No. Use a separate 5–6 V supply on V+ and join the grounds.
Can I run servos and LEDs at different frequencies?
Not on one chip. All channels share a frequency.
Why does the board reset when servos move?
The supply is dropping. Use a stronger, separate one.
Sources
Related parts
Use this information at your own risk. It is for general guidance only: the example code compiles, but it has not been tested on every part, and modules differ between sellers. Check the manufacturer's data before you build. Product names belong to their owners, and LCD Ultra Pro is not affiliated with them. See our Terms.