Repository navigation
Conversation
AutoDiscoverRTCClock::begin() adopted each RTC on an I2C ACK alone, so any device answering at 0x68, 0x52, 0x51 or 0x32 (an IMU, a 24-series EEPROM) became the clock: read as the time, and written on every sync. Upstream already worked around this per board with DISABLE_DS3231_PROBE for IMUs at 0x68. Read the seven time registers and skip the device if they cannot belong to that chip, per its datasheet: bits documented as always 0 for the DS3231, RV3028 and RX8130CE; documented field ranges for the PCF8563, whose unused bits are undefined, accepting it while VL is set since its time is then undefined. Two reads must both rule a device out; a failed read keeps today's behaviour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hardware testing showed the always-zero bits alone pass a register block of zeros, such as an LPS22HB pressure sensor's. Also require seconds, minutes, date and month to be valid BCD in range, unless the chip's documented power-loss flag (DS3231 OSF, RV3028 PORF, PCF8563 VL, RX8130CE VLF) is set, as each sets it at power-up when its time is undefined. From review: read with a repeated start, the only form the RX8130CE manual documents; never check the year, which MeshCore itself can write out of range from a bad epoch; compile the DS3231 table out with DISABLE_DS3231_PROBE; cite the RV3028 overview on p. 12; and state that a device whose bytes happen to fit is still adopted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RTC_RX8130CE::begin() clears the flag register, VLF included, on every boot without setting the time. A chip powered up with undefined time was adopted on the first boot, then ruled out on the next one if no sync came in between, and never adopted again. Check only its always-zero bits. Also correct the comment: the RV3028's time is defined at reset, and an all-0xFF block is ruled out even with VL set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
Hardware-specific RTC detection changes warrant final human validation.
Review effort: Lite
Findings: None
What changed in this PR
Adds chip-specific RTC register validation to prevent false I2C device detection during automatic RTC discovery.
Changes:
- Validates register masks, BCD fields, and power-loss conditions.
- Requires two consecutive rejection reads.
- Applies checks to all four RTC probes while preserving opt-outs.
| File | Description |
|---|---|
src/helpers/AutoDiscoverRTCClock.cpp |
Adds RTC identity validation and gates device adoption. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Upstream: PR meshcore-dev#3544 (from |
A DS1307 also answers at 0x68 and is driven by the DS3231 code. Its 00h bit 7 is CH, clock halt, set on first application of power (Maxim DS1307 Rev 3/15, p. 8), so the DS3231 mask ruled out every new DS1307 and its clock was never started. Drop that bit; the DS1307's other fixed bits read as 0 (its Table 2, p. 8) and match the DS3231's. Found by cross-checking the ZephCore implementation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Iteration branch for #13. Keep it open; never merge it. A clean single-commit branch for upstream is cut from it once it has baked. Upstream takes PRs on
dev.References used throughout. Each "§", table and "p." number below is the cited document's own, and page numbers are the printed ones.
dev3e3150c8c36cb9ee2.1.4, the version MeshCore buildsThe defect
#13 has the full write-up. In short,
begin()adopts each RTC on an I2C ACK alone. Any device answering at 0x68, 0x52, 0x51 or 0x32 then becomes the clock:getCurrentTime()reads its registers as the time.setCurrentTime()writes its registers on every sync, which includes GPS every 30 minutes.begin().Upstream has already worked around this board by board for IMUs at 0x68:
e7e97ec4, with lilygo_techo_card0908b034, with heltec_t1: "producing a bogus, unfixable clock"The fix
Each RTC is adopted only if its time registers read like that chip's. Each rule below is taken from that chip's datasheet (L29-L38, table L39-L65).
rtcCheck()rules a device out if any of these hold:Always-zero bit set. A bit the datasheet documents as always 0, in the seven time registers, reads 1:
00 80 80 F8 C0 60 00(05h bit 7 is Century, so it is allowed; 00h bit 7 is left out for the DS1307, see below)80 80 C0 F8 C0 E0 0080 80 C0 80 C0 E0 00, where "'0' means … the read value is always 0"All 0xFF. All seven bytes read 0xFF, as an erased 24-series EEPROM does.
Invalid time with the power-loss flag clear. Seconds, minutes, date or month is not valid BCD in range, and the chip's power-loss flag is clear. Each chip sets that flag at power-up, when its time may be undefined:
The RX8130CE is exempt. Its flag VLF (1Dh bit 1) cannot vouch for the fields, because
RTC_RX8130CE::begin()clears it on every boot without setting the time.How the decision is made:
rtcRuledOut()rejects a device only if two reads both rule it out. A failed read never does, so it keeps today's behaviour.rtcRead()uses a repeated start. That is the only read form the RX8130CE manual documents (§19.6), and all four documents allow it.year - 2000as BCD, so a bad epoch of 2100 or later givesA0+. A chip left in 12-hour mode sets the hours' PM bit. Neither may rule out a real chip.begin(). The existingDISABLE_DS3231_PROBEopt-outs stay, and compile the DS3231 table out.Notes for review
B1at 0Fh) sits where the DS3231 keeps OSF, so it reads as "power lost". The per-board opt-outs remain the answer for a known conflict.adjust()clears its clock-halt bit. It powers up with that bit (00h bit 7, CH) set (DS1307 Table 2 and text, p. 8), so the 0x68 mask leaves 00h bit 7 out. Every other mask bit is documented 0 on the DS3231, the DS3232 (Figure 1, p. 11) and the DS1307. A new DS1307 (seconds80, date and month01) then passes the field check. The trade-off: an ICM-20948's WHO_AM_I (EAat 00h) no longer trips the mask. It fails the field check instead, so it is ruled out unless its 0Fh has bit 7 set. Boards with that IMU keepDISABLE_DS3231_PROBE.begin(), and gates its own EEPROM writes with an RV3028-only always-zero check. Whichever lands second takes the other'sbegin()hunk.Testing
Builds: release builds of
RAK_4631_repeater(nRF52840),heltec_v4_repeater(ESP32-S3),RAK_11310_repeater(RP2040),Heltec_t1_repeater(withDISABLE_DS3231_PROBE) andR1Neo_repeater(the RX8130CE board), plusMESH_DEBUG=1on the first three. No new warnings.Hardware (read-only). A RAK4631 with a RAK12002 (RV3028), a RAK1902 (ST LPS22HB) and a RAK1906 (Bosch BME680) on the I2C bus, and nothing else, per an ACK scan. Its RAK12501 GNSS (Quectel L76K) is on the UART, not the I2C bus. The build is
test/rtc-probe-diag(cc4bf1fa):0aacafa4plus a throwaway serial CLI. The later change that leaves 00h bit 7 out of the 0x68 mask was recomputed against the raw blocks below and changes no verdict. It ACK-scans the bus, then runs each chip's check against each address found, using register reads only.06 09 12 01 08 01 00B1)00 00 00 00 00 00 0061)33 AA 16 49 13 01 39ZephCore, a separate firmware, has an independent implementation of the same identity rules: ptr727/liquidraver-ZephCore#40, for its issue #38. Run read-only on this same board at
a7b43f6(raw log), it gave the same verdicts at every address, with one recorded exception. ZephCore keeps a field check for the RX8130CE, so it also rules out the LPS22HB under that rule. MeshCore cannot keep that check, because its RX8130CE driver clears VLF on every boot.The two implementations agree on the rules and the masks. Where they differ, the difference is deliberate and has a recorded reason:
getCurrentTime()behaviour.An earlier build that checked only the always-zero bits let the LPS22HB's all-zero block pass three of the four rules. That result is why the field check with the power-loss exception was added.
Refs #13
🤖 Generated with Claude Code