Fix Si4684 response-parsing offset bug: RDS, DAB acquired/event status

readFmRds(), readDabDigRadStatus()'s acquired field, and
readDabEventStatus() all read raw[4] expecting AN649's RESP4 field, but
this driver's own established convention (getPartInfo(), and the
already-correct ficQuality/cnrDb fields in readDabDigRadStatus() itself)
is raw[5]=RESP4 (raw[0]=SPI lead-in, raw[1..4]=STATUS0-3). This is why
DAB_GET_EVENT_STATUS's serviceListReady never set — it was reading
STATUS3's ERRNR bit instead of RESP4's SVRLISTINT bit, so
/api/tuner/services returned service_list_empty forever regardless of lock
quality. Fixed all four sites; readFmRds()'s fifoUsed/blockA-D were
consequently also off by one and fixed together with it.

Confirmed live: first DAB ensemble locks in this project's history (3 found
sweeping freq_index 0-35, fic_quality=100, best CNR 20 dB on index 23), and
/api/tuner/services now returns real entries instead of service_list_empty.
The service-list entry contents themselves are still garbled (a third,
separate bug in fetchDabServiceList()'s body parsing, documented but not
fixed this session — see docs/si4684-rf-investigation-report.md).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178rASQ6ZETPMUamvpoR2KR
This commit is contained in:
2026-08-16 01:24:54 +02:00
co-authored by Claude Sonnet 5
parent 6974095f35
commit 9b337e7bca
2 changed files with 50 additions and 9 deletions
@@ -931,13 +931,16 @@ std::expected<Si4684FmRdsStatus, Si4684Error> Si4684Driver::readFmRds()
return std::unexpected(rd.error());
}
// raw[0]=lead-in, raw[1..4]=STATUS0-3, raw[5]=RESP4 (established
// convention, see getPartInfo()/readDabDigRadStatus() comments) — every
// offset below is RESP-number relative to that, not raw[4].
Si4684FmRdsStatus rds = {};
rds.received = (raw[4] & 0x01U) != 0U;
rds.fifoUsed = raw[10];
rds.blockA = readLe16(raw.data() + 12U);
rds.blockB = readLe16(raw.data() + 14U);
rds.blockC = readLe16(raw.data() + 16U);
rds.blockD = readLe16(raw.data() + 18U);
rds.received = (raw[5] & 0x01U) != 0U;
rds.fifoUsed = raw[11];
rds.blockA = readLe16(raw.data() + 13U);
rds.blockB = readLe16(raw.data() + 15U);
rds.blockC = readLe16(raw.data() + 17U);
rds.blockD = readLe16(raw.data() + 19U);
return rds;
}
@@ -1063,7 +1066,8 @@ Si4684Driver::readDabDigRadStatus()
Si4684DabDigRadStatus status = {};
status.ficQuality = raw[9];
status.cnrDb = raw[10];
status.acquired = (raw[4] & 0x08U) != 0U;
// raw[5]=RESP4 (see readFmRds()); ACQINT is RESP4 bit3.
status.acquired = (raw[5] & 0x08U) != 0U;
status.valid = status.ficQuality > 0U;
return status;
}
@@ -1085,9 +1089,10 @@ Si4684Driver::readDabEventStatus()
return std::unexpected(rd.error());
}
// raw[5]=RESP4 (see readFmRds()); SVRLISTINT is RESP4 bit0.
Si4684DabEventStatus events = {};
events.serviceListReady = (raw[4] & 0x01U) != 0U;
events.reconfig = (raw[4] & 0x02U) != 0U;
events.serviceListReady = (raw[5] & 0x01U) != 0U;
events.reconfig = (raw[5] & 0x02U) != 0U;
return events;
}
@@ -565,6 +565,42 @@ BT1035 → Bluetooth speaker) in this project's history. Remaining noise on
top of the music is attributed to antenna quality, not yet independently
confirmed with a proper antenna — flagged as follow-up, not closed.
## 2026-08-16 update: DAB lock confirmed on 3 ensembles + response-offset bug
With the tune fix in place, a full sweep of freq_index 0-35 found three real
ensemble locks (fic_quality=100 on all three): index 5 (CNR 7 dB), index 22
(CNR 15 dB), index 23 (CNR 20 dB, strongest). First confirmed DAB lock in
this project's history.
Chasing why `/api/tuner/services` returned `service_list_empty` even after
30+ seconds on a solid lock found a second bug class, this time in
**response parsing, not command construction**: `readFmRds()`,
`readDabDigRadStatus()`'s `acquired` field, and `readDabEventStatus()` all
read `raw[4]` expecting AN649's "RESP4" field, but this driver's own
established convention elsewhere (`getPartInfo()`, and the already-correct
`ficQuality`/`cnrDb` fields in `readDabDigRadStatus()`) is `raw[5]=RESP4`
(`raw[0]`=SPI lead-in, `raw[1..4]`=STATUS0-3). Fixed all four call sites to
the correct offset; `readFmRds()`'s `fifoUsed`/`blockA-D` fields were
consequently also all off by one and fixed together with it.
After the fix, `serviceListReady` now correctly gates open and
`/api/tuner/services` returns real data instead of `service_list_empty`
but the entries themselves are still garbled (implausible `service_id`
values, `component_id` fields that decode as ASCII spaces, e.g.
`538976288 = 0x20202020`, mostly-empty labels). This points to a **third,
separate bug** in `fetchDabServiceList()`'s service-list *body* parsing
(the entry structure walked in the loop over `serviceCount`), not yet
investigated — the DAB service list binary format is documented in AN649
§7 "Digital Services User's Guide" (starts around page 418), not the
command tables checked so far. Confirmed live: `POST /api/tuner/play` with
one of these garbled IDs accepted (`{"status":"playing"}`) but produced no
audio, consistent with a wrong service/component ID rather than a new
audio-path regression.
**Follow-up, not done this session**: fix `fetchDabServiceList()` entry
parsing against AN649 §7; then confirm actual DAB audio playback end to
end the same way FM was confirmed.
**Unrelated finding from the same session, logged for completeness**: BT1035
began failing boot deterministically (`no spontaneous UART bytes after
hardware reset`, then `AT init failed`) starting from this session, on both