Rename: product -> "Resound"; firmware roles -> hud/sink/broadcaster
Product name BikeAudio -> "Resound" (the device isn't bike-specific — works at
home, backyard, camping, parties, group rides). This is the A2DP advertise name
the phone connects to: sink.start("Resound"). All banners/comments + README
updated. (After reflashing the sink, the phone must forget "BikeAudio" and
connect to "Resound".)
Firmware vocabulary clarified (the old hub/sink/source was confusing —
"source" read backwards since those boards SEND audio):
hub_s3.cpp / env hub_s3 -> hud.cpp / env hud
board_sink.cpp -> sink.cpp (env sink)
board_source.cpp -> broadcaster.cpp (envs broadcaster_headset
0x11, broadcaster_speaker1 0x10,
broadcaster_guest 0x12)
hub_proto.h -> bus_proto.h
default_envs = sink + the three broadcasters. All envs build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
dca7d3ba46
commit
b5d1169392
@ -1,4 +1,4 @@
|
|||||||
# BikeAudio — 3-board Bluetooth relay
|
# Resound — 3-board Bluetooth relay
|
||||||
|
|
||||||
Relays iPhone audio to **two** Bluetooth speakers (JBL Charge 5 + Cardo "Tangerine EDGE")
|
Relays iPhone audio to **two** Bluetooth speakers (JBL Charge 5 + Cardo "Tangerine EDGE")
|
||||||
at the same time.
|
at the same time.
|
||||||
@ -89,7 +89,7 @@ B and C start reading it.
|
|||||||
1. Put the **JBL** and **Cardo** in pairing mode.
|
1. Put the **JBL** and **Cardo** in pairing mode.
|
||||||
2. Power Board B and Board C — each connects to its speaker by name
|
2. Power Board B and Board C — each connects to its speaker by name
|
||||||
(auto-reconnects on later power-ups).
|
(auto-reconnects on later power-ups).
|
||||||
3. Power Board A; on the iPhone, connect to **"BikeAudio"**.
|
3. Power Board A; on the iPhone, connect to **"Resound"**.
|
||||||
4. Play audio — both speakers should output together.
|
4. Play audio — both speakers should output together.
|
||||||
|
|
||||||
## Known limitations
|
## Known limitations
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
; BikeAudio — 3-board relay (iPhone -> Board A sink -> I2S -> Boards B/C sources -> JBL + Cardo)
|
; BikeAudio — split-board Bluetooth audio relay.
|
||||||
;
|
;
|
||||||
; One ESP32 cannot be an A2DP sink and source at once, and an A2DP source can
|
; Roles (one ESP32 can't be A2DP sink+source, and a source reaches one speaker,
|
||||||
; reach only one speaker — so the work is split across three boards that share
|
; so the work is split across boards sharing an I2S audio bus + an I2C control bus):
|
||||||
; an I2S bus. See README.md for wiring and the flash order.
|
; SINK - iPhone connects here (A2DP in) -> drives the I2S bus as master
|
||||||
|
; BROADCASTER - reads I2S -> A2DP-streams to ONE speaker (one per channel)
|
||||||
|
; HUD - ESP32-S3 round touch LCD; control panel over I2C
|
||||||
;
|
;
|
||||||
; Build all: pio run
|
; Channels are brand-agnostic: Headset / Speaker 1 / Guest (discovery picks the
|
||||||
; Build one board: pio run -e sink | -e source_jbl | -e source_cardo
|
; real device per channel). Build one: pio run -e sink | -e broadcaster_headset
|
||||||
|
; | -e broadcaster_speaker1 | -e broadcaster_guest | -e hud
|
||||||
;
|
;
|
||||||
; Common specs preserved from the original sketch: ESP32 Arduino core 2.0.x via
|
; Audio boards: ESP32 core 2.0.x via espressif32 ~6.6.0, esp32dev, huge_app (BT stack).
|
||||||
; espressif32 ~6.6.0, esp32dev, huge_app partition (BT stack), 115200 monitor.
|
|
||||||
|
|
||||||
[platformio]
|
[platformio]
|
||||||
default_envs = sink, source_jbl, source_cardo
|
default_envs = sink, broadcaster_headset, broadcaster_speaker1, broadcaster_guest
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
platform = espressif32 @ ~6.6.0
|
platform = espressif32 @ ~6.6.0
|
||||||
@ -24,47 +26,43 @@ lib_deps =
|
|||||||
https://github.com/pschatzmann/ESP32-A2DP#42601717cd70d5300c9b519f3c2bf1d64d77ea2b
|
https://github.com/pschatzmann/ESP32-A2DP#42601717cd70d5300c9b519f3c2bf1d64d77ea2b
|
||||||
https://github.com/pschatzmann/arduino-audio-tools#64b64dcb9bde18a0a17766eeb6529c3a53d920a8
|
https://github.com/pschatzmann/arduino-audio-tools#64b64dcb9bde18a0a17766eeb6529c3a53d920a8
|
||||||
|
|
||||||
; --- Board A: A2DP sink (iPhone) -> I2S master --------------------------------
|
; --- SINK: A2DP sink (iPhone) -> I2S master ----------------------------------
|
||||||
[env:sink]
|
[env:sink]
|
||||||
build_src_filter = +<board_sink.cpp>
|
build_src_filter = +<sink.cpp>
|
||||||
|
|
||||||
; --- Board B: I2S slave -> A2DP source -> JBL Charge 5 ------------------------
|
; --- BROADCASTER "Headset" -> Cardo (I2C 0x11) -------------------------------
|
||||||
[env:source_jbl]
|
[env:broadcaster_headset]
|
||||||
build_src_filter = +<board_source.cpp>
|
build_src_filter = +<broadcaster.cpp>
|
||||||
build_flags = '-DTARGET_SPEAKER="JBL Charge 5"' -DHUB_I2C_ADDR=0x10
|
|
||||||
|
|
||||||
; --- Board C: I2S slave -> A2DP source -> Cardo (Tangerine EDGE) --------------
|
|
||||||
[env:source_cardo]
|
|
||||||
build_src_filter = +<board_source.cpp>
|
|
||||||
build_flags = '-DTARGET_SPEAKER="Tangerine EDGE"' -DHUB_I2C_ADDR=0x11
|
build_flags = '-DTARGET_SPEAKER="Tangerine EDGE"' -DHUB_I2C_ADDR=0x11
|
||||||
|
|
||||||
; --- Board D: I2S slave -> A2DP source -> GUEST speaker (antenna board) -------
|
; --- BROADCASTER "Speaker 1" -> JBL (I2C 0x10) -------------------------------
|
||||||
; Occasional passenger speaker, furthest away (hence the onboard antenna).
|
[env:broadcaster_speaker1]
|
||||||
; No hardcoded name — pick the speaker via the hub's scan UI (Phase 3).
|
build_src_filter = +<broadcaster.cpp>
|
||||||
[env:source_guest]
|
build_flags = '-DTARGET_SPEAKER="JBL Charge 5"' -DHUB_I2C_ADDR=0x10
|
||||||
build_src_filter = +<board_source.cpp>
|
|
||||||
|
; --- BROADCASTER "Guest" -> antenna board, occasional passenger (I2C 0x12) ---
|
||||||
|
; No hardcoded device — pick the speaker via the HUD scan UI (discovery).
|
||||||
|
[env:broadcaster_guest]
|
||||||
|
build_src_filter = +<broadcaster.cpp>
|
||||||
build_flags = '-DTARGET_SPEAKER="Guest"' -DHUB_I2C_ADDR=0x12
|
build_flags = '-DTARGET_SPEAKER="Guest"' -DHUB_I2C_ADDR=0x12
|
||||||
|
|
||||||
; --- Hub: ESP32-S3-Touch-LCD-1.28 (round GC9A01 LCD + CST816S touch) ----------
|
; --- HUD: ESP32-S3-Touch-LCD-1.28 (round GC9A01 LCD + CST816S touch) ----------
|
||||||
; Different chip (esp32s3) from the relay boards. Drives the UI; later the
|
; Different chip (esp32s3); LVGL UI + I2C master to the audio boards.
|
||||||
; wired control bus to Boards B/C. NOT in default_envs — build with -e hub_s3.
|
; NOT in default_envs — build with: pio run -e hud
|
||||||
[env:hub_s3]
|
[env:hud]
|
||||||
platform = espressif32 @ ~6.6.0
|
platform = espressif32 @ ~6.6.0
|
||||||
board = esp32-s3-devkitc-1
|
board = esp32-s3-devkitc-1
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board_upload.flash_size = 4MB
|
board_upload.flash_size = 4MB
|
||||||
board_build.partitions = default.csv
|
board_build.partitions = default.csv
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
build_src_filter = +<hub_s3.cpp>
|
build_src_filter = +<hud.cpp>
|
||||||
lib_deps =
|
lib_deps =
|
||||||
lovyan03/LovyanGFX@^1.1.16
|
lovyan03/LovyanGFX@^1.1.16
|
||||||
lvgl/lvgl@^8.3.11
|
lvgl/lvgl@^8.3.11
|
||||||
; LVGL configured via build flags (LV_CONF_SKIP -> defaults + overrides), so
|
; LVGL via build flags (LV_CONF_SKIP -> defaults + overrides); no lv_conf.h.
|
||||||
; there is no separate lv_conf.h to maintain. 16-bit colour, byte-swapped for
|
; Keep flag values free of shell-special chars (parens/spaces). Tick via
|
||||||
; the SPI panel; millis() tick; the big Montserrat fonts for glanceable text.
|
; lv_tick_inc() in loop() (no LV_TICK_CUSTOM, which needs a parenthesised expr).
|
||||||
; NOTE: keep flag values free of shell-special chars (parens/spaces) — they go
|
|
||||||
; through a shell. LV_MEM_SIZE as a plain int; tick driven via lv_tick_inc() in
|
|
||||||
; loop() (no LV_TICK_CUSTOM, which needs a parenthesised millis() expr).
|
|
||||||
build_flags =
|
build_flags =
|
||||||
-DLV_CONF_SKIP=1
|
-DLV_CONF_SKIP=1
|
||||||
-DLV_COLOR_DEPTH=16
|
-DLV_COLOR_DEPTH=16
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* BikeAudio — Boards B & C : I2S SLAVE -> [FIFO delay] -> A2DP SOURCE
|
* Resound — Boards B & C : I2S SLAVE -> [FIFO delay] -> A2DP SOURCE
|
||||||
*
|
*
|
||||||
* Reads PCM from the shared I2S bus (clocked by Board A) into a FIFO, and an
|
* Reads PCM from the shared I2S bus (clocked by Board A) into a FIFO, and an
|
||||||
* A2DP source drains the FIFO to one Bluetooth speaker. The FIFO sits a fixed
|
* A2DP source drains the FIFO to one Bluetooth speaker. The FIFO sits a fixed
|
||||||
@ -24,10 +24,10 @@
|
|||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
|
||||||
#include "hub_proto.h"
|
#include "bus_proto.h"
|
||||||
|
|
||||||
#ifndef TARGET_SPEAKER
|
#ifndef TARGET_SPEAKER
|
||||||
#define TARGET_SPEAKER "BikeAudio-Speaker"
|
#define TARGET_SPEAKER "Resound-Speaker"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// I2C slave address (which speaker this board controls). Set per-env via build
|
// I2C slave address (which speaker this board controls). Set per-env via build
|
||||||
@ -305,7 +305,7 @@ void on_conn_state(esp_a2d_connection_state_t state, void *obj) {
|
|||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
delay(500);
|
delay(500);
|
||||||
Serial.printf("=== BikeAudio Source -> '%s' (FIFO delay, %ums cushion) ===\n",
|
Serial.printf("=== Resound Source -> '%s' (FIFO delay, %ums cushion) ===\n",
|
||||||
TARGET_SPEAKER, BASE_DELAY_MS);
|
TARGET_SPEAKER, BASE_DELAY_MS);
|
||||||
|
|
||||||
prefs.begin("bikeaudio", false);
|
prefs.begin("bikeaudio", false);
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* BikeAudio — control-bus protocol shared by the S3 hub (I2C master) and the
|
* Resound — control-bus protocol shared by the S3 hub (I2C master) and the
|
||||||
* source Boards B/C (I2C slaves). Included by both hub_s3.cpp and board_source.cpp.
|
* source Boards B/C (I2C slaves). Included by both hub_s3.cpp and board_source.cpp.
|
||||||
*
|
*
|
||||||
* Bus: I2C. Hub = master. Each source board = a slave at a fixed address.
|
* Bus: I2C. Hub = master. Each source board = a slave at a fixed address.
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* BikeAudio — Hub (ESP32-S3-Touch-LCD-1.28) : PHASE 3 discovery + control
|
* Resound — Hub (ESP32-S3-Touch-LCD-1.28) : PHASE 3 discovery + control
|
||||||
*
|
*
|
||||||
* Round GC9A01 LCD + CST816S touch, wired as an I2C MASTER to the source
|
* Round GC9A01 LCD + CST816S touch, wired as an I2C MASTER to the source
|
||||||
* boards. Three brand-agnostic channels are controlled (discovery picks the
|
* boards. Three brand-agnostic channels are controlled (discovery picks the
|
||||||
@ -41,7 +41,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <LovyanGFX.hpp>
|
#include <LovyanGFX.hpp>
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
#include "hub_proto.h"
|
#include "bus_proto.h"
|
||||||
|
|
||||||
class LGFX : public lgfx::LGFX_Device {
|
class LGFX : public lgfx::LGFX_Device {
|
||||||
lgfx::Panel_GC9A01 _panel;
|
lgfx::Panel_GC9A01 _panel;
|
||||||
@ -861,7 +861,7 @@ static void poll_timer_cb(lv_timer_t *t) {
|
|||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
delay(300);
|
delay(300);
|
||||||
Serial.println("=== BikeAudio Hub — S3 LVGL UI ===");
|
Serial.println("=== Resound Hub — S3 LVGL UI ===");
|
||||||
|
|
||||||
// Control bus: I2C master to the source boards on Wire1 (peripheral 1).
|
// Control bus: I2C master to the source boards on Wire1 (peripheral 1).
|
||||||
// Peripheral 0 is used by the LovyanGFX CST816S touch (GPIO6/7).
|
// Peripheral 0 is used by the LovyanGFX CST816S touch (GPIO6/7).
|
||||||
@ -1,8 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* BikeAudio — Board A : A2DP SINK -> I2S MASTER
|
* Resound — Board A : A2DP SINK -> I2S MASTER
|
||||||
*
|
*
|
||||||
* Part of the 3-board relay. The iPhone connects to this board over Bluetooth
|
* Part of the 3-board relay. The iPhone connects to this board over Bluetooth
|
||||||
* (A2DP name "BikeAudio"). This board decodes the audio to PCM and clocks it
|
* (A2DP name "Resound"). This board decodes the audio to PCM and clocks it
|
||||||
* out on a shared I2S bus as the MASTER. Boards B and C (A2DP sources) listen
|
* out on a shared I2S bus as the MASTER. Boards B and C (A2DP sources) listen
|
||||||
* to this same bus as slaves and stream it to the JBL / Cardo speakers.
|
* to this same bus as slaves and stream it to the JBL / Cardo speakers.
|
||||||
*
|
*
|
||||||
@ -64,7 +64,7 @@ void on_conn_state(esp_a2d_connection_state_t state, void *obj) {
|
|||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
delay(500);
|
delay(500);
|
||||||
Serial.println("=== BikeAudio Board A — A2DP SINK -> I2S master ===");
|
Serial.println("=== Resound Board A — A2DP SINK -> I2S master ===");
|
||||||
|
|
||||||
start_i2s(44100);
|
start_i2s(44100);
|
||||||
|
|
||||||
@ -72,9 +72,9 @@ void setup() {
|
|||||||
sink.set_stream_reader(write_pcm_to_i2s, false);
|
sink.set_stream_reader(write_pcm_to_i2s, false);
|
||||||
sink.set_on_connection_state_changed(on_conn_state);
|
sink.set_on_connection_state_changed(on_conn_state);
|
||||||
sink.set_auto_reconnect(true);
|
sink.set_auto_reconnect(true);
|
||||||
sink.start("BikeAudio");
|
sink.start("Resound");
|
||||||
|
|
||||||
Serial.println("[SINK] Advertising 'BikeAudio' — connect from iPhone");
|
Serial.println("[SINK] Advertising 'Resound' — connect from iPhone");
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
Loading…
x
Reference in New Issue
Block a user