To convert a microcontroller's PWM (Pulse Width Modulation) signal into a stable DC analog voltage, use a single-pole RC low-pass filter. For a standard 5 kHz PWM frequency on an ESP32 or Arduino, the optimal starting values are a 10 kΩ series resistor (R1) and a 1 µF shunt ceramic capacitor (C1). This combination yields a cutoff frequency of 15.9 Hz, reducing PWM ripple to under 10 mV while maintaining a settling time of roughly 50 ms. This specific RC configuration provides the best balance of signal smoothing, GPIO protection, and component availability for hobbyist and prototyping DAC (Digital-to-Analog) applications.

The PWM-to-Analog RC Topology

The single-pole RC low-pass filter relies on the fundamental timing relationship between capacitors and resistors in a circuit. Here is the exact node map for the topology:

  • Node A (Input): Connected directly to the microcontroller GPIO pin outputting the PWM square wave (e.g., ESP32 GPIO 25 via the LEDC peripheral).
  • R1 (Series Resistor): Connected between Node A and Node B. This limits current and sets the charge rate.
  • Node B (Output): The filtered analog DC voltage output, fed to an external ADC, op-amp, or analog actuator.
  • C1 (Shunt Capacitor): Connected between Node B and Circuit Ground (GND). This stores charge and smooths the voltage.

Component Behavior Matrix

Understanding how altering one element affects the entire network is critical for tuning your specific application. Assume a fixed 5 kHz PWM input.

Parameter Changed Effect on Cutoff Freq ($f_c$) Effect on Output Ripple Effect on Settling Time (10% to 90%)
Increase R1 (e.g., to 47 kΩ) Decreases Decreases (smoother DC) Increases (slower response)
Increase C1 (e.g., to 4.7 µF) Decreases Decreases (smoother DC) Increases (slower response)
Increase PWM Frequency (e.g., to 20 kHz) No change to filter Decreases (better filtering) No change
Decrease R1 (e.g., to 1 kΩ) Increases Increases (more sawtooth ripple) Decreases (faster response)

Why a Single-Pole RC Over Active Alternatives?

When designing an analog output stage, you generally have three choices. Here is why the passive RC filter wins for 80% of maker and prototyping scenarios:

  1. Single-Pole Passive RC (This Design): Costs ~$0.05, requires no extra power rails, and uses two basic components. Perfect for driving high-impedance loads like ADC inputs or op-amp non-inverting pins.
  2. Active Op-Amp Filter (Sallen-Key): Provides a sharper roll-off and can drive low-impedance loads (like speakers or motors), but requires a dual-rail or virtual-ground power supply, an op-amp IC (e.g., LMV321), and three extra passives. Overkill for simply reading a voltage.
  3. Dedicated I2C DAC (e.g., MCP4725): Provides true 12-bit analog output with zero ripple. However, it costs ~$2.50, requires I2C bus wiring, pull-up resistors, and software library overhead. Use this only when PWM ripple is strictly unacceptable.
Pro-Tip: If your downstream load has an input impedance below 100 kΩ, the load itself will act as a parallel resistor to C1, dragging your DC voltage down. In that case, you must buffer Node B with a unity-gain op-amp voltage follower.

Design Walkthrough: Sizing for 5 kHz PWM

Let's engineer the exact values for an ESP32 running the LEDC (LED Control) peripheral at a default high-speed frequency of 5,000 Hz.

1. Define the Cutoff Frequency Target:
A standard rule of thumb for PWM-to-DC conversion is to set the RC cutoff frequency ($f_c$) to at least 1/100th of the PWM frequency to achieve low ripple. However, 1/300th is a safer bet for < 10mV ripple. Let's target $f_c \approx 15$ Hz.

2. Select the Resistor (R1):
We need to protect the ESP32 GPIO. The absolute maximum source current is 40 mA, but 20 mA is the recommended safe limit. If Node B accidentally shorts to ground, R1 limits the current.
$I = \frac{3.3V}{10,000\Omega} = 0.33 mA$.
A 10 kΩ resistor provides excellent short-circuit protection while keeping the impedance low enough to avoid excessive thermal noise.

3. Calculate the Capacitor (C1):
Using the formula $C = \frac{1}{2 \pi R f_c}$:
$C = \frac{1}{2 \pi \times 10,000 \times 15} = 1.06 \times 10^{-6}$ Farads.
The closest standard E12 value is 1 µF.

Critical Trap: Ceramic Capacitor DC Bias Derating
If you buy a cheap 1 µF X7R capacitor in a tiny 0402 or 0603 SMD package, applying 3.3V DC across it will cause the dielectric to saturate. Its actual capacitance can drop by 40% to 60%, ruining your filter math. To guarantee a true 1 µF at 3.3V, either use a 1206 package size, or step up to a 2.2 µF or 4.7 µF rated part in an 0805 package. For through-hole breadboarding, a standard 50V rated 1 µF electrolytic or film capacitor will not suffer from this DC bias effect.

Failure Modes: What Breaks at the Extremes?

When troubleshooting a dead or noisy analog output, you must understand how the circuit behaves when a component fails open or short. Here is the failure-mode contrast:

Component Failure State Symptom at Node B Risk to Microcontroller
C1 (Capacitor) Short Circuit Output reads 0V DC. GPIO sources current through R1. With 10kΩ, current is 0.33mA (Safe). If R1 was 100Ω, current is 33mA (Fried GPIO).
C1 (Capacitor) Open Circuit Output is a raw 5 kHz square wave (0V to 3.3V). None. But downstream ADCs will read erratic RMS values, and audio amps will emit a harsh whine.
R1 (Resistor) Open Circuit Node B floats. Reads random noise, highly susceptible to 60Hz mains hum. None. GPIO is isolated.
R1 (Resistor) Short Circuit Output is raw PWM. High ripple. If Node B shorts to GND downstream, the GPIO pin will source >40mA and permanently burn out the silicon pad.

Step-by-Step Breadboard Verification

Do not trust your math until you verify it on the bench. Follow this exact sequence to validate the filter using a multimeter and an oscilloscope (or a logic analyzer with analog channels).

  1. Power Down: Ensure the ESP32/Arduino is disconnected from USB and external power.
  2. Place R1: Insert a 10 kΩ (Brown-Black-Orange-Gold) through-hole resistor. Connect one leg to the GPIO 25 row, and the other leg to an empty row (this is Node B).
  3. Place C1: Insert a 1 µF electrolytic capacitor. Watch the polarity stripe. Connect the long (positive) leg to the Node B row, and the short (negative) leg to the ground rail.
  4. Ground the Circuit: Ensure the breadboard ground rail is tied to the microcontroller's GND pin.
  5. Flash the Firmware: Write a simple script to output a 50% duty cycle PWM signal at 5 kHz on GPIO 25. Power on the board.
  6. DMM Verification: Set your digital multimeter to DC Volts. Probe Node B. You should read 1.65V ± 0.05V. (3.3V * 50%).
  7. Scope Verification: Connect an oscilloscope probe to Node B. Set the timebase to 20ms/div. You should see a flat line with less than 15 mV of peak-to-peak sawtooth ripple.
  8. Step Response Test: Change the PWM duty cycle from 0% to 100% in code. Measure the time it takes for the voltage on the scope to reach 3.0V. It should take approximately 50 ms ($3 \times \tau$, where $\tau = R \times C = 10ms$).

The Final Decision Path

Use this decision tree to finalize your exact Bill of Materials (BOM) based on your project's specific constraints. Do not guess; follow the logic to the terminal node.

If your project requires... Then you must... Final Concrete Pick / BOM
Standard sensor biasing or LED dimming control (Settling time ~50ms is fine) Stick to the baseline 5 kHz PWM design. R1: 10 kΩ 1/4W 1%
C1: 2.2 µF 50V X7R 1206 SMD (or 1µF Film TH)
Fast response for audio or closed-loop motor control (Settling time < 5ms) Increase PWM frequency to the hardware maximum (e.g., 20 kHz) and shrink the capacitor. R1: 4.7 kΩ
C1: 0.1 µF (100nF) C0G/NP0 Ceramic
PWM: 20,000 Hz
Ultra-low ripple (< 1mV) for precision 16-bit ADC references A single pole is insufficient. Add a second identical RC stage in series (Node B -> R2 -> Node C -> C2 -> GND). Stage 1: 10 kΩ + 1 µF
Stage 2: 10 kΩ + 1 µF
Note: Settling time will double to ~100ms.
Driving a low-impedance load (< 10kΩ) like a speaker or power transistor base The passive RC will suffer severe voltage sag. You must buffer the output. RC Filter: 10 kΩ + 1 µF
Buffer: LMV321 Op-Amp wired as a unity-gain voltage follower at Node B.

For the vast majority of microcontroller projects reading a potentiometer replacement or driving a basic analog actuator, the 10 kΩ / 2.2 µF baseline at 5 kHz is the definitive, most robust starting point. It protects your silicon, minimizes BOM costs, and guarantees a clean DC signal without the overhead of active components.