blue baa3ef7690 3-board relay firmware: sink + I2S + dual source
Implements the only architecture that can relay iPhone audio to two BT
speakers at once (one ESP32 cannot be A2DP sink+source, and a source holds
only one link):

  iPhone ))BT)) [Board A: A2DP sink -> I2S master]
                      ==I2S bus==> [Board B: I2S slave -> A2DP source] ))BT)) JBL
                      ==I2S bus==> [Board C: I2S slave -> A2DP source] ))BT)) Cardo

- src/board_sink.cpp   : A2DP sink "BikeAudio", forwards decoded PCM to an
  I2S master bus (BCK=5, WS=25, DATA=23); follows negotiated sample rate.
- src/board_source.cpp : I2S slave (BCK=19, WS=18, DATA=22) -> A2DP source,
  target speaker via TARGET_SPEAKER build flag; pads silence on underrun.
- platformio.ini       : 3 envs (sink, source_jbl, source_cardo) sharing an
  [env] base; sources differ only by TARGET_SPEAKER. build_src_filter selects
  the per-board source file. Libs pinned as before.
- README_RELAY.md      : wiring table, I2S bus topology, flash order, pairing,
  and the speaker-sync limitation.

Replaces the single-board src/main.cpp (architecturally impossible). All
three envs build clean. Hardware flash + wiring next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 11:35:12 -04:00
2026-06-09 12:51:08 -04:00
2026-06-09 12:51:08 -04:00

BikeAudio — Setup Guide

iPhone → ESP32 DevKitC v4 → JBL Charge 5 + Cardo Packtalk Edge (wireless, no latency concern)


1. Arduino IDE Setup

Install Arduino IDE

Download from https://www.arduino.cc/en/software (version 2.x)

Add ESP32 Board Support

  1. Open Arduino IDE → Preferences
  2. Add this URL to "Additional boards manager URLs": https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  3. Tools → Board → Boards Manager → search "esp32" → install "esp32 by Espressif Systems" VERSION 2.0.17 IMPORTANT: Use 2.0.17, NOT 3.x — the Bluetooth stack has regressions in 3.x

Board Settings (Tools menu)

Board: ESP32 Dev Module Partition Scheme: Huge APP (3MB No OTA/1MB SPIFFS) <-- CRITICAL Upload Speed: 921600 CPU Frequency: 240MHz Flash Frequency: 80MHz Flash Mode: QIO Flash Size: 4MB (32Mb)


2. Install Libraries

Option A — Arduino IDE Library Manager

  1. Sketch → Include Library → Manage Libraries
  2. Search "ESP32-A2DP" → install by Phil Schatzmann
  3. Search "arduino-audio-tools" → install by Phil Schatzmann

Option B — Manual (if Library Manager version is outdated)

Download ZIPs from: https://github.com/pschatzmann/ESP32-A2DP/archive/refs/heads/main.zip https://github.com/pschatzmann/arduino-audio-tools/archive/refs/heads/main.zip Sketch → Include Library → Add .ZIP Library → select each ZIP


3. First Time Pairing (do this once)

The order matters.

  1. Forget JBL Charge 5 and Cardo from your iPhone Bluetooth settings (iPhone must NOT be connected to them — ESP32 needs to claim them)

  2. Put JBL Charge 5 in pairing mode Hold the Bluetooth button until you hear the pairing sound

  3. Put Cardo Packtalk Edge in pairing mode Hold the phone button for 3 seconds until LED flashes

  4. Open BikeAudio.ino in Arduino IDE, plug in ESP32 via USB, flash it

  5. Open Serial Monitor (115200 baud) — you'll see: [JBL] Connecting... [JBL] Connected [CARDO] Connecting... [CARDO] Connected [SINK] Advertising as 'BikeAudio'...

  6. On iPhone → Settings → Bluetooth → connect to "BikeAudio"

  7. Play audio — both JBL and Cardo should output simultaneously

After first pairing, all three devices remember each other. On next boot, everything reconnects automatically within ~10 seconds.


4. Daily Use (after first pairing)

  1. Power on ESP32 (USB powerbank on bike, or small LiPo)
  2. Turn on JBL Charge 5
  3. Power on Cardo helmet
  4. iPhone auto-reconnects to "BikeAudio" (or tap it in BT settings)
  5. Play music/nav audio — both speakers play

No buttons, no app, no wires.


5. Customization

Edit these lines at the top of BikeAudio.ino:

#define SINK_NAME "BikeAudio" // name iPhone sees #define JBL_NAME "JBL Charge 5" // must match exactly #define CARDO_NAME "Tangerine EDGE" // must match exactly

To find exact BT name of a device: iPhone → Settings → Bluetooth → tap the device name shown there


6. Powering the ESP32 on the Bike

Options (all wireless, no wires to phone): A. USB powerbank in jacket pocket or tank bag — plug ESP32 via USB-C cable B. Small LiPo 3.7V 1000mAh + TP4056 charging module (~$5 total) — fully self-contained C. Tap 5V from bike's USB port if you have one

The ESP32 draws ~200-300mA during active BT streaming. A 1000mAh LiPo lasts ~3-4 hours.


7. Troubleshooting

JBL or Cardo not connecting:

  • Make sure they are NOT paired to your iPhone anymore
  • Put them in pairing mode before powering the ESP32
  • Check exact name match in the #define lines
  • Open Serial Monitor and watch the output

Compilation errors:

  • Double-check you're on ESP32 core 2.0.17 not 3.x
  • Double-check Partition Scheme = Huge APP
  • Make sure both pschatzmann libraries are installed

Audio cutting out:

  • Increase BUFFER_SIZE from 41024 to 81024 in the sketch
  • Keep ESP32 away from other 2.4GHz sources (WiFi routers)

Only one speaker plays:

  • The second source connection may have failed on boot
  • Check Serial Monitor — it will say which device is waiting
  • Power cycle everything and let them reconnect

8. How the Audio Path Works

iPhone sends standard Bluetooth A2DP audio (SBC codec, 44.1kHz stereo 16-bit). ESP32 receives it as a sink (like a Bluetooth speaker). Each audio frame is written into two separate ring buffers simultaneously. JBL source reads from buffer 1, Cardo source reads from buffer 2. Both get identical audio at the same time = in sync.

Latency added by the relay: ~50-150ms on top of normal BT latency. Since you said latency is fine, this is not an issue. Both speakers receive audio with the same added delay so they stay in sync with each other.

Description
No description provided
Readme 150 KiB
Languages
C++ 100%