Nothing on this page will run on a bare S3. The page below is unchanged — read it if you are planning ahead or using a different board.
DAC output
Two pins on the classic ESP32 produce a real voltage rather than a switching average - no filter needed. They are 8 bits and about 3 mA, which is exactly enough for the jobs they are good at and useless for the ones people try first.
A real voltage, in 256 steps
What it is genuinely good for
- A control voltage for an analog synth or an op-amp circuit.
- A slowly moving reference — a setpoint, a bias, a test signal.
- A simple tone, using the built-in cosine generator rather than a loop.
What it is not for
Audio playback beyond a beep, driving anything with a coil in it, and any chip that is not an original ESP32 or an S2. The C3, C6, S3 and P4 have no DAC at all, and the usual replacement is PWM at 20 kHz through a resistor and a capacitor — which produces an average voltage that is good enough for almost every case above.
The code
A lookup table and a hardware timer. Doing it with delay in a loop works up to a few hundred hertz and then falls apart, which is why the timer is here from the start.
#include <math.h>
const int PIN = 25; // DAC1. DAC2 is GPIO 26
uint8_t table[256];
volatile uint8_t idx = 0;
hw_timer_t *timer = nullptr;
void IRAM_ATTR onTick() { dacWrite(PIN, table[idx++]); }
void setup() {
for (int i = 0; i < 256; i++)
table[i] = 127 + 127 * sinf(i * 2 * PI / 256);
timer = timerBegin(1000000); // 1 MHz tick
timerAttachInterrupt(timer, &onTick);
timerAlarm(timer, 1000000 / (440 * 256), true, 0); // 440 Hz
}
void loop() {}For a pure tone there is a better way - the chip has a cosine wave generator that drives the DAC in hardware, with no CPU at all. dacWrite in a loop is for arbitrary shapes.
MicroPython exposes the DAC as an 8-bit write. A loop is fast enough for a slow control voltage, which is the honest use for it here.
from machine import DAC, Pin
import time
dac = DAC(Pin(25))
while True:
for v in range(0, 256, 4):
dac.write(v) # 0 to 255 maps to 0 to 3.3 V
time.sleep_ms(5)DAC exists only on the original ESP32 and the S2. On a C3, C6 or S3 the import itself fails - there is no DAC hardware to expose.
When it does not work
Your chip has no DAC. Only the original ESP32 and the S2 have one. On every other chip the answer is PWM through a low-pass filter, or an external I2C DAC such as the MCP4725.
The DAC sources about 3 mA. A speaker wants hundreds. It drives an amplifier input or an op-amp buffer, never a load.
8 bits is 256 levels and a dacWrite loop manages a couple of hundred thousand samples a second. Above a few kHz that is a staircase you can hear. Use I2S with DMA for audio.
Normal for this DAC. The last few counts at each end are not linear. Design for 0.1 to 3.2 V and it stops mattering.
Another piece of analog hardware that only some chips have, and the one where a fixed threshold is always the bug.
Touch pins →Edit this page — content/esp32/dac-output.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.