Skip to content

fix: persist PAN ID selection correctly, disconnect radio on Factory Reset - #46

Merged
thePunderWoman merged 2 commits into
mainfrom
fix/persist-current-droid-selection
Sep 8, 2026
Merged

fix: persist PAN ID selection correctly, disconnect radio on Factory Reset#46
thePunderWoman merged 2 commits into
mainfrom
fix/persist-current-droid-selection

Conversation

@thePunderWoman

Copy link
Copy Markdown
Owner

Summary

Fixes a real gap found during review of the merged firmware rewrite: switching droids only updated the radio's in-RAM ID setting and an in-memory display name — neither survived a power cycle, so the controller wouldn't reliably reconnect to the last-selected droid after a reboot. The droid list was already persisted (droid_persistence.cpp); which one is currently active was not.

  • XbeeControl::setPanId() now also sends WR after ID, committing the PAN ID to the module's own flash. Per how XBee modules actually behave, this makes the module remember it across power cycles on its own — nothing needs to re-apply it at boot, and it only changes again when setPanId() is explicitly called for a new droid.
  • XbeeControl::queryPanId() (new): reads back the module's current PAN ID. SnipsController.ino uses this once at boot to derive which saved droid (by name) it corresponds to, for the "Droid Name" complication — no separate persistence needed for that either, it's just derived fresh each boot from the list + the radio's own state.
  • Factory Reset now actually disconnects the radio: clears the PAN ID to a neutral all-zero value and forces an immediate leave (deliberately not rejoining anything, unlike a normal Switch Droid) — otherwise the controller would stay silently joined to the old droid's network post-reset, which would be confusing. Everything else about the radio (SL, etc.) is untouched.

This went through two iterations on this branch: the first commit added a full NVS-backed "current selection" persistence layer and a boot-time reapply step, which turned out to be unnecessary once it was confirmed that WR alone makes the module remember its PAN ID on its own — the second commit simplifies to the design above.

Test plan

  • pio test -e native — all 189 test cases pass
  • gcovr --exclude 'src/SnipsController\.ino' --exclude 'src/oled\.cpp' --exclude 'src/rgb_led\.cpp' --exclude 'src/calibration_store\.cpp' --exclude 'src/droid_persistence\.cpp' --exclude 'src/xbee_spi\.cpp' --exclude 'src/xbee_control\.cpp' --exclude 'src/complication_persistence\.cpp' --fail-under-line 90 — 98.6% line coverage
  • pio run -e esp32s3 — builds successfully
  • Flash to real hardware once boards arrive: switch to a droid, power-cycle, confirm the controller reconnects and the Droid Name complication shows the right name; run Factory Reset and confirm the radio actually leaves the network (not just that the on-screen list clears)

🤖 Generated with Claude Code

thePunderWoman and others added 2 commits September 7, 2026 22:08
Switching droids previously only updated the radio's in-RAM ID setting
and an in-memory display name — neither survived a power cycle, so the
controller wouldn't reliably reconnect to the last-selected droid after
a reboot. The droid *list* was already persisted; which one is
currently active was not.

- droid_persistence.h/.cpp gains saveCurrentSelection()/
  loadCurrentSelection()/clearCurrentSelection(), in their own NVS
  namespace separate from the list — DroidPersistence::save() calls
  prefs.clear() on the list's namespace every time it changes, which
  would otherwise wipe the current selection out from under it.
- XbeeControl::setPanId() now also sends "WR" after "ID", committing
  the PAN ID to the module's own flash — a backstop on the radio side,
  though SnipsController.ino reapplying it at boot is what actually
  guarantees reconnection regardless of what the module's flash holds.
- MenuController: setCurrentDroidName() is now public (SnipsController.ino
  calls it once at boot to seed the display from a restored selection)
  and a successful Switch Droid action now also reports through the new
  consumeCurrentSelectionChanged(), separate from
  consumeDroidStoreChanged() (list membership vs. current selection are
  independent — a selected droid stays "current" even if later removed
  from the list). Factory Reset now also resets the in-memory display
  name.
- SnipsController.ino: reapplies a saved selection at boot (set PAN +
  rejoin), persists a new one whenever a switch succeeds, and clears the
  persisted selection on Factory Reset.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Revises the previous commit on this branch after clarifying two points
about how XBee modules actually behave:

- Once XbeeControl::setPanId() commits a PAN ID via "WR", the module
  remembers it in its own flash across power cycles on its own — it
  never changes until setPanId() is called again, and never needs to be
  re-applied at boot. This makes the DroidPersistence current-selection
  NVS entries (and the boot-time reapply/rejoin logic) from the previous
  commit unnecessary — removed entirely.
- SnipsController.ino now derives the "Droid Name" complication at boot
  by querying the module's current PAN ID (XbeeControl::queryPanId(),
  new) and matching it against the already-loaded droid list, rather
  than needing its own separate persisted copy of "current selection."

Separately: Factory Reset should actually disconnect the radio from
whatever droid it was last on, not just forget it locally — otherwise
the controller would stay silently joined post-reset, which would be
confusing. It now clears the PAN ID to a neutral all-zero value and
forces an immediate leave, deliberately without rejoining anything
(unlike a normal Switch Droid) — it should sit disconnected until the
user explicitly picks a new droid. Nothing else about the radio (SL,
etc.) is touched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@thePunderWoman
thePunderWoman merged commit 09293ec into main Sep 8, 2026
2 checks passed
@thePunderWoman
thePunderWoman deleted the fix/persist-current-droid-selection branch September 8, 2026 03:23
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.

1 participant