An ESP32 drives an MCP4728 quad-channel DAC over I2C, producing four simultaneous analog waveforms in the 0–3.3 V range at 12-bit resolution, updated every 10 ms.
I wrote this as as a controllable test source for a high-speed ADC pipeline for my other projects. It turned out useful enough to share.
champaign is part of the Embedded Applications Lab — a set of working applications based on a set of reusable libraries across MCU, Linux, and RTOS targets.
| Ch | Waveform | Notes |
|---|---|---|
| A | Steady | DC reference — 1.0 V |
| B | Sine | 0.5 Hz, ±1.65 V around 1.65 V midpoint |
| C | Beat | f₁ = 1.0 Hz, f₂ = 1.1 Hz — envelope period 10 s |
| D | Brownian gravity | Random walk pulled toward 1.65 V |
The channel plot below was captured from 10000 lines (10 seconds) of log output.
Channels A and B captured on a digital scope:
Requires ESP-IDF. Clone the champaign repo with --recurse-submodules, then:
Configuration: waveform parameters (frequency, amplitude, type) are hardcoded in
champaign_library/src/dac.c. Edit the values and recompile to change them.
cd apps_espidf/dac
idf.py set-target esp32
idf.py build
idf.py flashapps_espidf/dac/main/main.c creates and configures the ESP32 platform-specific MCP4728 driver then starts a task to run the loop.
champaign_library/src/dac.c sets up the waveforms, and runs a timer-interval loop for driving the DAC output. This module is platform independent.
apps_linux/run_tests/src/main.c runs unit tests using a dummy MCP4728 back-end.
To build and run the tests:
cd apps_linux/run_tests
cmake -S . -B build
cmake --build build
build/appPulled in as git submodules under lib/:
- dtcore — core utilities and error handling
- dtmc_base — timeseries models and task runtime
- dtmc_espidf — ESP-IDF platform layer including MCP4728 driver
ESP32 → I2C (SDA=21, SCL=22, 100 kHz) → MCP4728 → four analog outputs (VA–VD), 0–3.3 V.
(The bench shot is a bit busy — Champaign is part of a larger setup.)




