# ModulosDSP — ESP32-S3 WiFi Bridge for ADAU14xx DSP An Arduino sketch for the **Waveshare ESP32-S3 Zero** that provides a WiFi interface to Analog Devices ADAU1401/1701 DSP chips. Runs on the companion **MSD ADAU14-1701 Adapter** PCB. ## Feature Summary | Feature | Endpoint / Port | Description | | ------- | --------------- | ----------- | | **SigmaTCP bridge** | TCP 8086 | Wireless SigmaStudio ↔ DSP bridge over I²C | | **EEPROM upload** | `POST /upload` | Flash DSP firmware to 24C256 with optional CRC32 verify | | **OTA firmware update** | `POST /ota_do` | Flash new ESP32 firmware over WiFi | | **DSP live status** | `GET /dsp_status` | Core Register, running flag, GPIO, ADC 0–3 — polled every 2 s | | **DSP soft reset** | `POST /dsp_reset` | Restart DSP execution without reloading EEPROM | | **GPIO register control** | `GET/POST /gpio` | Read and write GPIO All Register; GP0–GP3 toggle buttons in UI | | **Runtime parameter tuner** | `GET/POST /param` | Read/write any param RAM address live; safeload writes for glitch-free updates | | **Parameter header export** | — | Client-side export of `dsp_params.h` with `#define` constants for all tuner rows | | **NVS credential storage** | — | WiFi credentials stored in NVS; survive reboots | | **AP config portal** | SoftAP `ModulosDSP-Setup` | Browser-based WiFi setup on first boot or connect failure | | **WiFi credential reset** | `POST /wifi_reset` | Clear NVS and reboot into AP setup mode | | **mDNS hostname** | — | Device reachable at `modulos-dsp.local` (Bonjour/Zeroconf) | | **WiFi watchdog** | — | Auto-reconnect every 5 s; restores TCP socket and mDNS on recovery | | **TCP idle timeout** | — | Sessions auto-close after 30 s of inactivity | | **Safeload flush** | — | Pending safeload slots flushed at session start and disconnect | | **I²C error propagation** | — | Write failures reported in SigmaTCP ACK; logged to Serial | | **50 KB TCP receive buffer** | — | Decoupled drain/process loop prevents TCP ZeroWindow stall | | **NeoPixel status LED** | — | Colour-coded activity indicator (see Status LED section) | | **Optional LCD** | — | 20×4 hd44780 LCD for boot status and IP; graceful fallback if absent | --- ## Hardware | Component | Part | Notes | | --------- | ---- | ----- | | Microcontroller | Waveshare ESP32-S3 Zero | Onboard WS2812 NeoPixel on GPIO 21 | | DSP | ADAU1401 / ADAU1701 | I²C address 0x68 (7-bit: 0x34) | | EEPROM | 24C256 (32 KB) | I²C address 0xA0 (7-bit: 0x50) | | Display | 20×4 hd44780 I²C LCD | Boot status and IP address (optional) | ### Pin Assignments | Signal | GPIO | | ------ | ---- | | I²C SDA | 13 | | I²C SCL | 12 | | Status LED (NeoPixel) | 21 (built-in) | The DSP, EEPROM, and LCD all share the same I²C bus at 400 kHz. The LCD is optional — if not detected at boot the sketch continues without it. --- ## Arduino Libraries Required Install via **Sketch → Include Library → Manage Libraries**: | Library | Purpose | | ------- | ------- | | `WiFi` | ESP32 WiFi (built-in with ESP32 core) | | `WebServer` | HTTP server (built-in) | | `Wire` | I²C master (built-in) | | `Update` | OTA flash write (built-in with ESP32 core) | | `LittleFS` | Embedded filesystem for web assets (built-in) | | `Adafruit NeoPixel` | WS2812 status LED | | `hd44780` | I²C LCD driver | **Board:** select **ESP32S3 Dev Module** (or Waveshare ESP32-S3 Zero if your board package includes it) via the ESP32 Arduino core. --- ## Configuration ### WiFi Credentials Credentials are stored in NVS (non-volatile flash) using the `Preferences` library — there are no hardcoded SSID or password in the source code. **First boot / no credentials stored:** 1. The device starts a SoftAP named **`ModulosDSP-Setup`** (open, no password). 2. Connect your phone or laptop to `ModulosDSP-Setup`. 3. Open `http://192.168.4.1` in a browser — a setup page appears. 4. Enter your WiFi SSID and password and click **Save & Connect**. 5. The device saves the credentials to NVS and reboots, joining your network. **If the connection fails** (wrong password, network out of range) the device falls back to AP mode automatically. **Resetting credentials:** on the Device Management page (`/`) click **Reset WiFi Credentials**. The device clears NVS and reboots into AP mode. ### Hostname ```cpp const char* hostname = "modulos-dsp"; // resolves as modulos-dsp.local ``` The hostname is the only compile-time network constant. It is registered via mDNS (Bonjour/Zeroconf), so once on the network the device is reachable at `modulos-dsp.local`. Change it if you run more than one unit on the same network. --- ## Status LED The onboard NeoPixel (GPIO 21) shows current activity at a glance: | Color | Meaning | | ----- | ------- | | Off | Idle — no TCP client connected | | Green | DSP write in progress (SigmaTCP) | | Blue | DSP read in progress (SigmaTCP) | | Yellow | EEPROM write via SigmaTCP | | Magenta | EEPROM upload via HTTP | | Cyan | OTA firmware flash in progress | | Red flash | Error (I²C failure, buffer overflow, bad packet) | --- ## Using with SigmaStudio 1. Power on the board. The LCD displays the assigned IP address; Serial (115200 baud) also prints it alongside the mDNS hostname. 2. In SigmaStudio, open **Settings → Hardware Configuration**. 3. Add a **USBi** interface and switch the connection type to **SigmaTCP**. 4. Enter `modulos-dsp.local` (or the IP address) and port **8086**. 5. Click **Link → Compile Download** — SigmaStudio pushes firmware to the DSP wirelessly. Sessions that go idle for 30 seconds are closed automatically, freeing the bridge for a new connection. The bridge handles both **direct writes** and **safeload writes** (glitch-free atomic parameter updates on a live DSP). If the connection drops mid-safeload, any partial pending slots are flushed to parameter RAM on disconnect so the DSP's internal safeload counter is clean for the next session. --- ## EEPROM Uploader (Web UI) Navigate to `http:///` in any browser: 1. Click **Choose File** and select a SigmaStudio-exported binary. 2. Optionally tick **Verify (CRC32)** to read back and confirm the write. 3. Click **Upload** — the Magenta LED pulses during the write. The 24C256 holds **32 KB**. The DSP reads this EEPROM at power-up via its SELFBOOT pin, allowing standalone operation without the ESP32 actively driving it. --- ## Device Management (`/`) The main page at `http://modulos-dsp.local/` is the device management hub: **DSP Status** — live card polling `GET /dsp_status` every 2 seconds: | Field | Register | Description | | ----- | -------- | ----------- | | Core Register | 0x081C | Full 16-bit register value (hex) | | Running | Core Register bit 0 | Green badge when DSP is executing | | GPIO | 0x0808 | GPIO All Register value (hex) | | ADC 0–3 | 0x0809–0x080C | Raw ADC input values (hex) | **EEPROM Upload** — flash DSP firmware to the 24C256: 1. Click **Choose File** and select a SigmaStudio-exported binary. 2. Optionally tick **Verify (CRC32)** to read back and confirm the write. 3. Click **Upload and Program** — the Magenta LED pulses during the write. **DSP Control** — click **Soft Reset DSP** to restart DSP execution without reloading from EEPROM. **GPIO Control** — GP0–GP3 toggle buttons write to GPIO All Register (0x0808). Buttons go green when the pin is high. Output-only pins respond (configured via MpCfg registers). **WiFi Settings** — click **Reset WiFi Credentials** to clear NVS and reboot into AP setup mode. **Firmware Update** — link to the OTA page (`/ota`) for flashing new ESP32 firmware. ## Firmware Update Page (`/ota`) Navigate to `http://modulos-dsp.local/ota` to flash new ESP32 firmware: 1. In Arduino IDE, export the compiled binary: **Sketch → Export Compiled Binary** (produces a `.bin` file). 2. Click **Choose File** and select the `.bin`. 3. Click **Flash Firmware** — the Cyan LED indicates the write is in progress. 4. On success the device reboots automatically. The page redirects to `/` after 5 seconds. --- ## How It Works ### SigmaTCP Bridge SigmaTCP is the protocol used by Analog Devices' SigmaStudio to talk to hardware like the USBi programmer. This sketch implements the server side over TCP/WiFi and forwards all register traffic to the DSP or EEPROM via I²C. **WRITE (opcode 0x09):** - 10-byte header: command, safeload flag, placement, totalLen, chipAddr, dataLen, startAddress - `totalLen` is validated against `WRITE_HDR_LEN + dataLen` before buffering — mismatched packets drop the connection - Payload forwarded to DSP or EEPROM via I²C - I²C errors are reported in the 4-byte ACK `{0x09, 0x00, 0x04, status}` so SigmaStudio sees real write failures **READ (opcode 0x0A):** - 8-byte header: command, totalLen, chipAddr, dataLen, startAddress - I²C data read in 32-byte chunks - 6-byte response header (`0x0B, totalLen_hi, totalLen_lo, status, dataLen_hi, dataLen_lo`) + payload sent back - On I²C failure, zeros are sent rather than dropping the connection (allows SigmaStudio to probe an unresponsive DSP) **Safeload:** When the safeload flag is set, parameters are queued into the DSP's 5 hardware safeload slots and committed atomically by writing `0x003C` to the Core Register. This avoids audio clicks when updating parameters on a running DSP. Any pending safeload slots are flushed at both the start and end of every TCP session to keep the DSP's internal counter in sync. The TCP receive loop drains all available bytes into a 50 KB buffer every iteration (keeping the TCP receive window open), then processes complete packets from the buffer. This correctly handles large program downloads that span multiple TCP segments. ### WiFi Watchdog The sketch monitors WiFi status every 5 seconds. If the connection drops it attempts `WiFi.reconnect()` on each check. On recovery it restarts the TCP server socket and updates the LCD with the current IP address. ### EEPROM Writes Writes are chunked to ≤28 bytes, aligned to 64-byte page boundaries. After each chunk, the firmware polls the EEPROM for ACK (up to 120 ms) to wait for its internal write cycle before continuing. ### Fixed-Point Format The ADAU1401 uses **5.23 fixed-point** for parameter RAM (4 bytes per word). Safeload registers add a leading `0x00` padding byte, making them 5 bytes. The `DataConversion` class handles conversion from `float`, `int32_t`, and other C types. --- ## File Structure ```text ModulosDSP_101/ ├── ModulosDSP_101.ino Main sketch: WiFi, TCP/HTTP servers, I²C bridge ├── DSPWriter.h DSP register write interface and address map ├── DSPWriter.cpp I²C write and safeload implementations ├── DataConversion.h 5.23 fixed-point conversion declarations ├── DataConversion.cpp Fixed-point conversion implementations ├── index_html.h Main device management UI (DSP status, EEPROM upload, controls) ├── ota_html.h OTA firmware flash form ├── params_html.h Runtime parameter tuner UI └── ap_html.h SoftAP WiFi config portal (first-boot / failed-connect) ``` --- ## ADAU1401 Address Map | Region | Address Range | I²C bytes per register | | ------ | ------------- | ---------------------- | | Parameter RAM | 0x0000–0x03FF | 4 | | Program RAM | 0x0400–0x07FF | 5 | | Interface Registers 0–7 | 0x0800–0x0807 | 1 | | GPIO Register | 0x0808 | 1 | | ADC 0–3 (read-only) | 0x0809–0x080C | 1 | | Safeload Data 0–4 | 0x0810–0x0814 | 5 | | Safeload Address 0–4 | 0x0815–0x0819 | 2 | | Core Register | 0x081C | 2 (R0 reset) | | MpCfg 0–1 | 0x0820–0x0821 | 1 | | Analog Power Down | 0x0822 | 1 | | Analog Interface 0 | 0x0824 | 1 | --- ## Build / Version The build identifier uses `YYMMDD_rev` format (e.g. `260304_13` = 4 March 2026, revision 13). It is displayed on the LCD and printed to Serial at boot. When cutting a new build, update both the file header comment and the `version` constant in `ModulosDSP_101.ino`. --- ## References - [SigmaStudio TCP interface for ESP32 — Analog Devices forum](https://ez.analog.com/dsp/sigmadsp/f/q-a/164008/sharing-tcpi-interface-for-sigma-studio-using-esp8266-or-esp32) - [roTDSP — ADAU1401 carrier board](https://github.com/roTbear/roTDSP) - [SigmaDSP Arduino library (MCUdude)](https://github.com/MCUdude/SigmaDSP) - [Elektor AudioDSP — ESP32 + ADAU1701](https://github.com/ClemensAtElektor/Elektor_AudioDSP) - [How to program an Analog Devices DSP](https://daumemo.com/how-to-program-an-analog-devices-dsp/)