Skip to content

Thinknode M6 Repeater Enable LEDs and Function Button#2605

Open
AtlavoxDev wants to merge 7 commits into
meshcore-dev:devfrom
AtlavoxDev:thinknode-m6-power-button
Open

Thinknode M6 Repeater Enable LEDs and Function Button#2605
AtlavoxDev wants to merge 7 commits into
meshcore-dev:devfrom
AtlavoxDev:thinknode-m6-power-button

Conversation

@AtlavoxDev
Copy link
Copy Markdown

@AtlavoxDev AtlavoxDev commented May 22, 2026

What this fixes

On the original M6 repeater build:

  • No visual LED feedback for power-on, power-off, or reset: the device gave the user no indication that a button press, shutdown command, or boot was recognized. Gave the impression of a dead device.
  • No way to turn off the device: poweroff didn't actually sleep the chip. The original code called a SoftDevice API (sd_power_system_off()) that requires SoftDevice to be running — but SoftDevice isn't enabled in the repeater build, so the call silently failed and the chip stayed awake. No button mapped to poweroff.
  • Function Button was disabled.: It was defined in variant.h but never read or armed as a wake source or poweroff.
  • USB cable triggered power on: Charging the device would trigger the radio to boot up

What this adds

  • Full LED choreography for every power event.
    • Boot: dual-LED full bright → 1 s gap → red solid + blue "loading-activity" flicker (To prevent user from attempting to press buttons during boot phase) → blue flash on boot complete.
    • Hold ~2 s: power off, with red blink ack at 0 s and 1 s + full-bright commit cue + dual-LED goodbye flash.
    • Shutdown via poweroff CLI: same shutdown cue.
  • Robust SYSTEMOFF. New enterDeepSleep() helper with SoftDevice/direct-register fallback. Arms Function Button as SENSE-LOW wake source.
  • Reset-cause-aware boot. RESETREAS captured in a priority-101 constructor (before Nordic's errata-136 workaround scrubs RESETPIN).
  • Resilient deployment. GPREGRET2 user-intent flag distinguishes deliberate shutdown from transient resets. Survives SYSTEMOFF, cleared by full power loss. Result: device stays off when you asked it to, but self-heals from brownouts and auto-boots when a dead battery recovers.

Implementation notes

  • All M6-only and #if defined(THINKNODE_M6)-guarded.
  • Other M6 envs (room server, companion radio, kiss modem) inherit the SYSTEMOFF fix but not the repeater UX.
  • analogWrite PWM-binds pins on the Adafruit nRF52 core — boot path use digitalWrite to keep blue compatible with the existing LoRa TX LED indicator.

Test plan

Verified on hardware:

  • Hold ~2 s → power off, function button wake works
  • poweroff over serial → real SYSTEMOFF
  • Reset button → clean reboot
  • USB plug-in to off device → stays off

Not field-verified:

  • Dead-battery + solar recharge auto-boot (logic-tested only)

Out of scope

  • Charging-status LED (needs EXT_PWR_DETECT / EXT_CHRG_DETECT and a wake-on-charge mode) — planned follow-up.

@AtlavoxDev
Copy link
Copy Markdown
Author

AtlavoxDev commented May 22, 2026

Hey MeshCore team, this is my first ever attempt to contribute to a repo. Let me know if there are any questions or if there's something I should do differently next time. I only targeted the Repeater firmware this time, but will explore the other M6 firmware too for feature parity. Note I used Claude to assist and manually reviewed all code. Novice developer, wanted to take a stab at adding these critical features myself and contributing to the project. Tested over the past couple days.

@AtlavoxDev AtlavoxDev marked this pull request as draft May 22, 2026 16:17
Remove device-specific code from the universal main.cpp file
The M6 follows a cleaner version of the existing SenseCAP Solar pattern: device-specific button logic lives in the variant (ThinkNodeM6Board::pollButton()), with a minimal dispatch call in main.cpp. The buttonStateChanged() methods on ThinkNodeM3Board, T1000eBoard, and PromicroBoard were defined but never connected to any caller — those boards could adopt the same pollButton() pattern in follow-up PRs if button UX is wanted.
@AtlavoxDev
Copy link
Copy Markdown
Author

I moved as much as a could out of main.cpp. Only a couple small blocks inside main.cpp are minimal dispatch calls (board.bootComplete() and board.pollButton()) to enable boot monitoring and button functionality.

@AtlavoxDev AtlavoxDev marked this pull request as ready for review May 22, 2026 19:08
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