ESP32/Pins and signals/15. DAC output
Your chip
Your language
Not on your chip
The ESP32-S3 cannot do this.

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.

Pins and signals · 15 of 81

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.

/esp32/dac-output · arduino · S3

A real voltage, in 256 steps

400 Hz sine, 3.3 V peak-to-peak
600 samples per cycle
Output frequency400 Hz
Amplitude3.3 V
Step size
13 mV
Levels you use
255
Samples per cycle
600
600 steps of 13 mV each. This is a genuine analog voltage — no filtering needed, unlike PWM — but it can only source about 3 mA, so it drives an amplifier input or an op-amp, never a speaker.

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.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

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.

dac_sine.ino
#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.

When it does not work

DAC is not declared in this scope

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 output collapses when I connect a speaker

The DAC sources about 3 mA. A speaker wants hundreds. It drives an amplifier input or an op-amp buffer, never a load.

The waveform is a visible staircase

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.

The voltage never quite reaches 0 V or 3.3 V

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.

Where this goes next

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.

Browse ESP32 on the forum