ThinkNode M6: fix GPS and status LED in the repeater build - #3287
Closed
andyhoobing wants to merge 3 commits into
Closed
ThinkNode M6: fix GPS and status LED in the repeater build#3287andyhoobing wants to merge 3 commits into
andyhoobing wants to merge 3 commits into
Conversation
The M6 is a sealed solar repeater with a soldered-on L76K GNSS and two LEDs, but neither was usable in the ThinkNode_M6_repeater build. GPS: the repeater's NodePrefs default gps_enabled=0, so sensors.begin() detected the L76K and then immediately powered it back down via _location->stop(). With no button or screen on a sealed enclosure there was no way to turn it on short of a USB console. Even once enabled, the repeater defaulted advert_loc_policy to ADVERT_LOC_PREFS, so adverts carried the fixed ADVERT_LAT/ADVERT_LON (0,0) and ignored the GNSS entirely. Both defaults are now build-time overridable (GPS_ENABLED_DEFAULT and ADVERT_LOC_POLICY_DEFAULT) and unchanged for every other board; the M6 repeater env opts into GPS-on and ADVERT_LOC_SHARE. It also sets ENV_SKIP_GPS_DETECT, because the 1s "is a GPS attached?" probe gates the 'gps' setting, and losing that race left GPS unreachable until reboot -- the M6 always has the module fitted. Everything stays runtime-adjustable via 'gps on'/'gps off' and 'gps advert prefs|share|none'. LEDs: only the blue LED was driven (LoRa TX activity). The red LED -- the enclosure's power LED -- was configured as an output and then never touched. It now indicates state: solid through boot, then a ~1% duty heartbeat, one blink per 5s while running and two once the GNSS has a fix, so a deployed node can be checked through the case without a laptop. onBootComplete() and powerOff() handle it too. The heartbeat is driven from a small EnvironmentSensorManager subclass, the same pattern thinknode_m1 uses, since its loop() already runs every iteration and holds the live GNSS state -- no core or shared example changes needed. Verified: ThinkNode_M6_repeater builds (flash 47.8%, RAM 13.4%) and RAK_4631_repeater still builds unchanged, confirming the shared simple_repeater defaults are untouched for other boards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H7b3E6KcBEGZ2zr2jZMrBL
A 40ms blink every 5s is easy to miss entirely at arm's length. The LEDs sit on the bottom face of the enclosure next to the USB-C port, read in daylight, so stretch each blink to 150ms (still ~3% duty) and add a three-flash boot signature on both LEDs at the end of setup() -- that is how you confirm a freshly flashed node came up, without a console. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H7b3E6KcBEGZ2zr2jZMrBL
simple_room_server has the identical defect the repeater had: it defaults gps_enabled=0 and advert_loc_policy=ADVERT_LOC_PREFS, so on a sealed M6 the L76K is detected at boot and then powered straight back down, and adverts carry the fixed 0,0 instead of the GNSS position. Same approach as the repeater: both defaults become build-time overridable (declared in MyMesh.h alongside this example's other ADVERT_* fallbacks, matching its own convention) and are unchanged for every other board. The M6 room_server env opts in, and also skips the 1s GPS probe since the module is always fitted. The LED work needed nothing here -- the heartbeat lives in the variant's sensor manager, and simple_room_server already calls board.onBootComplete() and sensors.loop(). Verified: ThinkNode_M6_room_server and RAK_4631_room_server both build, and envdump confirms the new flags resolve for the M6 while remaining absent for RAK. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H7b3E6KcBEGZ2zr2jZMrBL
This was referenced Aug 24, 2026
Author
|
Superseded — split into two self-contained PRs per the one-fix-per-PR guideline in CONTRIBUTING.md:
They share no files and are independent, so they can be reviewed or merged in any order. Verified the split reconstructs this branch's tree exactly (41 + 116 = 157 insertions, 4 + 3 = 7 deletions across the same 8 files), and each branch builds standalone against Closing this one to avoid duplicate review. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
On the Elecrow ThinkNode M6 (nRF52840 + SX1262 + L76K GNSS, sealed IP65 solar
enclosure) the
ThinkNode_M6_repeaterbuild shipped with an unusable GNSS andone of its two LEDs completely dead. Pin definitions were already correct; the
problems were behavioural.
GPS
simple_repeaterdefaultsgps_enabled = 0, sosensors.begin()detectedthe L76K and then immediately powered it back down via
_location->stop()(which drives
PIN_GPS_ENlow). The M6 is a sealed box with no button orscreen, so there was no way to switch it on short of a USB console.
advert_loc_policytoADVERT_LOC_PREFS, so adverts carried the fixedADVERT_LAT/ADVERT_LON(0,0) and ignored the GNSS entirely.
initBasicGPS()gates thegpssetting; losing that race leaves GPS unreachable until reboot. The M6 always
has the module fitted, so the probe is skipped for this board.
Rather than changing behaviour for everyone, the two
simple_repeaterdefaultsare now build-time overridable:
Every other board keeps its existing defaults; only the M6 repeater env opts
in. Both remain adjustable at runtime via
gps on/gps offandgps advert prefs|share|none.LEDs
Only the blue LED was driven (LoRa TX activity). The red LED — the enclosure's
power LED, on the bottom face next to the USB-C port — was configured as an
output in
initVariant()and then never touched again.It now indicates state: solid through boot, a three-flash signature at
onBootComplete(), then a ~3% duty heartbeat — one blink per 5s while running,two once the GNSS has a fix. That makes a deployed node diagnosable through the
case without a laptop.
powerOff()clears it too.The heartbeat is driven from a small
EnvironmentSensorManagersubclass — thesame pattern
thinknode_m1already uses — since itsloop()already runs everyiteration and holds the live GNSS state. No core or shared-example changes
were needed for the LED work.
Testing
ThinkNode_M6_repeaterbuilds againstdev(flash 44.5%, RAM 14.5%).RAK_4631_repeaterbuilds unchanged, confirming the sharedsimple_repeaterdefaults are untouched for other boards.
gps -> on, active, fix, 10 sats,gps advert -> share, and the settings survive a reboot.Note for maintainers
One behavioural caveat worth being aware of, which cost me some debugging: these
compiled defaults only apply to a fresh install. A UF2 update does not erase
InternalFS, so a device that has run MeshCore before keeps its saved prefs and
silently ignores the new defaults — it has to be reconfigured at runtime or
factory reset. That is pre-existing behaviour, not something this PR changes.
Opened as a draft: happy to split the shared
simple_repeaterchange out fromthe M6-only variant changes if you would prefer two PRs per the one-fix-per-PR
guideline.
Authored with assistance from Claude (see
Co-Authored-Bytrailers on thecommits).