Hardware: Quectel LC76GABMD on a Waveshare ESP32-S3-Touch-AMOLED-1.75 (GPS variant), ESP32-S3 host, ESP-IDF 6.0.2, driver/i2c_master.h. Bus: shared I2C, SDA GPIO15 / SCL GPIO14, 100 kHz, 2.2 kΩ pull-ups. Other slaves: QMI8658 (0x6A), CST9217 (0x5A), PCF8563 (0x51), AXP2101 (0x34), TCA9554 (0x20). All access serialised by one mutex. GNSS VCC: hard-wired to the 3V3 system rail (not switchable). V_BCKP: permanently powered from the RTC rail. RESET_N: via TCA9554 P7.
What works
Full 4-step read and 4-step write protocol per Quectel’s I2C Application Note, with:
-
separate I2C frames per step (never repeated-START across 0x50/0x54);
-
≥10 ms delay before every step;
-
500 ms receive timeout on 0x54 (the module clock-stretches up to ~200 ms);
-
512-byte read chunking;
-
continuous drain of the 4096-byte TX buffer to
data_len == 0every cycle; -
dummy write to 0x50 after 5 consecutive zero-length results, to avoid inactivity sleep;
-
NMEA XOR checksum validation.
Result in normal operation: 3D fix, 7–14 satellites, stable RMC/GGA/GSA/GSV.
What does not work
-
400 kHz — corrupt reads on this board. Stayed at 100 kHz.
-
Repeated-START — NACK or garbage.
-
Raw NMEA written to 0x50 — corrupts module state; recovered only by a long power-off.
-
$PAIR690,0— left the module unresponsive; removed from our firmware entirely. -
Short (20 ms) read timeouts — misread clock stretching as failure.
The failure
After a period of healthy operation the module’s transmit side jams: 0x50 keeps ACKing every command, 0x54 NACKs every read, permanently.
Measured over one 228 s session with per-cause counters separated:
gps_rxfail (read fail on 0x54, 4 B) : 849
gps_nack (NACK on 0x50) : 0
gps_dfail (read fail on 0x54, data): 0
gps_busy (genuine 0xFFFFFFFF) : 0
Each failure returned in ~109 ms, not at the 500 ms timeout → active NACK from the module, not a bus timeout and not clock stretching. The rest of the bus stays healthy throughout.
Recovery attempts — all failed
-
Dummy write to 0x50 (the documented wake-from-sleep trick) — no effect. Note that every GETLEN already is a write to 0x50, so hundreds of them were sent with no change.
-
Host-side I2C bus reset — no effect.
-
RESET_N pulsed low 10 ms, then 500 ms, with retries and back-off. Pin state verified by reading the expander register back — the pin really toggled. No effect.
-
Attempted PMIC power cycle — electrically a no-op, GNSS VCC is not on a switchable rail.
-
Physical power disconnect (USB + battery) for ~10 minutes — this is the only thing that works. Our interpretation: V_BCKP is never removed by anything else, so the module’s I2C state and TX buffer survive every reset available to us.
Questions
-
Has anyone else seen a permanently jammed 0x54 while 0x50 still ACKs on an LC76G(AB)?
-
Is there a software-side reset of the I2C slave block that does not require dropping V_BCKP?
-
Is a dedicated I2C bus effectively mandatory for this module, or should mixed-slave buses be fine?
-
Is there a known trigger (command, latency between GETLEN and GETDATA, partially drained buffer) that causes this?
-
Would migrating to UART be the recommended fix? That is the direction we are leaning: point-to-point, no shared-bus contention, no buffered protocol to jam.
Happy to post full transaction logs, CSV counter logs, driver source and the schematic if that helps. Any pointers appreciated.