How to Configure Analog Output Using MCP4725 DAC in DIY Sim-Racing FFB Pedal

To configure analog output using the MCP4725 DAC, define Using_analog_output_ESP32_S3 in Main.h, wire the DAC to GPIO 4 (SDA) and GPIO 5 (SCL), and call dac.setVoltage() with a 12-bit value scaled to 0–4095.

The diy-sim-racing-ffb-pedal firmware supports external digital-to-analog conversion via the MCP4725 (or MCP4728) I²C DAC. This provides a clean 0–5 V analog signal for sim-racing interfaces that expect traditional voltage levels rather than PWM. Below is the complete configuration guide derived from the source code in Firmware_for_V3/PedalFirmware/.

Enable MCP4725 Support in Firmware Configuration

All DAC-specific code is wrapped in conditional compilation blocks. You must explicitly enable the analog output path before the firmware will initialize the I²C bus or the DAC object.

Defining the Preprocessor Flag in Main.h

Open the board-specific header file located at Firmware_for_V3/PedalFirmware/include/Main.h. Locate the analog output section (around lines 95–96) and uncomment the following definition:

#define Using_analog_output_ESP32_S3

If you are compiling for a different board variant, you can also inject this flag via a custom platformio_override.ini file without modifying the source headers. Rebuild the firmware after making this change; the compiler will now include the MCP4725 initialization and update logic.

Hardware Wiring and Pin Configuration

The firmware uses a dedicated I²C bus for the DAC to avoid conflicts with other peripherals. The default pin mapping is defined in Main.h (lines 28–30):

ESP32 Pin MCP4725 Pin Function
GPIO 4 (MCP_SDA) SDA I²C Data
GPIO 5 (MCP_SCL) SCL I²C Clock
3.3 V / 5 V VCC Power Supply
GND GND Common Ground

Connect the MCP4725’s A0 pin to GND to set the default I²C address to 0x60. If you need multiple DACs on the same bus, tie A0 to VCC for address 0x61, or use the MCP4728 variant which supports four channels at addresses 0x600x67.

Initialize the MCP4725 DAC in Code

The firmware automatically probes the I²C bus during the setup() phase in Firmware_for_V3/PedalFirmware/src/Main.cpp (lines 650–690). It attempts to detect the DAC at any address in the range 0x600x67:

TwoWire MCP4725_I2C = TwoWire(1);
MCP4725_I2C.begin(MCP_SDA, MCP_SCL, 400000);

uint8_t i2c_address[8] = {0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67};
int found_address = 0;

for (int i = 0; i < 8; ++i) {
    MCP4725_I2C.beginTransmission(i2c_address[i]);
    if (MCP4725_I2C.endTransmission() == 0) {
        found_address = i;
        break;
    }
}

if (!dac.begin(i2c_address[found_address], &MCP4725_I2C)) {
    Serial.println("Couldn't find MCP, will not have analog output");
    MCP_status = false;
} else {
    Serial.println("MCP founded");
    MCP_status = true;
}

The dac object is an instance of Adafruit_MCP4725. If initialization fails, MCP_status is set to false and the firmware continues without analog output, ensuring the pedal remains functional even if the hardware is absent.

Write Analog Output Values at Runtime

During each control-loop iteration, the firmware converts the normalized pedal position (0–10000 representing 0–100%) into a 12-bit DAC value (0–4095). This logic resides in Main.cpp around lines 1545–1555:

#ifdef Using_analog_output_ESP32_S3
if (MCP_status) {
    // Scale to 0-4095, limit to 90% of full scale (≈4.5V) to protect the MCU
    int dac_value = (int)(joystickNormalizedToInt32 * 4096 * 0.9 / 10000);
    dac.setVoltage(dac_value, false);   // false = no EEPROM write
}
#endif

The multiplication by 0.9 caps the output at approximately 4.5 V, providing a safety margin for downstream circuits. The false parameter prevents unnecessary EEPROM writes, extending the lifespan of the DAC’s internal non-volatile memory.

Alternative: Native ESP32 PWM DAC

If you prefer not to use external hardware, the firmware supports the ESP32’s built-in 8-bit PWM DAC via dacWrite(). Enable this path by defining Using_analog_output (without the _ESP32_S3 suffix) in Main.h. The runtime write occurs in the same control-loop location, but uses the native ESP32 driver instead of the MCP4725 library.

Summary

  • Enable the feature by uncommenting #define Using_analog_output_ESP32_S3 in Firmware_for_V3/PedalFirmware/include/Main.h.
  • Wire the DAC to GPIO 4 (SDA) and GPIO 5 (SCL) with pull-up resistors and appropriate power supply.
  • Library dependency is handled automatically via adafruit/Adafruit MCP4725 @ ^2.0.2 in platformio.ini.
  • Initialization scans I²C addresses 0x60–0x67 in Main.cpp (lines 650–690) and sets MCP_status on success.
  • Runtime updates convert pedal position to 12-bit values and call dac.setVoltage() at lines 1545–1555, capped at 90% for circuit protection.

Frequently Asked Questions

What is the default I²C address for the MCP4725?

The default address is 0x60 when the A0 pin is tied to ground. If you connect A0 to VCC, the address becomes 0x61. The firmware automatically scans the full range 0x60–0x67 to accommodate multiple DACs or the MCP4728 variant.

Can I use the MCP4728 instead of the MCP4725?

Yes. The MCP4728 is a quad-channel version that uses the same I²C address range (0x60–0x67). The bridge firmware (Firmware_for_V3/BridgeFirmware/src/Main.cpp) demonstrates MCP4728 usage with the setChannelValue() method instead of setVoltage(). The initialization and wiring remain identical.

Why is the DAC output limited to 90% of full scale?

The firmware multiplies the calculated DAC value by 0.9 to cap the output at approximately 4.5 V instead of the full 5 V. This safety margin protects downstream microcontrollers or analog inputs from over-voltage conditions while still providing sufficient resolution for pedal position sensing.

Which file contains the DAC initialization code?

The initialization logic resides in Firmware_for_V3/PedalFirmware/src/Main.cpp between lines 650–690. This block creates the TwoWire instance, scans for the MCP4725 at addresses 0x60–0x67, and calls dac.begin() to establish communication before setting the global MCP_status flag.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →