Bring your six back.
A tiny ESP32 stick that brings back the six Internet-radio preset buttons on Bose SoundTouch speakers after Bose shut down their cloud (2026-05-06). It speaks just enough of the BMX cloud protocol that the speaker firmware — which can no longer be updated — happily keeps working.
No subscription, no account, no Bose servers. One USB stick on your LAN.
SixBack was formerly developed and published as BoseFix32. All functionality is preserved; the rename reflects the project's identity independent of any Bose trademark.
| Component | State |
|---|---|
Cloud replacement (/bmx/registry, /streaming/…, /updates/…) |
working — 22 / 30 ueberboese-spec endpoints served |
| Spotify trigger (S3 / C3 / C6) (v0.7.7) | working — bind any preset slot to a spotify:track/album/playlist:... URI; physical button press fires Spotify Web-API /play to the speaker as a Connect device; one-time OAuth flow in WebUI; UI shows live trigger log with 🎵-badges per slot |
| Marge keep-alive (v0.7.7) | working — 60s background ping of /setMargeAccount to every known speaker; prevents the scmudc event-stream from going silent after hours of idle |
Marge pair-bootstrap (/setMargeAccount round-trip) |
working — /streaming/account/{a}/device/ echoes deviceid with Bearer-credentials |
| scmudc telemetry — per-device NowPlaying + event trace | working — body-captured /v1/scmudc/{deviceId} JSON parsed into per-speaker store |
TuneIn preset resolver (Tune.ashx + Describe.ashx) |
working — stations show with correct name & artwork |
| Preset push to speaker — serialized FreeRTOS queue (v0.6.0) | working — single persistent worker drains pushes one-by-one; depth 16, 503 when full |
| Preset-loss defense (Defense-in-Depth) | working — handleMigrate pre-imports; /presets and account/full return 404 when store empty; TUNEIN source-block carries username=TuneIn so sourceAccount survives every sync |
| Opaque-source passthrough — DLNA / UPnP / Bluetooth presets | working — original <ContentItem> captured at import and replayed 1:1; STORED_MUSIC and STORED_MUSIC_MEDIA_RENDERER declared in accountSources; serialized as Bosman-schema <preset> blocks with <location> + <source> reference (v0.6.537) |
| DLNA preset workflow end-to-end | working — verified on SoundTouch 30 with 6/6 OPAQUE slots reboot-persistent (2026-05-21) |
Speaker telnet bootstrap (sys configuration … via TCP 17000) |
working |
| Migrate verify post-boot (v0.7.632) | working — second getpdo after waitForSpeakerBack_; mismatch → MIGRATE_FAILED instead of silent MIGRATED |
Auto-import existing presets via BMX /presets |
working |
| Stereo-Pair / Multi-Room group API | working — POST/PUT/DELETE on /streaming/account/{a}/group/, NVS-persistent |
| Auto-Mode — discover + migrate + preserve presets on first boot | working — gated by NVS flag, default on |
| Auto-Mode cron — periodic re-check every 30 min when enabled | working — light discovery + auto-claim/release + migrate newcomers |
| Peer-aware Auto-Mode (v0.7.5) | working — HTTP-probes other SixBack sticks in the LAN; skips speakers already claimed by a peer; UI shows claimed by peer @ <ip> |
| Source-Normalizer — TuneIn / Local / RadioBrowser → playable | working — RadioBrowser UUID resolved via radio-browser.info |
| IP-Failsafe — auto-remigrate on ESP-IP change, with pre-probe | working — skips speakers already on the new base |
| SETTLING status (v0.6.541) | working — backend reports settling instead of offline when only Telnet:17000 is down but BMX:8090 still answers |
| Preset UI — drag&drop, dual-row (HW vs Store), per-slot revert | working — modal progress, per-speaker export/import, refresh discards unsaved (v0.7.3) |
| Diagnostic snapshot (v0.6.0) | working — GET /api/speaker/{id}/diagnostic-snapshot + one-shot pre-migrate snapshot persisted to /snapshots/{deviceId}.json; WebUI download or "Send to maintainer" upload to sixback.io/snapshots/bosefix/snapshot |
| OTA — app & LittleFS | working — UPDATE_SIZE_UNKNOWN + stream-to-EOF + 90% sanity-abort (v0.7.0 fix for HTTPS Content-Length truncation) |
| Tag-based release versioning (v0.7.6) | working — RELEASE_TAG env bakes the same version string into all four target firmwares; eliminates multi-target build-drift |
| Build size-gate (v0.7.5) | working — build_release.sh aborts if any firmware or LittleFS image exceeds its partition slot |
| Asymmetric OTA partition (C3 / C6, v0.7.7) | working — app0 = 2.5 MB (full SixBack incl. Spotify), app1 = 1 MB recovery slot (currently empty); spiffs 384 KB; new install needs USB-flash (image too large for old 1.81 MB slots) |
| WiFi provisioning — Improv-Serial (idle-window) + Captive AP | working — both armed in parallel on cold boot |
| ESP32-C6 WPA2 reliability | working — WiFi.setSleep(WIFI_PS_NONE) + setAutoReconnect(true) applied before WiFi.begin(); closes 4-Way-Handshake-Timeout on WPA2-Mixed APs |
| System health — Task-WDT, WiFi / heap watchdog, crash counter, self-ping | working |
| Builds for ESP32 / ESP32-S3 ★ / ESP32-C3 / ESP32-C6 | working — S3 is the recommended target |
| ESP-Web-Tools landing page (auto-detects chip) | working — https://sixback.io/ |
Open the web flasher in Chrome or Edge desktop and click Connect:
The page reads webflasher/manifest.json,
detects the chip family of the connected board, and writes the matching
factory image — bootloader + partition table + firmware + Web UI — in a
single shot. Right after the flash, esp-web-tools also offers to hand
over WiFi credentials via Improv-Serial.
If Web Serial is unavailable, every target also ships an
*-firmware.bin (for OTA over WiFi) and *-littlefs.bin (for FS-OTA).
A freshly-flashed device boots with auto_migrate_on_boot = true in NVS.
Once it is on your WiFi, it will:
- Discover all SoundTouch speakers on the LAN (SSDP + ARP-probe).
- For every eligible speaker (model whitelist
SoundTouch 10/20/30, firmware whitelist27.0.6.xand27.0.3.x):- Read its current presets via the BMX API.
- Normalize each preset (TuneIn passthrough; RADIO_BROWSER converted
to a direct stream URL; DLNA / Bluetooth captured as opaque
<ContentItem>and replayed 1:1). - Rewrite the speaker's cloud URLs via Telnet
:17000. - Reboot the speaker; presets survive without long-press because the
normalized list is embedded in the speaker's
account/fullsync.
If you'd rather drive each migration by hand, turn the switch off at
the very top of http://sixback.local/ before the device finds your
speakers — or pre-disable it via PUT /api/auto-mode (Body:
{"enabled":false}). The default is "on" because the typical install
path is flash → provision → presets work, and the foot-gun guards
(eligibility whitelists, max_per_boot=4) are tight enough that nothing
unrelated on your LAN gets touched.
After the initial boot pass, SixBack keeps the auto-mode pipeline alive
as a periodic cron (default every 30 minutes, configurable via
cron_interval_s). Each tick does a light discovery (SSDP + known-IP
probe, no full /24 sweep), runs Auto-Claim/Release on the inventory
(so a speaker that someone else migrated away gets dropped from the
"owned" list automatically), and migrates any newcomer that matches the
eligibility whitelist. The countdown to the next tick is visible at the
top of the Web UI.
If multiple SixBack sticks coexist on the same LAN, the peer-aware auto-mode (v0.7.5+) keeps them from fighting over the same speakers: each stick HTTP-probes any foreign cloud URL it sees, and if the response looks like another SixBack instance the speaker is left to its current owner. The UI labels such speakers as claimed by peer @ <ip>.
The top of http://sixback.local/ is where the Auto-Migrate at Boot
switch lives. Below it every discovered speaker gets a card with its
current state (migrated / settling / original / foreign-cloud / offline),
its 6 preset slots, and per-speaker actions (migrate, revert, reboot,
edit presets, group sync).
On every cold boot the device opens two parallel provisioning windows. Whichever finishes first wins; the other is torn down. Same pattern as the sister project ip4knx / TUL KNX-Gateway.
| Path | When | Window |
|---|---|---|
| Improv-Serial | always | 30 s idle (with creds) / 120 s idle (without) |
| Captive AP | no NVS creds or STA-connect timeout | 5 min idle |
The Improv path is what esp-web-tools uses right after flashing.
The Captive Portal opens an open AP called SixBack-XXYYZZ
(no password) with a DNS hijack so any phone connecting to it gets the
WiFi-setup form automatically; after the user submits, the success
page auto-redirects to the device's freshly assigned LAN IP via
<meta http-equiv="refresh">.
| Chip | Board reference | Flash | Notes |
|---|---|---|---|
| ESP32-S3 ★ | esp32-s3-devkitc-1 (N16R8V) |
16 MB | recommended — 8 MB PSRAM, mature WiFi 5 stack, comfortable flash headroom |
| ESP32 | esp32dev (DevKitC-1) |
4 MB | classic — biggest hobby installed base; external USB-UART |
| ESP32-C3 | esp32-c3-devkitm-1 |
4 MB | flashes over the chip's built-in USB-Serial-JTAG |
| ESP32-C6 | esp32-c6-devkitc-1 |
4 MB | WiFi 6 — works, but cold-start discovery occasionally drops SSDP-multicast packets and rare HTTP-server hangs need a reset |
S3 is the recommended target for distribution. During the 4-phase end-to-end test (S3 ↔ C6 ping-pong with full erase/flash/provision each round) the S3 hit 3/3 speakers discovered + migrated in every single auto-mode run, while the C6 needed a second boot in one cold-start case and produced one HTTP-server hang that recovered only after a hardware reset. The extra ~5 € for an S3-DevKitC-1-N16R8 buys noticeable robustness and ~40 % free flash for future features.
The other three targets are fully functional and stay built/published on every release — the C3 and C6 currently sit at ~95 % flash use, so adding heavy features needs care.
All four targets share the same source tree and the same Web UI; the
PlatformIO extends = common mechanism keeps the per-target diff small
(platformio.ini).
After clicking Migrate in the Web UI, SixBack talks to the Bose Diagnostic Shell on TCP :17000 of the speaker and rewrites the cloud URLs the firmware caches in NVS:
sys configuration bmxRegistryUrl http://<sixback-ip>:8000/bmx/registry/v1/services
sys configuration statsServerUrl http://<sixback-ip>:8000
sys configuration margeServerUrl http://<sixback-ip>:8000
sys configuration swUpdateUrl http://<sixback-ip>:8000/updates/soundtouch
envswitch boseurls set http://<sixback-ip>:8000 http://<sixback-ip>:8000/updates/soundtouch
sys reboot
No SSH, no firmware mod, no Bose login. The change is fully reversible via Revert to original Bose — the speaker returns to its factory URL set even though the original cloud is offline.
Requires PlatformIO and a Linux/macOS host.
# build everything (all four targets) + LittleFS images
pio run -e esp32 -e s3 -e c3 -e c6
pio run -e esp32 -e s3 -e c3 -e c6 -t buildfs
# produce tagged factory images + manifest for the web flasher
./scripts/build_release.sh v0.7.6 # tag arg bakes the version into all 4 firmwares
# flash a single target via USB
pio run -e s3 -t upload
pio run -e s3 -t uploadfsVersioning + build snapshots are automatic
(see scripts/version_bump.py): every local
build snapshots the working tree before bumping build_number.txt, so
you can always roll back to the exact state a given binary was built
from. Those snapshot commits stay local — only tagged releases are
pushed to the public repo.
src/ Firmware (Arduino + ESP-IDF mix)
web-src/ Web UI source (index.html, gzipped at build time
into data/ for LittleFS)
webflasher/ esp-web-tools landing page + manifest (binaries
are .gitignored — rebuild via build_release.sh)
images/ README assets — title PNG + Web-UI screenshot
scripts/ version_bump pre-build hook + build_release.sh
partitions.csv 16 MB partition table (ESP32-S3 16-MB modules)
partitions-4mb.csv 4 MB partition table (ESP32 / C3 / C6)
platformio.ini Multi-env config, see `[common]` + `[env:*]`
-
julius-d/ueberboese-api — OpenAPI specification of the legacy Bose SoundTouch streaming cloud, reconstructed from observed traffic. It is SixBack's verifiable ground-truth for endpoint shapes, header semantics, and event-body formats (scmudc envelope, NowPlaying structure, kebab-case event types, group/preset XML). Thanks to julius-d for publishing it.
-
tostmann/ip4knx — sister project. The dual-path WiFi provisioning (Improv + Captive in parallel) and the system-health / self-ping watchdog pattern are carried over from there.
SixBack is an independent open-source project. It is not affiliated with, endorsed by, or sponsored by Bose Corporation. All references to Bose products and protocols are nominative, for interoperability with hardware their owners have already paid for. Use at your own risk.
PolyForm Noncommercial 1.0.0. See LICENSE for the full text and THIRD-PARTY-LICENSES.md for upstream component licences.

