What is a DAC?
A Digital-to-Analog Converter (DAC) is the opposite of an ADC. Where an ADC reads a voltage and converts it to a number, a DAC takes a number and generates a proportional analog voltage. ESP32 includes two 8-bit DAC channels, letting you output any voltage between 0V and 3.3V from GPIO25 and GPIO26.
Applications include audio output, voltage reference generation, waveform synthesis, analog control signals for external amplifiers, and smooth LED dimming without PWM ripple.
ESP32 DAC Pins and Characteristics
| Feature | Specification |
|---|---|
| DAC channels | 2 (DAC1 = GPIO25, DAC2 = GPIO26) |
| Resolution | 8-bit (0–255) |
| Output range | 0 V to 3.3 V (VDD) |
| Voltage per step | 3.3 V / 255 ≈ 12.9 mV |
| Maximum output current | ~1 mA (buffer amplifier needed for loads) |
| Settling time | ~1 µs |
| Waveform generator | Hardware cosine oscillator built-in |
Basic dacWrite() Usage
#include <Arduino.h>
#define DAC_PIN_1 25 // DAC1
#define DAC_PIN_2 26 // DAC2
void setup() {
Serial.begin(115200);
// No pinMode needed for DAC pins — dacWrite() handles it
}
void loop() {
// Step through all 256 voltage levels
for (int val = 0; val <= 255; val++) {
dacWrite(DAC_PIN_1, val);
float volts = val * (3.3f / 255.0f);
Serial.printf("DAC value: %3d Voltage: %.3f Vn", val, volts);
delay(20);
}
delay(500);
}
DAC vs PWM Comparison
| Feature | DAC (True Analog) | PWM (Simulated Analog) |
|---|---|---|
| Output type | Steady DC voltage | Pulsed 0V/3.3V switching |
| Resolution | 8-bit (256 steps) | Up to 16-bit (65536 steps) |
| Switching noise | None ✓ | Present (filterable) |
| Available pins | GPIO25, GPIO26 only | All output-capable GPIOs |
| For audio output? | Better ✓ | Requires filter |
| For LED dimming? | Works (1 mA limit) | Better (higher current) |
| For motor speed? | No (insufficient current) | Yes ✓ |
Generating a Sine Wave
#include <math.h>
#define DAC_PIN 25
#define SAMPLES 100 // Points per cycle
#define FREQ_HZ 50 // Target frequency: 50 Hz
uint8_t sineTable[SAMPLES];
void setup() {
Serial.begin(115200);
// Pre-calculate sine lookup table
for (int i = 0; i < SAMPLES; i++) {
float angle = 2.0f * M_PI * i / SAMPLES;
sineTable[i] = (uint8_t)((sin(angle) + 1.0f) * 127.5f); // 0–255
}
}
void loop() {
unsigned long delayUs = 1000000UL / (FREQ_HZ * SAMPLES); // µs per step
for (int i = 0; i < SAMPLES; i++) {
dacWrite(DAC_PIN, sineTable[i]);
delayMicroseconds(delayUs);
}
}
At 50 Hz with 100 samples, each step needs a 200 µs delay. This is achievable but CPU-blocking. For higher frequencies or non-blocking operation, use the I2S-DAC interface with DMA.
Simple Audio Tone
#include <math.h>
#define DAC_PIN 25
#define TONE_HZ 440
void setup() {
Serial.begin(115200);
Serial.println("Playing 440 Hz tone on DAC...");
}
void loop() {
static unsigned long lastMicros = 0;
static int phase = 0;
const int STEPS = 50;
unsigned long stepUs = 1000000UL / (TONE_HZ * STEPS);
if (micros() - lastMicros >= stepUs) {
lastMicros = micros();
float angle = 2.0f * M_PI * phase / STEPS;
dacWrite(DAC_PIN, (uint8_t)((sin(angle) + 1.0f) * 127));
phase = (phase + 1) % STEPS;
}
}
Dual DAC: Two-Phase Signal
#include <math.h>
#define DAC1_PIN 25 // In-phase
#define DAC2_PIN 26 // Quadrature (90° offset)
#define STEPS 64
void setup() { }
void loop() {
for (int i = 0; i < STEPS; i++) {
float angle = 2.0f * M_PI * i / STEPS;
dacWrite(DAC1_PIN, (uint8_t)((sin(angle) + 1.0f) * 127));
dacWrite(DAC2_PIN, (uint8_t)((sin(angle + M_PI/2) + 1.0f) * 127));
delayMicroseconds(100);
}
}
Adding an Op-Amp Buffer for Driving Loads
ESP32 DAC pins can only source ~1 mA. For driving a small speaker, headphone, or any analog circuit requiring more current, add an LM358 op-amp configured as a unity-gain voltage follower:
ESP32 GPIO25 ──── LM358 IN+ (non-inverting) LM358 OUT ──── LM358 IN– (inverting, direct feedback = unity gain) LM358 OUT ──── Load (speaker, analog input, etc.) LM358 VCC: 3.3V–12V (higher voltage allows more output swing) LM358 GND: shared with ESP32 GND Output follows input with up to 20 mA drive capability.
Practical Limitations and When to Use External DAC
| Need | ESP32 DAC | External DAC Recommendation |
|---|---|---|
| 8-bit rough voltage | Sufficient ✓ | — |
| Audio output (low quality) | Sufficient ✓ | — |
| High-quality audio | Insufficient | PCM5102A (32-bit I2S) |
| 12-bit precision voltage | Insufficient | MCP4725 (12-bit I2C) |
| 16-bit output | Insufficient | DAC8552 (16-bit SPI) |
| Multiple channels | 2 channels max | MCP4728 (4-ch, 12-bit I2C) |
Summary
ESP32's built-in 8-bit DAC on GPIO25 and GPIO26 provides true analog voltage output without PWM noise, making it ideal for audio tones, waveform generation, and smooth analog control signals. Its main limitations are 8-bit resolution (12.9 mV steps) and 1 mA current output. Add an op-amp buffer for driving loads and consider an external I2S DAC for high-quality audio applications.