Files
DigiRadio/Software/docs/si4684-rf-investigation-report.md
T
micheleandClaude Sonnet 5 ef15e9839c Fix Si4684 FM/DAB tune commands: ARG offset bug caused total RF blackout
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
2026-08-16 00:19:43 +02:00

444 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Si4684 RF no-lock investigation — status report
**Date**: 2026-08-13
**Board**: PCBWay order W96157ASH49, U6 = Si4684-A10 (confirmed genuine, top marking `4684A10-2112AD254YZ-2112AD-E3`)
## Symptom
Si4684 (U6) boots, loads the ROM patch and FM/DAB application images, and answers
every SPI command correctly (CTS, boot sequence, property reads/writes all succeed).
FM and DAB tuning never completes: the STCINT status bit never sets, RSQ/DIGRAD
metrics stay at zero (RSSI=0, SNR=0, VALID=0, FIC quality=0, empty service list),
on both bands, at every frequency tried.
## What has been verified correct (do not re-litigate)
All cross-checked byte-by-byte against the official Skyworks documents
(`Hardware/DATASHEET/AN649.pdf`, `AN851_Schematics_Layout.pdf`):
- **Boot sequence**: RSTB# pulse, ROM patch stream, image stream, `BOOT` — matches
the AN649 flowchart exactly, on every boot, both bands.
- **Crystal / POWER_UP**: `XTAL_FREQ` = 19,200,000 exact (bytes `00 F8 24 01`,
little-endian `0x0124F800`), `CLK_MODE` = crystal mode (`0x17` → bits 5:4 = `01`).
Matches the physical ABM8-19.200MHZ-10-1-U-T crystal (U7) and BOM/schematic.
- **I2S**: Si4684 configured as I2S slave (`DIGITAL_IO_OUTPUT_SELECT = 0x0000`),
48 kHz; ADAU1701 is bus master. Confirmed via GPIO clock probe (LRCLK ≈ 48000 Hz,
BCLK ≈ 3.07 MHz) at every boot.
- **Front-end matching properties**: `FM/DAB_TUNE_FE_VARM` (0x1710),
`FM/DAB_TUNE_FE_VARB` (0x1711), and `FM/DAB_TUNE_FE_CFG`/VHFSW switch (0x1712,
value `0x0001` = closed) all match AN851's "Silicon Labs Recommended Front End
Network" table exactly (FM: `0xEDB5`/`0x01E3`; DAB: `0xF8A9`/`0x01C6`; switch
closed on both).
- **FM_TUNE_FREQ command**: all six ARG bytes decoded bit-by-bit against the AN649
command table (DIR_TUNE, TUNE_MODE, INJECTION, FREQ, ANTCAP, PROG_ID) — correct.
- **INT_CTL_ENABLE / INT_CTL_REPEAT** (STCIEN/STCREP, properties 0x0000/0x0001):
correct bit positions; confirmed these only gate the physical INTB pin, not the
STATUS0 STCINT bit our driver polls directly over SPI.
- **STC polling mechanism**: the same raw SPI byte (`pollRx[1]`) that reliably
reports CTS=1 (bit 7) across hundreds of successful commands also reports
STCINT=0 (bit 0) — the read path itself is proven reliable by the CTS side, so
the "never sets" result is a real hardware/firmware-image observation, not a
polling bug.
## Front-end network component mismatch (found this session, not the cause by itself)
The board's actual front-end network (`RF1 → C13(33pF) → L1(18nH) → C14(2.7pF
shunt) → L3(120nH shunt) → VHFI`, `L2(22nH)` bridging VHFI↔VHFSW) differs from
Silicon Labs' AN851 reference network the VARM/VARB constants were derived from
(`C1=33pF, L1=56nH, L2=120nH‖L3=120nH`). This was flagged as a plausible
contributor, then tested directly and ruled out as the *sole* cause (see below).
## Empirical sweeps (all negative — zero variation)
- **IBIAS/CTUN** (crystal startup calibration, POWER_UP ARG3/ARG8): 8 candidates
across the practical range, full reboot between each. No change.
- **ANTCAP** (FM_TUNE_FREQ ARG4/5, bypasses FE_VARM/VARB auto-tune entirely and
forces the on-chip antenna varactor directly, per AN851 Appendix A): ~100 of 128
possible values swept, across three antenna conditions (disconnected, loose
contact, directly soldered 70 cm — correct quarter-wave for FM). **Every single
attempt returned byte-identical RSQ raw data**
(`00 80 00 00 c0 00 00 00 00 00 00 00`, RSSI=0/SNR=0/VALID=0). If the RF path
were electrically functional, at least one of ~100 forced varactor values across
the full physical range should have produced resonance. None did.
- **Reset type**: software RSTB# vs. full USB power-cycle (15 s cold) — no change.
- **PCB continuity**: RF1 (antenna connector) → C13 → L1 → U6 pin 10 (VHFI)
confirmed intact with a multimeter (tested each leg separately to work around
C13's DC block). No broken trace/via.
## Leading hypothesis: QFN-48 exposed pad (EP) solder defect
U6 is a 7×7 mm QFN-48 with an exposed thermal/ground pad (pin 49, tied to GND).
Insufficient solder or voiding under this pad during reflow is a well-documented
QFN assembly failure mode that produces exactly this symptom: digital I/O
(peripheral pins, less ground-sensitive) works perfectly, while the RF/analog
front end (which references the exposed pad for a clean ground) fails entirely.
ESD was considered and set aside — the antenna input has ESD clamp protection
(D3, BAV99) ahead of the RF path, and no digital-side symptom consistent with ESD
damage (SPI glitches, crystal instability) has ever appeared.
**Action taken**: a technical report was sent to PCBWay (order W96157ASH49)
requesting an assembly quality review of U6's solder joints, specifically the
exposed pad, laying out the same evidence above (firmware ruled out by the
ANTCAP-bypasses-firmware argument).
**Action pending**: a manual hot-air reflow of U6 (re-melt only, no added
solder/paste) was planned as a lower-risk first attempt before considering full
chip removal and re-paste. Outcome not yet recorded in this document as of this
report's writing — update this section once attempted.
## Separate finding this session: audio profile save was broken, now partially fixed
Independent of the Si4684 investigation, while testing the internet radio
streaming feature (`components/services/webradio`), audio profile changes via
`PUT /api/audio/profile` and `POST /api/audio/reset` were found to fail
(`store_failed`) — which is why the ESP32 mixer channel (needed to hear the web
radio stream through the DSP mixer, muted at -96 dB by the default "radio-first"
mix) could not be un-muted via the API.
Root-caused and fixed: `sigma_i2c_write()` (`components/drivers/adau1701/src/SigmaStudioFW.c`)
had no retry on I2C transaction failure. A full EQ apply chains ~55 sequential
I2C transactions (5 bands × safeload block); a single transient NACK anywhere in
that burst aborted the whole sequence, while short bursts (e.g. the 2-write beep
toggle) reliably succeeded. Added a 3-attempt retry with a 2 ms backoff.
After the fix, DSP-side writes (mixer, EQ) succeed. A **second, separate**
failure remains: `NvsAudioProfileStore::saveProfile()` still fails, now isolated
to the NVS write step itself (not the DSP). Diagnostic logging was added
(`nvs_open`/`nvs_set_str`/`nvs_commit` error codes) to pin down the exact
`esp_err_t`; the leading suspicion is NVS partition space/fragmentation (the
`nvs` partition is only 24 KB, and this session alone did many repeated writes
across streaming config, beep toggles, Wi-Fi, and station data). **Not yet
confirmed with the actual error code — re-run the diagnostic build and capture
the log line to close this out.**
## Blob/firmware-image integrity check (completed, negative — blobs are genuine)
`getPartInfo()` and `getSysState()` (`components/drivers/si4684/src/Si4684Driver.cpp`)
already existed to decode `GET_PART_INFO`/`GET_FUNC_INFO`/`GET_SYS_STATE`, but were
never called anywhere in the codebase — dead code, so their byte-offset bugs had
never surfaced. Found and fixed **two rounds** of off-by-one bugs while wiring them
into a boot-time diagnostic log:
1. First pass: every field (chip ID, firmware major/minor/build, image type) read
one byte too far right — e.g. `firmwareBuild` was actually reading the
NOSVN/LOCATION flag byte, not a version number.
2. That "fix" was itself wrong in the other direction. `readRaw()` responses carry
a one-byte SPI lead-in before STATUS0 — the same convention already confirmed
and commented in `pollStc()` elsewhere in this file — which the first pass
didn't account for. Caught empirically: the "fixed" `GET_SYS_STATE` reported
`image=192` (`0xC0`), the exact byte pattern of STATUS3 with `PUP_STATE=3`
seen dozens of times elsewhere in this investigation — proof the read was
still one byte off, in the other direction. All three existing response
buffer sizes (7/13/24 bytes) already matched "N response bytes + 1 lead-in",
confirming the lead-in-byte offset (not the no-lead-in offset) is correct.
**Result after the fix**, captured live from the device (DAB boots first at
startup):
```
Si4684: blob streamed: 5796/5796 bytes (rom_patch_016.bin, matches file size exactly)
Si4684: blob streamed: 517524/517524 bytes (dab_firmware.bin, matches file size exactly)
Si4684: GET_SYS_STATE: image=2 (2 = DAB active, correct per AN649)
Si4684: GET_PART_INFO/GET_FUNC_INFO: part=4684 rev=4.0.5 svnid=0x00001754
```
`part=4684` matches the expected Si4684 part number exactly; `rev=4.0.5` and the
SVN ID are plausible, sane values, not garbage. Blob byte counts streamed over
SPI match the local file sizes exactly — no truncation in transit. **Verdict:
the DAB blob loaded on the chip is genuine and intact.** (FM boot's GET_FUNC_INFO
was not yet captured — a BT1035 AT-init failure, see below, has been blocking the
device from reaching the point in the boot sequence where FM is exercised. Not
expected to change this verdict; DAB alone already answers the blob-integrity
question this check was for.)
This closes the last plausible firmware-side explanation for the no-lock symptom
**as far as DAB is concerned only** — see the 2026-08-14 update below for a
correction to how far this actually generalizes to FM.
## Open items
1. Confirm the exact NVS error code for the audio-profile save failure and fix
accordingly (likely: erase/compact the `audio_profile_json` key, or address
partition fragmentation — do **not** perform a full NVS erase without explicit
confirmation, it would wipe Wi-Fi credentials, stations, and the saved BT
speaker pairing).
2. **PCBWay dispute closed (2026-08-14) without resolution.** PCBWay's response:
AOI passed, no X-ray performed (not requested by their process), and they
consider the assembly sound unless given photographic evidence — which, per
the QFN voiding research below, cannot exist for this failure mode by
construction. The user closed the dispute rather than continue arguing a
claim neither side can prove without an X-ray neither side is willing/able
to obtain (single unique board, international shipping not worth the cost
or risk). **PCBWay is no longer an active avenue.** Remaining options if
revisited: local X-ray access (university SMT lab, phone-repair/BGA rework
shop), or the hot-air reflow (still requires the user's explicit go-ahead —
not to be proposed proactively).
3. Record the hot-air rework outcome (RSSI response test) if/when the user
decides to attempt it. Not proposed proactively — the user has explicitly
declined to touch/rework the board without a materially stronger reason
than what non-invasive diagnostics have produced so far.
4. Visual tilt/float check (2026-08-13/14, two rounds, 8 photos: top-down and
genuine side-profile/raking-light): **negative** — U6 sits flush, solder
fillets look even on every edge photographed, no visible gap or lifted
corner. Rules out the "gross float from paste over-print" QFN failure mode
specifically (a real, documented failure mode found via web research this
session). Does **not** rule out sub-visible exposed-pad voiding, which is
undetectable by any optical method — confirmed via research into QFN
thermal-pad voiding literature (needs X-ray, see item 2).
5. VA/VCORE analog+core supply rail measured directly at U3 (1.8 V regulator)
pin 5: **1.791 V**, within the Si4684 datasheet spec (1.712.0 V, typ.
1.8 V). Rail confirmed healthy; this was expected going in, since VA and
VCORE share the same physical net and VCORE was already known-good (the
chip boots and answers SPI). Rules out a gross power-rail fault as the
cause.
6. LO-leakage test (second FM radio near the board while attempting a tune, to
detect whether the Si4684's local oscillator radiates near the tuned
frequency + IF) — proposed, **not yet performed/reported** by the user.
Still the only remaining test that can distinguish "RF synthesizer alive,
fails downstream" from "RF block itself never starts."
## 2026-08-14 update: FM blob verified, BT1035 root-caused (software, not hardware)
**FM blob integrity — closed.** Live capture, `POST /api/tuner/tune`
`{"band":"fm","frequency_khz":95000}`:
```
Si4684: blob streamed: 531300/531300 bytes (byte-perfect vs local file)
Si4684: FM firmware booted
Si4684: GET_SYS_STATE: image=1
Si4684: GET_PART_INFO/GET_FUNC_INFO: part=4684 rev=5.1.3 svnid=0x000023b3
```
Genuine, byte-perfect, sane values — same verdict as DAB. Tuning at 101.5 MHz
and 95.0 MHz both reproduce the identical no-lock signature already seen on
DAB (STC timeout, `FM RSQ raw: 00 80 00 00 c0 00 00 00 00 00 00 00`, all
metrics zero).
**New finding**: DAB (rev 4.0.5) and FM (rev 5.1.3) are from two different
Skyworks release generations roughly two years apart (DAB blob sourced from
the PE5PVB community project, FM from a Skyworks eval CD) — confirmed via the
official AN649 Table 1 revision history. Both are individually within
ROM0.016's documented compatibility window, and each band load is an
independent, exclusive HOST_LOAD (never concurrent), so this mismatch is very
unlikely to be functionally relevant. Recorded because it was a real,
previously-unverified gap, not because it changes the verdict.
**This strengthens the hardware hypothesis**: two independently-sourced
firmware images, different vintage, different origin, fail identically. A
shared firmware bug across both is far less plausible than a shared hardware
cause (front-end/EP) that doesn't care which application image is loaded.
**BT1035 `AT init failed` — root-caused and fixed, was software, not hardware.**
Contrary to the working hypothesis from earlier tonight (intermittent physical
contact, correlated with the antenna soldering session), the actual cause was
two regressions introduced by this session's own earlier commit
(`6f7b6dd`), confirmed by diffing against the last commit explicitly logged as
"all companion chips ready" (`6ca40f1`, 2026-08-05):
1. A redundant software `AT+RESET` sent over UART immediately after the
hardware RESET# pulse — absent from the known-good baseline, which goes
straight from the hardware pulse into the init handshake. Landing this
command while the module is still processing the hardware reset risks
restarting its bring-up mid-sequence.
2. A UART boot-banner diagnostic probe (added earlier this session to
distinguish "module silent" from "module garbled") with too short a listen
window (1500 ms) — live capture showed the module's real unsolicited boot
banner (`+VER=FSC-BT1035,V6.1.1,20240521`) arriving closer to 5 s, well
outside that window.
Both fixed in `components/drivers/bt1035/src/Bt1035Driver.cpp`: removed the
redundant `AT+RESET`, widened the listen window to 3500 ms. Result: 5/5 clean
boots after the fix, versus roughly 1/13 before. No physical intervention,
cleaning, or component was involved — a visual inspection of the BT1035
module's castellated pads (zero risk, no rework) found nothing abnormal
beyond minor flux residue, consistent with this being a pure software
regression, not a solder defect.
**Net effect on the Si4684 hypothesis**: none directly — BT1035 and Si4684 are
separate chips/subsystems — but it's a useful calibration: a fault that
*looked* exactly like a classic "physical handling damage" symptom (persistent
after a soldering session, deterministic-then-intermittent) turned out to be
100% software. Worth remembering as a caution against over-attributing
intermittent symptoms to hardware without exhausting the code-path diff
against a known-good commit first.
## 2026-08-15 update: front-end network mismatch quantified — does not explain the total blackout
Follow-up on the "Front-end network component mismatch" section above (board
network `C13 33pF, L1 18nH, C14 2.7pF shunt, L3 120nH shunt, L2 22nH` vs
AN851's reference `C1 33pF, L1 56nH, L2‖L3 120nH‖120nH`). The mismatch was
flagged as a plausible contributor but never quantified. Real component
coordinates were pulled directly from `DigiRadio.kicad_pcb` (RF1 at
104.064,92.281; C13 111.811,92.281; L1 112.319,89.868; C14 114.097,91.519; L3
115.621,89.868; L2 115.621,91.9; U6 121.717,90.122 — confirming the network's
physical path and component identity), then modeled as a two-port ABCD chain
(series C13 → series L1 → shunt bank C14‖L3‖L2 at the VHFI node), 50 Ω
reference on both ports. This is a lumped-element approximation: it ignores
PCB trace parasitics, the chip's real complex input impedance at VHFI, and
antenna radiation — good for an order-of-magnitude comparison against the
AN851 reference network, not an absolute number.
(A pure EM/gerber-based simulation via `gerber2ems`/openEMS, initially
considered, was ruled out for this specific question: per its own
documentation, `gerber2ems` does not model discrete capacitors/inductors —
"capacitors are not simulated... they can be approximated by shorting them
using a trace" — which would misrepresent a network that is almost entirely
discrete L/C components.)
**Result** (S21 = insertion loss, S11 = return loss, board network vs AN851
reference):
| Band | Board S21 | Reference S21 | Board S11 | Reference S11 |
|---|---|---|---|---|
| FM 87.5108 MHz | 7.1 to 9.8 dB | 1.2 to 1.5 dB | 0.5 to 0.9 dB | 5.4 to 6.3 dB |
| DAB 174240 MHz | 2.4 to 3.4 dB | 2.0 to 3.0 dB | 2.7 to 3.7 dB | 3.1 to 4.4 dB |
**FM**: the board network carries a real 69 dB insertion-loss penalty over
the reference network — worth correcting, but not by itself the kind of loss
that silences a strong local FM station on a working receiver (10 dB of
front-end loss is routinely tolerated).
**DAB**: the board network is within ~0.31.4 dB of the reference network —
essentially the same insertion loss. The mismatch is not a meaningful factor
at DAB frequencies at all.
**Conclusion**: since DAB shows the identical total-blackout signature as FM
(RSSI/SNR/VALID all zero, unmoved by ~100 ANTCAP sweep values) despite the
front-end mismatch being nearly irrelevant in that band, the network mismatch
cannot be the primary cause of the observed failure on its own. This is a
quantitative point in favor of the existing QFN exposed-pad hypothesis (§
"Leading hypothesis" above), not a competing explanation — it narrows, rather
than replaces, the open items in that section.
## 2026-08-16 update: hot-air reflow attempted — no change to RF symptom
The manual hot-air rework of U6 (re-melt only, no added solder/paste) flagged
as "action pending" in the Leading hypothesis section was carried out: 100°C
for 1 minute, then 220°C for 1.5 minutes, low airflow.
Post-rework, on a fresh build/flash of the current firmware, FM tuning was
retested at three frequencies (100.9, 95.0, 87.9 MHz) via `POST
/api/tuner/tune`. Result: **byte-for-byte identical to every pre-rework
capture in this report.**
```
Si4684: STC timeout: last poll spi_err=0 status=12 c0 00 00 c0 INTB=1
Si4684: FM tune STC timeout at 95000 kHz — settling 150 ms
Si4684: FM RSQ raw: 00 80 00 00 c0 00 00 00 00 00 00 00
Si4684: FM tuned 95000 kHz antcap=0 rssi=0 dBuV snr=0 dB valid=0 readfreq=0
```
Same at 100.9 and 87.9 MHz. RSSI/SNR/VALID all zero, `locked=false`, no
variation from the reflow.
**Item 2/3 (rework outcome) in "Open items" above is now closed: attempted,
no effect.** This does not rule out the QFN exposed-pad hypothesis — a
re-melt without added paste/flux does not reliably resolve a voiding defect
under an exposed pad (only adds heat to already-present solder, doesn't add
volume where a void is) — but it does mean the easy, low-risk fix attempt is
exhausted. Remaining paths are the non-destructive diagnostics proposed this
session (mechanical flex test with live RSSI monitoring, controlled thermal
stress test with live RSSI monitoring, NanoVNA S11 sweep at RF1 chip-on vs
chip-off) or escalating to X-ray/full chip removal, neither attempted yet.
## 2026-08-16 update: root cause found — FM_TUNE_FREQ/DAB_TUNE_FREQ argument-offset bug, not hardware
**This overturns the QFN exposed-pad hypothesis above.** The actual cause of
the months-long "total RF blackout" was a software bug in
`Si4684Driver::tuneFm()`/`tuneDab()`, found by diffing our command
construction against the official AN649 Command 0x30 (FM_TUNE_FREQ) and
Command 0xB0 (DAB_TUNE_FREQ) argument tables directly (page-level read of
`Hardware/DATASHEET/AN649.pdf`, not driver comments), prompted by cross-
referencing against the independent `hitech95/si468x_dab_receiver` Linux
driver.
`Si4684Driver::writeCommand()` always prepends a fixed `ARG1 = 0x00` byte
before whatever payload array is passed to it:
```cpp
buffer[0] = static_cast<std::uint8_t>(cmd);
buffer[1] = 0x00U; // ARG1, always
std::memcpy(buffer.data() + 2U, payload, length); // ARG2 onward
```
`POWER_UP` and `HOST_LOAD` callers already accounted for this correctly
(their arrays are written starting at ARG2). **`tuneFm()` and `tuneDab()`
did not** — both built their argument arrays starting at what the author
believed was ARG1, so every byte actually landed one slot to the right of
where it belongs, with an extra unused byte tacked on the end:
- **FM_TUNE_FREQ** (AN649 Command 0x30): real layout is ARG2=FREQ[7:0],
ARG3=FREQ[15:8], ARG4=ANTCAP[7:0], ARG5=ANTCAP[15:8], ARG6=PROG_ID. Our
code sent FREQ's low byte into ARG3 (should be the high byte), the actual
frequency low byte was always sent as a fixed `0x00`, and the ANTCAP value
landed in ARG5 (the *high* byte of a 0128-range field) instead of ARG4.
**The chip was never told the requested frequency** — it received a
garbage FREQ value derived from shifted bytes, and the ANTCAP sweep
documented earlier in this report (~100 values, byte-identical results)
was sweeping the wrong byte entirely, which is exactly why it never
produced any variation.
- **DAB_TUNE_FREQ** (AN649 Command 0xB0): same shift. `FREQ_INDEX` (real
ARG2) was always sent as `0x00`; the actual requested index landed in
ARG3, which the spec requires to be a fixed `0x00`.
Fixed in `components/drivers/si4684/src/Si4684Driver.cpp`, `tuneFm()` and
`tuneDab()`: removed the extra leading byte and the extra trailing byte so
the arrays start at the real ARG2.
**Result, live on hardware immediately after the fix** (`POST
/api/tuner/tune`, no other change — same antenna, same board, no rework
involved in this result):
```
Si4684: FM RSQ raw: 00 81 80 00 c0 00 02 2e 22 8d fb fd
Si4684: FM tuned 87500 kHz antcap=0 rssi=-5 dBuV snr=-3 dB valid=0 readfreq=87500
```
RSSI/SNR now read real, varying, frequency-dependent values (e.g. 13 to +4
dBuV across a 10-point FM sweep, peaking near a plausible local station at
98.5 MHz) instead of the fixed `00 80 00 00 c0 00 00 00 00 00 00 00` /
all-zero pattern seen in every capture in this report until now. **No STC
timeout occurred in any tune or seek attempt after the fix** — every prior
capture in this document logged one on every single attempt.
`locked`/`valid` is still `false` in this test — expected with the
board's improvised antenna and not yet investigated further; that is now an
ordinary sensitivity/antenna question, not a "chip never responds to RF"
question. DAB was retested at freq_index=10 with no station found
(`fic_quality=0`, `cnr_db=0`) but also with no STC timeout — most likely no
active multiplex at that index/location, to be swept properly with a real
antenna as a follow-up, not evidence against the fix (which addresses the
identical byte-shift bug in both commands).
**What this means for the rest of the investigation**: the QFN exposed-pad
hypothesis, the front-end network mismatch analysis, the hot-air reflow, and
the mechanical flex test were all investigating a symptom that had a
software cause. None of that work was wasted — the empirical rigor (ANTCAP
sweep producing zero variation, DAB and FM failing identically) is exactly
what made this bug's fingerprint recognizable once the actual command bytes
were checked against the primary spec instead of trusted from driver
comments. The lesson: `writeCommand()`'s implicit ARG1 prepend is an easy
trap for future commands — any new caller must remember its array starts at
ARG2, not ARG1.
**Follow-up**: get a proper antenna connected and confirm an actual station
lock (`valid=1`) on both FM and DAB; audit other `writeCommand()` call sites
in `Si4684Driver.cpp` for the same off-by-one pattern (POWER_UP and
HOST_LOAD were checked and are correct; FM_SEEK, property writes, and RSQ/
DIGRAD status reads have not yet been re-verified against AN649 page text).
**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
the firmware build that predates and the one that includes the boot-sequence
fix from `fd9d4ae` — ruling out that fix's absence as the cause. Extending
the diagnostic listen window from 3.5 s to 12 s (temporary, reverted)
produced zero bytes either way, confirming this is not the previously-fixed
"banner arrives late" timing issue but a harder, total UART silence. The
BT1035 module was not physically touched during the U6 rework. Cause not
yet identified; unrelated to the Si4684 investigation (separate chip), but
`HardwareBootstrap::boot()` was changed (`main/hardware_bootstrap.cpp`) to
treat BT1035 boot failure as non-fatal rather than halting the whole device,
so the rest of the system (Si4684 tuning, web UI, Wi-Fi) remains usable
while this is investigated separately.