writeCommand() had no way to send a non-zero ARG1, since it always
hardcoded ARG1=0x00 before the caller's payload. Every command whose real
ARG1 needed to carry a flag (STCACK, INTACK, SERTYPE, DIGRAD/EVENT ack) or
whose payload needed to start at ARG1 instead of ARG2 was silently broken:
- seekFm(): SEEKUP/WRAP never reached the chip (always ARG2=0x00), so
hardware seek always searched down/no-wrap; masked by the existing
100 kHz software-step fallback in Si4684Tuner.
- startDabService()/stopDabService(): SERVICE_ID/COMPONENT_ID shifted one
byte right of their real ARG4-11 positions, with SERTYPE landing where
the spec requires a fixed 0x00.
- readDabServiceData(): same shift, plus STATUS_ONLY was bit3 (0x08)
instead of the correct bit4 (0x10).
- clearFmStc(), readFmRsq(), readFmRds(), fetchDabServiceList(),
readDabDigRadStatus(), readDabEventStatus(): these AN649 commands have
only ARG1 and no ARG2 at all, so the old two-argument writeCommand()
could never carry their ack/status flags — clearFmStc()'s STCACK never
fired in this driver's history (masked by FM_TUNE_FREQ/FM_SEEK_START
auto-clearing STC per their own spec).
writeCommand() gains a fourth parameter, arg1 (default 0x00, preserving
every already-correct call site); each caller above now passes its flag
through arg1 instead of the payload array.
Confirmed live: first locked:true and first genuine hardware seek (not
software-fallback) in this driver's history — 87.5 -> 98.3 MHz, RSSI +12
dBuV, SNR +14 dB.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178rASQ6ZETPMUamvpoR2KR
writeCommand() always prepends a fixed ARG1=0x00 byte before the payload;
tuneFm()/tuneDab() built their argument arrays starting at what the author
believed was ARG1, so every byte landed one slot right of its real AN649
position and an extra unused byte was appended. The chip never received the
requested frequency. This is the root cause of the months-long total RF
blackout (RSQ frozen at all-zero on every frequency/ANTCAP value) previously
attributed to a QFN exposed-pad hardware defect — that hypothesis is now
overturned, confirmed live: RSSI/SNR now read real, frequency-dependent
values with zero STC timeouts after the fix.
Also make BT1035 boot failure non-fatal in HardwareBootstrap::boot() so a
companion-chip fault no longer halts the whole device (Si4684 tuning, web
UI, Wi-Fi already isolate BT1035 readiness via CompanionChipStatus).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178rASQ6ZETPMUamvpoR2KR
FM blob confirmed byte-perfect and reproduces the identical no-lock symptom
seen on DAB, strengthening the hardware-side hypothesis over firmware.
PCBWay closed the dispute without X-ray verification. Records the BT1035
AT-init root cause (software, not hardware) as a calibration note against
over-attributing intermittent symptoms to physical damage.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Redundant AT+RESET sent right after the hardware RESET# pulse could interrupt
the module mid bring-up; boot-banner probe window (1500ms) was too short for
the real +VER banner (~5s), causing spurious "AT init failed". 5/5 clean
boots after fix vs ~1/13 before.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Si4684Driver::getPartInfo()/getSysState() existed but were never called, so
their byte-offset bugs never surfaced. Wired them into a boot-time diagnostic
log (part number, firmware revision, active image, streamed blob byte counts)
to check whether the loaded DAB/FM firmware images are genuine and intact, as
an alternative explanation to the QFN exposed-pad hardware hypothesis.
Fixed two rounds of off-by-one bugs found while doing this: the fields were
initially read one byte too far right (e.g. firmwareBuild was reading a flag
byte, not a version number); the first fix undershot in the other direction
by not accounting for readRaw()'s one-byte SPI lead-in before STATUS0 (already
documented and confirmed elsewhere in this file, in pollStc()) -- caught
because the "fixed" GET_SYS_STATE reported image=192 (0xC0), the exact
signature of STATUS3 with PUP_STATE=3 seen throughout this investigation.
All three response buffers were already sized for the lead-in byte, which
confirmed the correct fix.
Verdict, captured live: blob streamed bytes match local file sizes exactly (no
truncation), GET_SYS_STATE reports image=2 (DAB active, correct), GET_PART_INFO
reports part=4684 (matches expected Si4684 part number) with a plausible
firmware revision -- the loaded DAB firmware is genuine and intact. This closes
the last plausible firmware-side explanation for the FM/DAB no-lock symptom;
docs/si4684-rf-investigation-report.md and docs/TODO.md (P4/H5) updated with
the full record and verdict.
Also noted, not yet fixed: BT1035 AT-init now fails deterministically on every
boot (was a one-off earlier this session) -- see report's Open Items.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add docs/si4684-rf-investigation-report.md: full record of the FM/DAB no-lock
investigation (crystal, front-end matching, ANTCAP sweep, continuity, leading
EP solder-defect hypothesis, PCBWay report sent) plus the separate audio
profile NVS bug found and partially fixed this session.
- Fix sigma_i2c_write() (SigmaStudioFW.c): no retry on I2C failure meant a single
transient NACK anywhere in a long safeload burst (EQ apply = ~55 sequential
transactions) aborted the whole sequence. Added a 3-attempt retry.
- Add granular failure logging (AudioService::applyProfileToDsp/persistProfile,
Adau1701Driver::applyMixer/applyEq, NvsAudioProfileStore::saveProfile error
codes) to isolate the remaining NVS-side audio profile save failure.
- Si4684Driver::tuneFm gains an optional ANTCAP argument (default 0 = unchanged
auto-tune behavior) used during this session's front-end matching sweep.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Streaming (main feature this session):
- New WebRadioConfig/WebRadioJson core types, ISecureStore-backed persistence
- New webradio::WebRadioService (thread-safe live config) + GET/POST /api/streaming
- web_radio_stream task now runtime-toggleable (no reboot), no hardcoded URL
- Content-Type diagnostic: warns clearly when a URL is a webpage, not an audio stream
Boot cleanup:
- Removed boot-time auto FM/DAB tune, auto-beep, and the (now-concluded) Si4684
crystal IBIAS/CTUN empirical sweep from main.cpp — tuning/beep are on-demand
via the existing REST API only
Web UI:
- Modernized styling (cards, gradients, toggle switches, light/dark theme)
- New Stream tab wired to /api/streaming
Fixes found via real idf.py build (not just clangd):
- Restored wrongly-removed si4684/Si4684Tuner.hpp include in main.cpp
- Fixed MP3Decode() argument types in web_radio_stream.cpp (unsigned char**/int*)
Quality-gate fixes:
- Host-test stub headers (esp_log.h, freertos/*) so TunerService.cpp's
scanForStation logging/pacing compiles for station_service_test /
integration_service_test instead of running stale binaries
- Added WifiScanner and WebRadioService manual sections; filled in missing
Doxygen docs on BluetoothService, i2s_sdata_probe, test_firmware, Bt1035At
- Ignore clangd's .cache/ index directory
Also includes prior uncommitted work carried in the tree: Wi-Fi/Bluetooth
device scan REST API and UI (WifiScanner, BT scan), SigmaStudio TCP bridge,
and the current ADAU1701 SigmaStudio DSP program export.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Initialise NVS before integration startup, recover from encrypted-partition mismatches without re-enabling encryption, and improve provisioning logs and POST body handling.
Co-authored-by: Cursor <cursoragent@cursor.com>
Bundle crystal, LDO, ESD, and module PDFs under Hardware/DATASHEET plus ODB++ gerbers and pick-and-place for the 2026-08-05 board release.
Co-authored-by: Cursor <cursoragent@cursor.com>
Refresh BOM (2026-07-09 CSV), Gerbers, EasyEDA project, pick-and-place, and schematic export; document corrected ADAU crystal/PLL strapping and Si4684 reference clock in the manual; add PCBWay acknowledgement on the back cover only.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add wrapping table columns, breakable paths, and ragged callout boxes in digiradio-manual.sty, then reflow long endpoint headings, JSON examples, and hardware/driver tables to stay within page margins.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add gitignore rules for aux, log, toc, fls, fdb_latexmk, listing, out, and pdf so only source .tex/.sty stay in version control.
Co-authored-by: Cursor <cursoragent@cursor.com>
Switch BT1035 bring-up from Line-In to I2S slave (AT+AUXCFG=3, AT+I2SCFG=67) to match the ADAU1701 PCM routing, confirm 2 kΩ I2C pull-ups on R1/R16, and sync firmware docs, AGENTS rules, and the DATASHEET bundle.
Co-authored-by: Cursor <cursoragent@cursor.com>
Rename conflicting TikZ step style to drstep, add missing ch:firmware label, and use TeX Gyre Heros font fallback instead of Linux Biolinum O.
Co-authored-by: Cursor <cursoragent@cursor.com>
Align README, manual, and backlog to 0.8.4; add Web UI System tab for OTA/DSP uploads, serial in health header, FM seek down, and BT1035 paired list plus auto-reconnect API.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace angle-bracket suffix placeholders that Doxygen parsed as invalid
HTML tags, restoring a clean WARN_AS_ERROR build on Ubuntu CI.
Co-authored-by: Cursor <cursoragent@cursor.com>
Stream application binaries to the inactive OTA slot via POST /api/system/ota,
validate the esp_app_desc project name in core, and cancel rollback after a
healthy network boot through OtaService::confirmBoot().
Co-authored-by: Cursor <cursoragent@cursor.com>
Decouple the SigmaStudio RAM download from compiled-in adau1701_program.c:
DRAD v1 blobs in the dsp partition with embedded fallback, POST /api/dsp/program,
and host-tested blob parse/serialize helpers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Read the factory 24AA025E48 serial after ADAU I2C boot, derive SoftAP SSID, BT name, STA hostname, and expose serialNumber on GET /api/health with graceful fallbacks.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace factory with ota_0/ota_1, otadata, and a dsp data partition on 4 MB flash; enable bootloader app rollback for future OTA work.
Co-authored-by: Cursor <cursoragent@cursor.com>
Enable GPIO_PULLDOWN_ENABLE during gpio_config so RSTB cannot glitch high before gpio_set_level(0), and add manual validation notes explaining why the internal pull-down complements the external one.
Co-authored-by: Cursor <cursoragent@cursor.com>
Align root, Software, and Firmware READMEs with security, CI, web UI, blob policy, and six-band EQ details.
Co-authored-by: Cursor <cursoragent@cursor.com>
Update READMEs, manual chapters, agent guides, CONTRIBUTING, and TODO to reflect T1–T8 done, encrypted NVS, CI gates, and pending HIL checklist.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add initEncryptedStorage, development-mode Kconfig defaults, production overlay, and security HIL docs; wire NetBootstrap through encrypted NVS bring-up.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expand local-only firmware README, add check_si4684_blobs.py to verify gitignore and clean history, and gate main with a new si4684-blobs workflow job.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add now-playing view with metadata polling, six-band EQ controls, full REST coverage, companion-chip badges, and gzip-www helper for the embedded setup page.
Co-authored-by: Cursor <cursoragent@cursor.com>
Document firmware capabilities, version milestones, CI, API summary, and updated project structure after integration service and metadata work.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the services stub with IntegrationService for boot preset recall and tune orchestration, persist last preset in NVS, and remove invalid @return tags that broke CI on T4 metadata headers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Ship station reorder, DAB playing ids in presets, RDS PS/RT and DAB DLS in tuner status, Si4684 data-service read, host tests, and UI now-playing lines.
Co-authored-by: Cursor <cursoragent@cursor.com>
Clear doc-block warnings so doxygen exits 0, add GitHub Actions
for host tests, Doxygen, and manual sync, and document CI in the manual.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adopt the prioritised task list for the coding agent and document
fw 0.7.0 status plus the full HTTP API in Software/README.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose discoverable mode and A2DP control over REST, add persisted
DAB/FM preset list with web UI, and document gaps in docs/TODO.md.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose Si4684, ADAU1701, and BT1035 boot flags via CompanionChipStatus after
HardwareBootstrap; update API manual schema for Slice 8.
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove legacy DigiRadio_Manual.tex/PDF and stub main.tex; point docs/ at the
canonical manual. Update README and CONTRIBUTING paths.
Co-authored-by: Cursor <cursoragent@cursor.com>
Implement Bt1035Driver AT init (AT+AUXCFG=1), core AT parser with host tests,
wire boot into HardwareBootstrap, and document DAB/FM tuning workflows in ch-si4684.
Co-authored-by: Cursor <cursoragent@cursor.com>