Guide no. 84
The Adafruit 935 module gives the FireBeetle 2 ESP32-E, or an Arduino UNO, a real analog output: 4096 levels controlled over I²C, to measure with a multimeter and watch on the Serial Plotter.
By Ada Intelisano · informaticait.it technical team
- Microchip's MCP4725 turns a number from 0 to 4095 into a steady voltage, not pulses like PWM.
- The reference is the supply: VOUT = VDD × code ÷ 4096. At 3.3 V one step is about 0.8 mV.
- It is controlled over I²C at address 0x62 with the Adafruit MCP4725 library; with the ESP32 the module goes on 3V3.
- With
setVoltage(code, true)the value stays in the EEPROM and the chip finds it again at every power-up.
Boards have an ADC to read a voltage, but almost never anything to produce one. The PWM in guide no. 37 switches the pin on and off very quickly and the multimeter sees an average, but the real voltage keeps jumping between zero and the maximum. A DAC, on the other hand, holds the voltage you ask for steady on its output. Microchip's MCP4725 does it with 4096 levels, takes its orders over I²C and remembers the last saved value even when it is switched off.
1The parts
- the Adafruit 935 module with the MCP4725: it comes with a loose six-pin header that you have to solder. Adafruit warns that just pressing it into the holes without soldering does not work (guide no. 70);
- the FireBeetle 2 ESP32-E with its pins already soldered, and five female-to-female jumper wires;
- a multimeter (guide no. 8). Instead of the FireBeetle you can use an Arduino UNO, step 8.
2The module
- VIN and GND: power, 2.7 to 5.5 V for the chip. Adafruit recommends the same voltage as the board that controls it: the 10 kΩ pull-up resistors take SDA and SCL up to VIN itself. So with the ESP32, 3.3 V.
- SCL and SDA: the I²C bus (guide no. 22). The address is 0x62; it becomes 0x63 if you take A0 to VIN or close the “Addr” jumper on the back. This way two modules can share the same bus.
- VOUT: the output. The same signals, except VOUT, also go through the two STEMMA QT connectors.
3How much one step is worth
The chip has no reference of its own: it uses the supply. The datasheet formula is VOUT = VDD × code ÷ 4096, with the code from 0 to 4095. If VDD changes, the output changes too.
- At 5 V one step is 1.22 mV (datasheet table); at 3.3 V about 0.8 mV (our calculation).
- With 4095 the output gets to one step below VDD, not to VDD.
- Near the ends of the range the accuracy drops: Microchip measures linearity between codes 100 and 4000, and the zero error is not calibrated at the factory.
4Connect and measure
- Five straight wires: 3V3 → VIN, GND → GND, 22 → SCL, 21 → SDA, 35 → VOUT. Pin 35 is also A3, an analog input: this way the FireBeetle reads back the voltage it asked for.
- VIN to 3V3, never to VCC: VCC carries the roughly 5 V from USB, the pull-ups would bring SDA and SCL to 5 V as well, and the ESP32 pins cannot take that (guide no. 21).
- Multimeter on V⎓: black probe in the − hole and red probe in the + hole of the optional terminal block (they are GND and VOUT), or straight onto the GND and VOUT pins.
5Four voltages
// The MCP4725 DAC: four voltages, one every 4 seconds. Measure them with the // multimeter between VOUT and GND; A3 reads them back. Library: Adafruit MCP4725. #include <Arduino.h> #include <Adafruit_MCP4725.h> const byte ADDRESS = 0x62; // Adafruit 935: 0x62 (0x63 with the jumper closed) #ifdef ESP32 const float VDD = 3.3; // FireBeetle 2 ESP32-E: module on 3V3 #else const float VDD = 5.0; // Arduino UNO: module on 5V #endif const int CODES[] = {0, 1024, 2048, 4095}; // from 0 to 4095: 12 bits Adafruit_MCP4725 dac; void setup() { Serial.begin(115200); if (!dac.begin(ADDRESS)) { Serial.println(F("MCP4725 not responding: check the wires and the address")); while (true) delay(1000); } } void loop() { for (int code : CODES) { dac.setVoltage(code, false); // false: the EEPROM is left alone delay(50); Serial.print(F("code ")); Serial.print(code); Serial.print(F(" about ")); Serial.print(code * VDD / 4096, 2); // the datasheet formula Serial.print(F(" V A3: ")); #ifdef ESP32 Serial.print(analogReadMilliVolts(A3)); // millivolts Serial.println(F(" mV")); #else Serial.println(analogRead(A3)); // from 0 to 1023: about code / 4 #endif delay(4000); } }
Install the Adafruit MCP4725 library. dac.begin(0x62) returns false if the chip does not answer;
setVoltage(code, false) changes the output without touching the EEPROM. Open the Serial Monitor at 115200 baud: for each code
you get the expected voltage and the one read on A3.
- “MCP4725 not responding”: SDA and SCL swapped, VIN not connected, or the address is 0x63 because the “Addr” jumper is closed.
- The multimeter reads a little less than expected: measure the 3V3 of the board and redo the sum with that value instead of 3.3.
- A3 and the multimeter do not agree: the ESP32 ADC is less accurate than a multimeter (guide no. 14). Trust the multimeter.
6A wave on the Plotter
// A wave from the MCP4725 DAC and what A3 reads back, both on the Serial Plotter. #include <Arduino.h> #include <Adafruit_MCP4725.h> const byte ADDRESS = 0x62; // Adafruit 935 const bool SINE = false; // true: sine wave; false: triangle wave const int POINTS = 32; // points in each period Adafruit_MCP4725 dac; int point = 0; void setup() { Serial.begin(115200); if (!dac.begin(ADDRESS)) { Serial.println(F("MCP4725 not responding: check the wires and the address")); while (true) delay(1000); } } void loop() { int code; if (SINE) { code = 2048 + 2047 * sin(TWO_PI * point / POINTS); } else { // up for half a period, then down int half = POINTS / 2; int rise = point < half ? point : POINTS - point; code = rise * 4095L / half; } dac.setVoltage(code, false); delay(2); #ifdef ESP32 Serial.print(F("Sent_mV:")); Serial.print(code * 3300L / 4096); // module on 3V3 Serial.print(F(",A3_mV:")); Serial.println(analogReadMilliVolts(A3)); #else Serial.print(F("Sent:")); Serial.print(code / 4); // in A3 counts: 1023 = 5 V Serial.print(F(",A3:")); Serial.println(analogRead(A3)); #endif point = (point + 1) % POINTS; delay(40); }
About every 40 milliseconds the DAC takes a step: 32 points to go up and down. Open the Serial Plotter at 115200 baud. With
SINE = true the wave becomes a sine wave, centered on half the supply. The Plotter in IDE 2 shows the last 50
lines, a little more than one and a half waves.
Sent_mV is the voltage requested, A3_mV the one read back. The multimeter is slow and struggles to follow the wave: that is normal.7A value for power-up
With dac.setVoltage(2048, true) the code also goes into the
EEPROM, and the chip reloads it at every power-up: the output starts from that value straight away, even before the sketch runs. Handy
for a reference voltage that must always be there.
- An EEPROM write takes up to 50 ms (datasheet) and the number of writes is not unlimited: do it once, in
setupor when a button is pressed, never in theloop.
8With the Arduino UNO
- VIN to 5V, GND to GND, SCL and SDA to the SCL and SDA pins next to AREF, VOUT to A3. The output goes from 0 to 5 V.
- The sketch is the same: it compiles for the UNO and prints the A3 reading from 0 to 1023. The DAC and the UNO's ADC use the same 5 V as their reference, so the reading is about code ÷ 4 even if the USB port gives a little less than 5 V (our calculation).
- No jumper wires: the STEMMA QT cable carries power and I²C, with male plugs for the UNO and female sockets for the FireBeetle. Red V+, black GND, blue SDA, yellow SCL; VOUT stays on the header.
- A slow ramp, from 0 to 4095 in ten seconds, to follow on the multimeter.
- Read a potentiometer (guide no. 36) and copy its position to the DAC.
- Measure the output with 0 and with 4095: how far is it from 0 V and from VDD?
- a DAC gives a steady voltage, not pulses;
- VOUT = VDD × code ÷ 4096: the reference is the supply;
- with
truethe value stays in the EEPROM, but it should be written rarely.
Useful products
Other guides
- Resistor colour code and tolerances
- N-channel and P-channel MOSFETs
- Bipolar transistor (BJT) or MOSFET?
- Relays: how to drive them and how much current they draw
- THT and SMD/SMT components
- XIAO board pinouts
- LEDs: forward voltage and series resistor
- The multimeter: measuring voltage and current
- Wemos D1 mini: first steps
- MOSFET module with Arduino and ESP32
- Relay module with Arduino and ESP32
- 3D printing filaments: PLA, PETG and fibre-filled versions
- Analog and digital: the differences
- ADS1115: 16-bit converter with Arduino and ESP32
- From bits to real values: converting sensor readings
- DS3231 RTC clock with Arduino and ESP32
- Linear or switching regulator: which one to choose
- Step-down, step-up and buck-boost: choosing the right module
- Voltage divider: reading voltages higher than 5 V or 3.3 V
- Pull-up, pull-down and bounce: wiring a button properly
- 3.3 V and 5 V logic levels: getting an ESP32 and an Arduino to talk
- The I²C bus: addresses, pull-ups and the scanner
- Lithium batteries: voltages, charging and protection
- ESP32 power consumption and deep sleep: how long the battery lasts
- Temperature sensors compared
- ESP32: which pins to use and which to avoid
- Getting started with the ESP32 in Arduino IDE 2 and PlatformIO
- SSD1306 OLED display and 16×2 I²C LCD with Arduino and ESP32
- PIR motion sensor and HC-SR04 ultrasonic sensor
- Date and time from the Internet with the ESP32: NTP, Italian time zone and daylight saving time
- MicroSD data logger: saving measurements to CSV
- Solar-powered ESP32: panel, charger and the numbers for Italy
- Web server on the ESP32: control a relay from your phone
- Your first LED: wire it up and make it blink
- The button: let Arduino decide
- The potentiometer: turn a knob and read its value
- Soft light: adjusting the brightness of an LED
- The buzzer: playing notes and melodies
- The traffic light: three LEDs in sequence
- The thermometer: reading the temperature with the TMP36
- The servo motor: moving an arm with Arduino
- The night light: the photoresistor and your first decision
- The relay: switching on a light bulb with Arduino
- The LCD display: making Arduino write
- The electronic dice: your first complete project
- The RGB LED: every color from three lights
- The joystick: two-axis control with a shield
- The LED ring: twelve colors from a single pin
- The DC motor: driving it with a MOSFET
- Soil moisture: the plant that asks for water
- The stepper motor: turn exactly as far as you want
- The parking sensor: faster and faster beeps
- The 7-segment display: big, bright numbers
- Automatic watering: the plant that waters itself
- The keypad: a lock with a secret code
- Networked weather station: values and chart on your phone
- A Telegram bot on the ESP32: your home from your phone
- MQTT with the ESP32: the language of connected devices
- Updating the ESP32 over Wi-Fi (OTA)
- ESP-NOW: two ESP32 boards talking without a router
- WebSocket with the ESP32: the LED that follows the slider
- Readings on a Google sheet, with deep sleep
- WiFiManager: choose the Wi-Fi from your phone
- Bluetooth LE with the ESP32: the phone without a network
- The camera in your browser with the ESP32-S3
- A portable Bluetooth speaker you build yourself
- Bluetooth desktop speakers, 2 × 20 W
- Stereo MP3 player with two buttons
- The shelf that plays music: a Visaton exciter
- How to solder well
- How much does it draw? Current and power with the INA219
- Load-cell scale with the HX711
- CO2 traffic light: when to open the windows
- RFID card door opener
- GPS: track logger on microSD
- LoRa: how far does it go? A range test with two boards
- Battery weather sign with an e-paper display
- Web radio with an ESP32 and an I2S amplifier
- Getting started with the Raspberry Pi Pico 2 in MicroPython
- Your first amplifier: the LM386 and a speaker
- Talking Arduino: the DFR0760 speech synthesis module
- The spectrum of your music on an LED matrix
- Drawing with 64 LEDs: the 8×8 matrix with the MAX7219
- The heartbeat on Arduino: an ECG with the AD8232