Skip to content

Store RV3028 backup switchover and trickle charger config in EEPROM (iteration branch) - #11

Open
ptr727 wants to merge 22 commits into
devfrom
work/rv3028-eeprom-config
Open

ptr727 wants to merge 22 commits into
devfrom
work/rv3028-eeprom-config

Conversation

@ptr727

@ptr727 ptr727 commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Iteration branch for #10. Keep it open; never merge it. The upstream PR comes from the clean single-commit branch fix/rv3028-eeprom-config (f64cfc2c), which is byte-identical to this branch's head 037d493b. Upstream takes PRs on dev.

References used throughout:

  • The manual: RV-3028-C7 Application Manual, Micro Crystal, Rev. 1.4, November 2021, from the Documents section of the product page: https://www.microcrystal.com/en/products/real-time-clock-rtc-modules/rv-3028-c7. Every "§" section number and "p." page number below is that document's.
  • The code: links point at fixed commits:
    • before: upstream dev fad0ffb7, which this branch is based on
    • after: the clean commit f64cfc2c
    • the RTC library MeshCore builds against: Melopero RV3028 Arduino library, tag 1.2.0

The defect

AutoDiscoverRTCClock::begin() configured the RV3028 with two plain register writes (L38-L39):

rtc_rv3028.writeToRegister(0x35, 0x00);
rtc_rv3028.writeToRegister(0x37, 0xB4);

Melopero's writeToRegister() is a plain I2C write, so these set only the RAM mirror of EEPROM-backed configuration registers:

  • The setting doesn't last. With EERD = 0, which is the default (Control 1, §3.7, p. 23), the chip reloads that mirror from EEPROM at power-on and every day at midnight (§4.6.2, p. 54). §4.6.9 (p. 57) warns that "the new, changed configurations are lost as soon as a refresh occurs".
  • The factory EEPROM undoes it. It holds BSM = 00 and TCE = 0 (§3.15.6, p. 39), so the backup switchover and the trickle charger switch off at the first midnight after boot. After that a power cut loses the time, and the supercap is no longer charged.
  • The calibration bit gets overwritten. 0xB4 also forces 37h bit 7, which is EEOffset[0], the LSB of the factory frequency calibration (§3.15.6, p. 39).

The mode itself is right. §7.3 (p. 105) specifies DSM with the trickle charger for a capacitor backup, and adds: "Power Management settings have to be stored in EEPROM for permanent configuration".

The fix

The config is stored in EEPROM following the manual, including §3.15.6 (p. 39): BSM must be 00 or 10 for any EEPROM read or write. Links are to f64cfc2c.

  1. Identity check. rv3028Impostor(), called from begin(), skips the config if a time register (00h-06h) shows a bit an RV3028 always reads as 0 (§3.2, p. 12, and §3.3, p. 14), confirmed by a second read. A device at 0x52 that is not an RV3028, such as a 24-series EEPROM, then gets none of these writes. A failed read does not count.
  2. Hold off refresh. rv3028StoreConfig() sets EERD = 1, then waits for EEbusy = 0 (§4.6.7, p. 56). This also covers the ~66 ms POR refresh (§4.6.1, p. 54).
  3. Switchover off. It saves RAM 37h, then writes it back with BSM = 00, so the switchover is off while the EEPROM is accessed (§3.15.6, p. 39).
  4. Compare and write per byte. For each byte in rv3028_config, it reads the EEPROM copy with a single-byte read (EECMD 22h, §4.6.6, p. 55). It writes with a single-byte write (21h, §4.6.5, p. 55) only if the byte differs (rv3028EepromRead/Write()). The bits written:
    • 35h: CLKOE = 0 (§3.15.4, p. 37).
    • 37h: TCE = 1, FEDE = 1, BSM = 01, TCR = 00 (§3.15.6, p. 39). The manual says FEDE "should always be set to 1", and the old 0xB4 set it too.
    • EEOffset[0] and BSIE keep the chip's own values. 36h, the rest of the factory trim, is never written.
  5. Refresh and verify. It sends a Refresh (12h, §4.6.4, p. 54), which reloads RAM from EEPROM and brings back the stored switchover mode. Then it reads the config back, even when nothing was written, so every boot confirms the switchover came back.
  6. Restore if the Refresh failed. If the Refresh did not complete, it waits for EEbusy (best effort), then writes the saved 37h back so the switchover is not left off.
  7. Release refresh. It clears EERD. Control1 is read again first, because the chip clears TE itself when a single-shot countdown ends. If that re-read fails, Control1 is left alone and the store reports failure.

Waits and transfers:

  • rv3028EepromCommand() waits per §4.6.7 (p. 56): 1 ms after a read or Refresh, 10 ms after a write. Each gets an extra 1 ms, because Arduino delay() can return early on some cores.
  • rv3028EepromIdle() ends the EEbusy wait on the first failed status read, so a dead bus is not polled through each transfer's timeout.
  • Every transfer is checked through TwoWire directly (rv3028Read/Write()). Melopero's readFromRegister() ignores the I2C results and returns 0xFF on a failed transfer, and a failed read must never be written back.

Failure handling: rv3028Configure() runs the store. If it fails, it sets the RAM mirror as before, after a best-effort EEbusy wait, with a checked read-modify-write (rv3028SetRam()). A RAM-only config lasts only until the next refresh, so getCurrentTime() re-runs the configuration every 10 minutes, at most 3 times per boot (L160-L168). The cap is there because a password-locked chip, or a foreign device the identity check missed, never succeeds, and each attempt writes to it again. Failures are logged with MESH_DEBUG_PRINTLN.

An already-configured chip costs two EEPROM byte reads and a Refresh per boot, and no EEPROM write.

Notes for review

  • Why §3.15.6 instead of the vendor driver's Update. Micro Crystal's Linux driver and Zephyr's drivers/mfd/mfd_rv3028.c run a Refresh and a whole-block Update (§4.6.3, p. 54) with BSM live. That was this branch's first version too, f0af008a. Testing on two boards found no spurious switchovers either way (see Testing), and the maintainer chose §3.15.6 on the manual:
    • It is the only sequence that stays valid when the backup sits above VDD. In DSM (§4.2.2, p. 46) the chip then runs from backup permanently, and an EEPROM write needs VDD (§4.6.8, p. 57).
    • It never rewrites the 36h trim.
    • Its cost: the switchover is off for an estimated 5-10 ms per boot, plus up to 2 ms for DSM to react once re-enabled (§4.2.2, p. 46). None of this was measured. A full power cut inside that window loses the time.
  • Behaviour change: the settings now outlive MeshCore. A module flashed once keeps DSM and trickle charging after a reflash to other firmware, or after a move to another carrier. That is the point of the fix. It matters only for a carrier whose backup element is not rechargeable.
  • Retry limits. A store that fails only at the final EERD clear is retried too. That retry finds the EEPROM matching and writes nothing. After the 4th failed attempt nothing retries until the next boot. If that last attempt also lost the RAM fallback to a bus fault, RAM can keep BSM = 00, or EERD can stay 1. EERD = 1 keeps the RAM config, but it also stops the daily refresh (§4.6.2, p. 54) that would otherwise repair a RAM config byte corrupted later. Reaching that state takes 4 failures over about 30 minutes.
  • 35h: the old 0x00 also cleared CLKSY, PORIE and FD in RAM. Those now keep the chip's values (§3.15.4, p. 37). With CLKOE = 0 the pin is held low, so CLKSY and FD have no effect. PORIE stays at its factory 0.
  • Out of scope, pre-existing:

Testing

Builds: RAK_4631_repeater (nRF52840), heltec_v4_repeater (ESP32-S3) and RAK_11310_repeater (RP2040), release and MESH_DEBUG=1, with no new warnings.

Hardware setup:

  • Board and RTC: a RAK4631 with a RAK12002 (RV3028) whose EEPROM still held factory values: 35h = C0, 37h = 10, EEOffset[0] = 0.
  • No GPS time sync: the board also carries a RAK12501 (Quectel L76K, on the UART). It was indoors on the bench with no fix, so GPS never set the clock during these tests.
  • Power: USB only, no battery.
  • Diagnostic CLI: each build had a throwaway serial CLI added, published as test/rv3028-diag (290cfc5b, never merged). Its rv dump prints RAM and EEPROM 35h/37h and status 0Eh. Its rv set writes the time registers, so the RTC could be stepped to 23:59:50 to pass midnight in seconds. Its rv factory restores EEPROM 35h/37h to their factory values, keeping EEOffset[0].
  • Power cuts: done by the maintainer: USB unplugged for about 60 s, after the RTC had passed midnight.
Build (ref) Test Result
dev fad0ffb7 boot on factory EEPROM RAM 35h/37h = 00/B4: bit 7 forced to 1 against the chip's 0. EEPROM still C0/10
dev fad0ffb7 RTC passes midnight RAM back to C0/10: switchover and trickle charger off, CLKOUT on
dev fad0ffb7 after midnight, ~60 s power cut time lost: the RTC came back at 2000-01-01, 0Eh = 11 (PORF = 1)
first version f0af008a (vendor Update sequence) store, midnight, reboot, ~60 s power cut EEPROM 40/34 written once. Held past midnight. No write on reboot. Time kept to the second (the RTC advanced 4:47 against 4:47 wall clock), 0Eh = 30 (BSF = 1, PORF = 0)
§3.15.6 sequence 4f6de862 store from factory, midnight, reboot, ~60 s power cut EEPROM 40/34 written once, RAM BSM restored. Held past midnight. No write on reboot. Time kept to the second (4:07 against 4:07), 0Eh = 30
4f6de862 with the 124bdf41 wait change store from factory, reboot written once; no write on reboot
final logic 6faac3bc store from factory, reboot, midnight 2 EEPROM bytes written, then 0 on reboot. Held past midnight. The identity check accepted the real RV3028

037d493b, and so the byte-identical f64cfc2c, differs from 6faac3bc only by the identity check's confirming second read and comment fixes. On a real RV3028 that second read never runs, so it was not flashed separately.

Spurious switchovers: status 0Eh was polled every 30 s for 31 minutes on the f0af008a build, with DSM and the trickle charger stored, across 3 reboots. BSF never set. Spot reads across the session's serial-DFU flashes showed it set only after the deliberate power cuts.

Second implementation, same hardware. ZephCore carries the same §3.15.6 sequence in ptr727/liquidraver-ZephCore#35 (see its Testing section). On a second RAK4631 + RAK12002, with USB only and the power cuts also done by the maintainer:

  • Without the switchover configured, a 30 s cut came back with the power-loss flag set and the clock at 1970.
  • With it stored in EEPROM, a 31 s cut kept the time, with PORF clear and BSF set.
  • A 55-minute 0Eh monitor saw no spurious BSF.

Refs #10

🤖 Generated with Claude Code

ptr727 and others added 3 commits October 3, 2026 18:01
AutoDiscoverRTCClock::begin() wrote 35h and 37h with plain register
writes, which set only the RAM mirror. With EERD = 0 the RV3028 reloads
that mirror from EEPROM every day at midnight, so on a part still holding
the factory EEPROM (BSM = 00, TCE = 0) the backup switchover and trickle
charger turned off at the first midnight after boot. Writing 0xB4 to 37h
also forced bit 7, the LSB of the factory frequency calibration.

Follow the manual's procedure (RV-3028-C7 App Manual 4.6): wait for
EEbusy, set EERD, Refresh, read-modify-write only CLKOE in 35h and TCE,
BSM and TCR in 37h, Update only if something changed, Refresh and read
back, then clear EERD. If the store fails, set the RAM mirror as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Melopero's readFromRegister() returns 0xFF on a failed transfer. Fed into
the read-modify-write, one glitched read of 37h would have been stored in
EEPROM, flipping EEOffset[0] and BSIE. Read and write through TwoWire
directly and abort on any error. Set EERD before waiting for EEbusy, in
the manual's order.

The old 0xB4 also forced FEDE = 1, which the manual says should always
be set. Include it in the 37h mask.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Writing back the copy taken at the start could re-arm a countdown timer
the chip ended by itself in between (TE clears when a single-shot
countdown ends). Read Control1 again and clear only EERD, falling back
to the copy if that read fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings October 4, 2026 01:21
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 5512aa2c-e333-4302-9e38-1ce11963d58d
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Handle failures from the fallback rv3028Write() operation.

Review effort: Lite
Findings: None

What changed in this PR

Updates RV3028 initialization to persist backup switchover and trickle-charger settings in EEPROM while preserving factory calibration bits.

Changes:

  • Adds checked EEPROM refresh/update handling.
  • Uses masked read-modify-write configuration.
  • Retains a RAM-only fallback when EEPROM configuration fails.
File Summary
src/​helpers/​AutoDiscoverRTCClock.cpp Implements persistent RV3028 configuration and fallback handling. The fallback does not check rv3028Write() failure.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@ptr727

ptr727 commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

On the overview note "the fallback does not check rv3028Write() failure" (head f0af008): declining, deliberately.

  • The fallback in begin() is the last resort. It runs only after rv3028StoreConfig() has already failed, so if its RAM write fails too, nothing is left to try.
  • No caller consumes the result. rv3028_success = true follows unconditionally, exactly as on dev. The old code ignored both of its writeToRegister() results, which return void.
  • The read is what must be checked. if (rv3028Read(reg, old)) ensures a failed read is never written back, which was the calibration-bit hazard. An unchecked write can only leave the register as it was.

ptr727 and others added 4 commits October 3, 2026 19:26
Manual 3.15.6: BSM must be 00 or 10 for any EEPROM read or write. Hold
BSM = 00 in RAM during the procedure, compare each configuration byte
against the EEPROM itself with single-byte reads, write only the bytes
that differ, then Refresh, which reloads RAM from the EEPROM and
restores the stored switchover mode. This replaces the whole-block
Update, so the factory calibration in 36h is never rewritten. If the
Refresh does not complete, the saved 37h is written back so the
switchover is not left disabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
delay() can return up to 1 ms early on the nRF52, ESP32 and STM32 cores,
so delay(1) after a single-byte EEPROM read could poll EEbusy, and read
EE_DATA, before the read had started. Wait one extra millisecond.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On nRF52, delay() can return more than 1 ms early. State only that it does not guarantee the full time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The EEPROM store parks BSM = 00 in RAM. If the store fails and a bus
fault also defeats both the restore of 37h and the RAM fallback, the
switchover stays off, until the next boot if EERD was left set. Record
that and retry the RAM config from getCurrentTime() until it succeeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Address the unchecked control read, preserve pending EEPROM retries after fallback, and fix the helper-comment wording.

Review effort: Lite
Findings: None

If the store fails at boot but the bus recovers, the RAM fallback holds
only until the next refresh. On a part still holding the factory EEPROM
that turns the switchover off again at midnight, until the next boot.
Retry the whole store from getCurrentTime(), at most once an hour since a
failing store blocks while it polls EEbusy. The RAM config is still
retried on every read while it is unconfirmed. Also state that
rv3028StoreConfig() returns false when EERD could not be cleared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ptr727

ptr727 commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

Disposition of the three overview notes on e16bf2e. None of them opened a thread.

  1. "Preserve pending EEPROM retries after fallback": fixed in ec0b0ee. The gap was real: if the boot store failed but the bus then recovered, the RAM fallback lasted only until the next refresh. On a part still holding the factory EEPROM, the switchover turned off again at midnight, until the next boot. A failed store now sets rv3028_store_pending, and getCurrentTime() re-runs the whole store, reading the EEPROM fresh. It does this at most once an hour, because a failing store blocks while it polls EEbusy (each transfer can take the 50 ms Wire timeout on ESP32). The RAM config is still retried on every read while it is unconfirmed. A retry writes an EEPROM byte only when a successful single-byte read shows a mismatch, so repeated retries cause no wear.
  2. "Fix the helper-comment wording": fixed in ec0b0ee. The rv3028StoreConfig() comment now also says it returns false when EERD could not be cleared afterwards.
  3. "Address the unchecked control read": declining. The only unchecked Control1 read is the re-read just before EERD is cleared, and it is unchecked on purpose. rv3028Read() assigns its out-parameter only on success, so on failure ctrl1 keeps the copy read, and checked, at the start of the function. & ~0x08 then clears EERD from that copy. The comment above it documents this. The re-read exists only because the chip clears TE itself when a single-shot countdown ends, and nothing in MeshCore uses that timer.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Avoid writing stale Control1 state when the final read fails.

Review effort: Lite
Findings: 1 Medium severity

Open (1)

Comment thread src/helpers/AutoDiscoverRTCClock.cpp Outdated
Writing back the copy taken before the EEPROM procedure could set TE
again after a single-shot countdown ended in between, re-arming it.
Treat a failed re-read as a failed store instead: EERD stays set, which
keeps the RAM config, and the hourly store retry clears it later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Low-level RTC EEPROM sequencing and recovery behavior warrant final human review.

Review effort: Lite
Findings: None

Resolved since last review (1)

ptr727 and others added 3 commits October 3, 2026 20:02
A password-locked chip, or a non-RTC device answering at 0x52, never
completes the store, so the hourly retry ran forever, each run stalling
getCurrentTime() and, on a non-RTC device, writing to it again. Stop
after 3 retries per boot. Log a failed store, and a failed RAM fallback,
with MESH_DEBUG_PRINTLN. Matches the ZephCore fix's retry cap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A failed status read counted as busy, so on a dead bus each of the 100
polls ran into the I2C transfer timeout: about 5 s per wait on ESP32,
whose Wire timeout is 50 ms. Fail the step at once instead; the capped
retry tries again later. Found by cross-checking the ZephCore fix, where
a 1000 ms transfer timeout makes the same wait last about 100 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found by cross-reviewing this fix against the ZephCore one:

- Skip the config when a time register shows a bit an RV3028 always
  reads as 0 (manual 3.2): a 24-series EEPROM answering at 0x52 would
  otherwise get Control1, EEPROM-command and config writes.
- Drop the per-read RAM retry. On such an EEPROM it rewrote a byte on
  every clock read. Retry the whole configuration instead, at most 3
  times per boot, every 10 minutes now that a failed attempt is cheap.
- Read the config back after the closing Refresh even when nothing was
  written, so each boot confirms the switchover came back.
- Wait for EEbusy, best effort, before re-enabling the switchover after
  a failed step, so a still-running EEPROM operation can finish.
- Mask the table's values, and note in the header that begin() and
  getCurrentTime() can block briefly on the RTC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A single corrupted read that showed an always-zero bit set skipped the config of a real RV3028 for the whole boot. Count a register only when two reads agree. The retry comments promised a retry the cap can rule out, and the DSM comment above the impostor check described neither it nor the config call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ptr727
ptr727 requested a lite review from Copilot October 4, 2026 03:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

EEPROM power-management sequencing and failure recovery warrant final human review.

Review effort: Lite
Findings: None

ptr727 added a commit to ptr727/liquidraver-ZephCore that referenced this pull request Oct 4, 2026
Closes the failure-path items #35 listed as open, in the shape the
MeshCore fix (ptr727/meshcore-dev-MeshCore#11, 037d493b) settled on, so
the two behave alike:

- EEbusy wait: a failed status read ends it at once. With this board's
  1000 ms I2C transfer timeout, polling through failures could block
  about 100 s at boot or on the system work queue.
- Read-back: runs after every Refresh, not only when a byte was written,
  so every boot confirms the stored switchover mode came back.
- Fallback: when the Refresh did not complete, 37h is written as
  configured only after a best-effort wait for EEbusy, so an EEPROM
  operation still running normally finishes first.
- Any failed store, including one whose Refresh completed but whose
  read-back did not match, now sets the config in RAM (masked
  read-modify-write), so switchover is on until the chip's next daily
  refresh, by which time a retry has normally stored it.
- Retry: a delayed work item re-runs the store every 10 minutes, at most
  3 times per boot, instead of only on a save, which a node without GPS,
  app or CLI syncs never makes. Each failed attempt logs whether the RAM
  fallback took.
- Identity: an RV3028 always-zero bit must show on two reads before a
  chip at an RV3028 descriptor is refused, so one corrupted read does
  not cost a real RV3028 its adoption.

The header and binding now state the two-read identity check, the RAM
fallback and its lifetime, and that the power-on refresh is waited out
before switchover is turned off rather than inside that window.

Still open, as in MeshCore: a password-locked chip can report success
with nothing written (manual 4.18.1), and the switchover-off window is
estimated, not measured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ptr727

ptr727 commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

Upstream: PR meshcore-dev#3545 (from fix/rv3028-eeprom-config at 84691667, byte-identical to this branch's a9989594) and issue meshcore-dev#3547. Companion hardening: meshcore-dev#3544.

From review: "so these check" did not say what checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ptr727 and others added 7 commits October 5, 2026 07:15
The impostor check passed a device whose reads failed, so a device at 0x52 that one read had
ruled out could still get EEPROM writes if the confirming read failed. The config store now
runs only when a read of the time registers shows no bit an RV3028 always reads as 0, with a
second read taken when the first rules the device out. This matches the rule adopted for the
ZephCore counterpart (liquidraver/ZephCore#98 review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A failed identification read skipped the config for the whole boot, with no retry, leaving a
factory-fresh part's switchover off where the old code at least set it in RAM. Identification
now runs inside each configure attempt: a failed read writes nothing and is retried from
getCurrentTime() like a failed store, while two reads that rule the device out end it.

Also say what the check proves (the always-zero bits read as 0, not that the part is an
RV3028), and drop the stale reason from the retry-cap comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A switchover to VBACKUP means VDD is unstable, and the EEPROM needs VDD (manual 4.6.8). Once
the chip reads like an RV3028, BSF is cleared, the time registers are read again, and BSF must
still read 0; otherwise the store waits for the retry. BSF is cleared first because a power cut
before this boot leaves it set. This matches the ZephCore counterpart (liquidraver/ZephCore#98).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A switchover releases the bus, which reads as 1s, so two reads that rule the device out now
end the config only if BSF reads 0 afterwards; otherwise identification is not settled and is
retried. The deferral log and comment now name every cause, not only a switchover at boot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Manual 3.15.6 requires BSM = 00 or 10 for any EEPROM read or write, and an operation is still
running while EEbusy reads 1. Neither the restore after a failed Refresh nor the RAM-only
fallback now writes the switchover back unless EEbusy reads 0 within the bound; otherwise it
stays off until the next refresh or retry restores it from the EEPROM. EERD is still cleared.
This is the rule agreed for MeshCore, liquidraver/ZephCore#98 and zephyrproject-rtos/zephyr#121252.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A command whose write reported failure may still have been latched by the chip, and EEbusy
read within a millisecond of it proves nothing (manual 4.6.7 waits 10 ms after a write). The
restore now waits as long before checking. The comments no longer claim a refresh restores the
switchover: on a part still holding the factory BSM = 00 only a later attempt does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The restore comment named only a retry's store; the RAM fallback in the same attempt, or any later attempt, can also bring it back, as rv3028Configure()'s comment already says.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants