PCA9685 with ESP32 and ESPHome: 16-Channel PWM for Servos and LEDs

Use the PCA9685 with ESP32 and ESPHome for 16 channels of hardware PWM. Covers servo power, 50 Hz control, LED dimming, Home Assistant and troubleshooting.

The PCA9685 is one of the most useful companion chips for an ESP32 when a project needs many independent PWM outputs. Instead of consuming a large number of ESP32 LEDC channels, one PCA9685 provides 16 hardware PWM outputs over I²C, each with 12-bit duty-cycle resolution.

It is widely sold as a servo driver board, but that label only describes one common use. The PCA9685 was designed as a 16-channel PWM LED controller, and in ESPHome each channel is exposed as a floating-point output that can drive a light, fan-control input, servo component or another PWM-based function.

The important limitation is that all 16 outputs share the same PWM frequency. That makes the PCA9685 excellent for sixteen servos at around 50 Hz, or sixteen LED channels at a higher dimming frequency, but much less suitable for mixing those two jobs on the same chip.

This guide covers safe wiring, the separate logic and load power rails found on common breakout boards, current ESPHome configuration, servo control from Home Assistant, LED dimming, frequency selection, multiple PCA9685 boards and the most common troubleshooting problems.

What the PCA9685 Does

The PCA9685 is a 16-channel, 12-bit PWM controller from NXP. Each channel can be set from fully off to fully on with 4096 duty-cycle steps. The device has its own oscillator, so once the ESP32 has written the required values over I²C, the PCA9685 continues generating the PWM waveform in hardware without software timing loops.

FeaturePCA9685
PWM channels16
Resolution12-bit / 4096 steps
InterfaceI²C
Default I²C address0x40
Typical PWM rangeabout 24 Hz to 1526 Hz
Logic supply2.3 V to 5.5 V
Common frequency across channelsYes
Per-channel duty cycleIndependent
ESPHome supportNative pca9685 output component

NXP specifies a typical PWM-frequency range of about 24 Hz to 1526 Hz. Current ESPHome exposes this as the frequency setting and accepts values from 24 Hz to 1525.88 Hz, with a default of 1000 Hz.

The chip also supports an external clock. ESPHome can enable that mode, but the internal oscillator is adequate for nearly every Home Assistant servo or lighting project.

PCA9685 vs MCP23017

The PCA9685 and MCP23017 are both I²C expansion devices, but they solve different problems.

DeviceBest atNot intended for
PCA968516 channels of hardware PWMGeneral digital-input expansion
MCP2301716 digital inputs/outputsIndependent hardware PWM

If you need buttons, reed switches or ordinary on/off GPIOs, see our MCP23017 with ESP32 and ESPHome guide. If you need sixteen servo signals or dimmable output channels, PCA9685 is the better fit.

Understand VCC and V+ Before Wiring

Most PCA9685 breakout boards expose two separate power systems. Confusing them is the fastest way to create unstable servos, ESP32 resets or damaged wiring.

Pin / railPurposeTypical connection
VCCPCA9685 logic powerESP32 3.3 V
GNDCommon referenceESP32 GND and load-supply GND
SDAI²C dataESP32 SDA
SCLI²C clockESP32 SCL
V+Servo/load supply on many boardsExternal supply appropriate for the load

VCC is not the servo power rail. On common servo-driver breakout boards, VCC powers the PCA9685 logic while V+ feeds the positive rail of the three-pin servo connectors. A typical project therefore powers VCC from the ESP32 3.3 V rail and powers V+ from a separate 5 V or 6 V supply sized for the servos.

The grounds must still be common. Connect the external supply ground, PCA9685 ground and ESP32 ground together unless your design includes deliberate galvanic isolation.

Do Not Power Multiple Servos from the ESP32 5 V Pin

A servo can draw a surprisingly high current when starting, accelerating or stalled. Several servos moving together can require amperes, not a few tens of milliamps. The ESP32 development board, USB cable and onboard regulator are not a sensible servo power distribution system.

Use a separate regulated supply with enough current headroom for the actual servos. Feed that supply to V+ on the breakout and keep the logic side at 3.3 V. Add local bulk capacitance near the servo power rail if the wiring is long or the servos create large transient dips.

The exact servo voltage depends on the servo. Many hobby units are designed around 4.8–6 V, while some modern high-voltage servos use higher supplies. Do not assume every three-wire servo is a 5 V device.

Basic Wiring to an ESP32

For a classic ESP32 DevKit, GPIO21 and GPIO22 are convenient I²C pins. Other ESP32 variants can use different pins as long as your chosen GPIOs are valid for I²C.

PCA9685ESP32Notes
VCC3.3 VLogic supply
GNDGNDCommon ground
SDAGPIO21I²C data example
SCLGPIO22I²C clock example
OEOptional / GNDActive-low output enable
V+External load supplyNeeded for servos on common breakout boards

The default I²C address is 0x40. Breakout boards normally provide address solder pads or jumpers if you need multiple boards.

Minimal ESPHome Configuration

The following configuration creates an I²C bus, a PCA9685 hub at 0x40 and one PWM output on channel 0.

esphome:
  name: esp32-pca9685
  friendly_name: ESP32 PCA9685

esp32:
  board: esp32dev

logger:

api:

ota:
  - platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true
  frequency: 400kHz

pca9685:
  - id: pca_hub
    address: 0x40
    frequency: 1000Hz

output:
  - platform: pca9685
    pca9685_id: pca_hub
    channel: 0
    id: pwm_channel_0

If wiring is correct, the I²C scan should report 0x40. If it does not, solve the power, address, SDA/SCL or pull-up problem before configuring lights or servos.

Using PCA9685 for LED Dimming

For a single dimmable LED channel or a logic-level LED driver input, expose the PCA9685 output through ESPHome’s monochromatic light component.

pca9685:
  - id: pca_hub
    address: 0x40
    frequency: 1000Hz

output:
  - platform: pca9685
    pca9685_id: pca_hub
    channel: 0
    id: led_pwm_0

light:
  - platform: monochromatic
    name: "Cabinet LED"
    output: led_pwm_0

Home Assistant then gets a normal dimmable light entity. Brightness from 0 to 100% is translated into the PCA9685 channel’s 12-bit duty-cycle range.

For a bare indicator LED, include a current-limiting resistor and remain within the electrical limits of the PCA9685 output. For LED strips, lamps or meaningful power, use the PCA9685 as the PWM signal source for a suitable MOSFET or LED driver. The chip is not a multi-amp LED power stage.

PWM Frequency for LEDs

A frequency around 500–1000 Hz is a practical starting point for ordinary LED dimming. Higher frequency reduces visible flicker but can increase switching losses in external drivers. If a camera will record the lighting, test the result because rolling-shutter banding can remain visible at frequencies that look perfectly steady to the human eye.

Remember that every channel on the same PCA9685 uses this same frequency.

Using PCA9685 for a Servo

Most hobby servos expect a control frame around 50 Hz. ESPHome’s servo component is designed to sit on top of a float output such as a PCA9685 channel.

pca9685:
  - id: pca_hub
    address: 0x40
    frequency: 50Hz

output:
  - platform: pca9685
    pca9685_id: pca_hub
    channel: 0
    id: servo_pwm

servo:
  - id: flap_servo
    output: servo_pwm

ESPHome’s current servo defaults map the minimum position to 3% duty cycle, centre to 7.5% and maximum to 12%. At 50 Hz, one complete PWM period is 20 ms, so those percentages correspond roughly to 0.6 ms, 1.5 ms and 2.4 ms pulses.

Those defaults cover a broad hobby-servo range, but not every servo should be driven through that full span. A mechanical system may hit its end stop before the command reaches ±100%, and continuously forcing the servo against an end stop can overheat or damage it.

Expose Servo Position to Home Assistant

The ESPHome servo component does not automatically create a Home Assistant entity. A convenient approach is to add a template number and convert -100 to +100 into the servo’s -1.0 to +1.0 command range.

number:
  - platform: template
    name: "Vent Flap Position"
    min_value: -100
    max_value: 100
    step: 1
    initial_value: 0
    optimistic: true
    set_action:
      - servo.write:
          id: flap_servo
          level: !lambda 'return x / 100.0;' 

Home Assistant now receives a slider that moves the servo. For a real mechanism, reduce the minimum and maximum travel until both mechanical ends have comfortable margin.

Detach the Servo After Moving

ESPHome also provides a servo.detach action. Detaching stops the PWM output and can reduce idle current, noise and heating on systems that mechanically hold their position without continuous torque.

button:
  - platform: template
    name: "Detach Vent Servo"
    on_press:
      - servo.detach: flap_servo

Do not detach a servo that must actively hold a load. Once PWM stops, a conventional hobby servo no longer actively maintains commanded position.

Servo Calibration

The actual pulse width corresponding to a servo’s safe physical travel varies by model and mechanism. ESPHome allows min_level, idle_level and max_level to be changed.

servo:
  - id: flap_servo
    output: servo_pwm
    min_level: 4.5%
    idle_level: 7.5%
    max_level: 10.5%
    restore: false

Start conservatively. Move the mechanism slowly toward each end and stop increasing travel before the linkage binds. Do not copy another project’s percentages just because it uses the same PCA9685; the safe limits depend on the servo and mechanical installation.

Why You Should Not Mix Servos and High-Frequency LEDs on One PCA9685

The PCA9685 gives each channel independent duty cycle, but it does not give each channel an independent frequency. One prescaler controls the whole chip.

If you set the board to 50 Hz for servos, every LED PWM channel also operates at 50 Hz. That can produce visible flicker. If you set the board to 1000 Hz for good LED dimming, standard hobby servos may not behave correctly.

The clean solution is two PCA9685 boards with different I²C addresses: one at 50 Hz for servos and another at a higher frequency for lighting.

Two PCA9685 Boards at Different Frequencies

pca9685:
  - id: servo_board
    address: 0x40
    frequency: 50Hz

  - id: led_board
    address: 0x41
    frequency: 1000Hz

output:
  - platform: pca9685
    pca9685_id: servo_board
    channel: 0
    id: servo_0_pwm

  - platform: pca9685
    pca9685_id: led_board
    channel: 0
    id: led_0_pwm

Change the address jumpers on the second board so it appears at 0x41. Both boards remain on the same SDA and SCL wires, but ESPHome treats them as independent PWM hubs with independent frequencies.

PCA9685 Addressing

Common PCA9685 breakout boards expose hardware address jumpers. The default is 0x40, and multiple boards can be placed on one I²C bus by assigning different addresses.

Check the exact board schematic before assuming how a solder bridge changes the address. The PCA9685 also supports group-call and all-call addresses internally, so an I²C scanner can sometimes show additional addresses depending on the breakout and configuration.

Using OE for Hardware Output Disable

The PCA9685 has an active-low Output Enable pin, usually labelled OE. Pulling OE high disables the PWM outputs without needing to rewrite every channel over I²C.

For simple projects, OE is often tied low so the outputs are always enabled. For systems where outputs should be disabled independently of firmware, route OE to appropriate hardware control. It can be useful as part of a safe-start design, but do not treat it as a certified emergency-stop function.

The Power-On State Matters

At power-up, do not assume your connected mechanism immediately receives the exact final state defined in Home Assistant. The ESP32 has to boot, ESPHome has to initialize the I²C bus and the PCA9685 has to receive its channel values.

If a mechanism must remain in a safe position during startup or communications failure, solve that requirement in the mechanical and electrical design as well as in YAML. For a high-energy actuator, a hobby servo board and Wi-Fi firmware should not be the only safety layer.

Troubleshooting: PCA9685 Not Found

  • Confirm VCC is connected to a valid logic supply and GND is common with the ESP32.
  • Check SDA and SCL have not been swapped.
  • Run ESPHome with scan: true and look for 0x40 or the address selected by the jumpers.
  • Check whether another board already uses the same address.
  • Inspect I²C pull-ups. Many breakout boards include them, so several boards can create overly strong parallel pull-ups.
  • Reduce the bus to 100 kHz while debugging questionable wiring.
  • Do not confuse the external servo V+ rail with the PCA9685 logic VCC rail.

Troubleshooting: Servo Jitters or ESP32 Resets

This is usually a power problem before it is a software problem. Servo current changes rapidly with load. If the supply voltage collapses, the servo may twitch while the ESP32 browns out or loses Wi-Fi.

  • Use a separate regulated servo supply with adequate current capacity.
  • Connect the servo supply ground to the ESP32/PCA9685 ground.
  • Use short, reasonably thick power wiring for the servo rail.
  • Add bulk capacitance close to the PCA9685 servo connectors when needed.
  • Avoid powering several servos through breadboard jumper wires.
  • Test with one servo before connecting the full mechanical load.

If the power system is solid but the servo still chatters, check the PWM frequency and pulse limits. A 1000 Hz LED configuration is not a correct servo configuration.

Troubleshooting: Servo Moves the Wrong Way

The PCA9685 does not know which mechanical direction you call open or closed. Reverse the logic in your Home Assistant control or map the template number accordingly.

Do not reverse servo polarity by swapping supply wires. A three-wire hobby servo expects fixed power polarity; direction is encoded in the control pulse.

Troubleshooting: LED Is Dim Even at 100%

Check whether the PCA9685 output is driving the LED directly or driving an external MOSFET/driver stage. Verify LED supply voltage, current limiting, transistor gate/base drive and common ground.

Also confirm no max_power limit is set on the ESPHome output. If several channels share an undersized supply, the apparent brightness drop may be voltage sag rather than a PWM problem.

PCA9685 and Home Assistant

PCA9685 itself never appears as a Home Assistant device entity. ESPHome is the hardware abstraction layer. You create lights, numbers, fans or other entities that use PCA9685 outputs, and those higher-level entities are what Home Assistant sees.

This is useful because basic control can remain local. A physical button connected to the ESP32 or an MCP23017 can trigger a servo or PCA9685 output directly through ESPHome even when Home Assistant is temporarily unavailable.

When PCA9685 Is the Right Choice

  • Several hobby servos: one board can generate sixteen independent servo duty cycles at a common 50 Hz frame rate.
  • Multi-channel LED dimming: 12-bit resolution provides smooth brightness control.
  • Many identical PWM control inputs: useful when the ESP32 does not have enough convenient PWM channels.
  • Stable hardware-generated PWM: the PCA9685 continues generating outputs without software bit-banging.

When to Use Something Else

  • Only one or two PWM outputs: ESP32 LEDC can usually do the job with no additional chip.
  • Addressable LEDs: WS2812/SK6812-style LEDs use a serial data protocol, not ordinary PWM.
  • Digital input expansion: use MCP23017 or another GPIO expander.
  • Different PWM frequencies per channel: PCA9685 cannot provide that on one chip.
  • High-power motor control: PCA9685 can provide a control waveform but is not a motor power stage.
  • Safety-critical actuation: use hardware and controls designed for the required safety integrity.

Practical Recommendations

  • Power PCA9685 logic from 3.3 V when connecting directly to an ESP32.
  • Treat V+ as a separate load rail on common servo-driver boards.
  • Use an external servo supply rather than the ESP32 regulator.
  • Use about 50 Hz for conventional hobby servos unless the servo manufacturer specifies otherwise.
  • Use a higher frequency such as 500–1000 Hz as a practical starting point for LED dimming.
  • Remember that all 16 channels share one frequency.
  • Use a second addressed PCA9685 if you need both servo-rate PWM and higher-frequency LED PWM.
  • Calibrate servo travel conservatively so the mechanism never drives hard into its end stops.
  • Use proper external drivers for LED strips, motors or other loads that exceed logic-level output capability.

Final Thoughts

The PCA9685 is a particularly good match for ESPHome because it turns one I²C connection into sixteen stable, hardware-generated PWM channels. It is inexpensive, well supported and flexible enough for servo banks, dimmable lighting and other low-power control signals.

The most important design decisions happen outside the YAML: separate logic power from servo/load power, provide enough current, share the correct ground, choose a PWM frequency appropriate for the load and remember that the whole PCA9685 shares that frequency.

For current YAML syntax, see the ESPHome PCA9685 documentation and the ESPHome Servo documentation. Electrical limits and timing details are available in the NXP PCA9685 datasheet.

Share your love