Skip to content

Fix GPS on ThinkNode M3: power rail and reset polarity#3009

Closed
ebaschiera wants to merge 2 commits into
meshcore-dev:devfrom
ebaschiera:thinknode-m3-gps-fix
Closed

Fix GPS on ThinkNode M3: power rail and reset polarity#3009
ebaschiera wants to merge 2 commits into
meshcore-dev:devfrom
ebaschiera:thinknode-m3-gps-fix

Conversation

@ebaschiera

Copy link
Copy Markdown

Fix GPS on ThinkNode M3

The ThinkNode M3 ships with a serial CASIC AT6558R GNSS receiver (UART, 9600 baud, GPS+BeiDou), but GPS never worked on this variant because of two bugs. The receiver stayed completely silent, so gps_detected was always false and the app never exposed the location section.

Root cause

  1. Power rail never driven. PIN_GPS_POWER (pin 14) is a load switch feeding the module, active HIGH, but it was declared and never driven — so the module was permanently unpowered → 0 bytes on Serial1.
  2. Reset polarity inverted. The M3's reset line is asserted HIGH (idle LOW). The variant defined the unused macro GPS_RESET_ACTIVE LOW, while MicroNMEALocationProvider reads PIN_GPS_RESET_ACTIVE (default LOW) — so the module was held in reset forever.

Verified in isolation: with only fix (1) → 0 bytes on Serial1; with (1)+(2) → GPS detect: waited 199 ms, 7 bytes on Serial1 + GPS detected.

Changes

  • variants/thinknode_m3/variant.hPIN_GPS_RESET_ACTIVE HIGH (replacing the unread GPS_RESET_ACTIVE LOW).
  • variants/thinknode_m3/ThinkNodeM3Board.{h,cpp} — drive the pin-14 load switch via a RefCountedDigitalPin periph_power(PIN_GPS_POWER, HIGH), begin()-ed at board init (same pattern as the Heltec variants).
  • variants/thinknode_m3/target.cpp — pass &board.periph_power as the location provider's power pin so enabling/disabling GPS actually cuts the rail.
  • src/helpers/sensors/EnvironmentSensorManager.cpp — GPS detection now polls Serial1 up to GPS_DETECT_TIMEOUT_MS (default 10 s) instead of a single fixed delay(1000), so modules that take a moment to emit their first sentence are still detected (the M3 needs ~200 ms in practice). Also guards the gps setting toggle against repeated values: start_gps()/stop_gps() claim/release the ref-counted power pin, so a repeated gps=1 would push the claim count above 1 and a following single gps=0 would then fail to power the module down — now only a real state transition acts.

Testing

Flashed on hardware (ThinkNode_M3_companion_radio_ble). Boot log shows GPS detected and the AT6558R banner, followed by a continuous, well-formed NMEA stream ($GNGGA, $GNRMC, $GPGSV, …), up to 15 satellites in view (10 GPS + 5 BeiDou). Confirmed a real position fix outdoors during a full day of field use. Toggling location off in the app powers the receiver down via the load switch (cold restart on re-enable), so battery is preserved when GPS is disabled.

Refs #1864.

ebaschiera and others added 2 commits July 20, 2026 09:56
The M3 has a serial CASIC AT6558R GNSS receiver on Serial1, but it never
emitted a single NMEA sentence, so gps_detected stayed false and the app
never offered the location setting. Two independent problems kept it mute:

The module is fed through a load switch on PIN_GPS_POWER (pin 14, active
HIGH). The pin was declared in variant.h but nothing ever drove it, so the
receiver was permanently unpowered. Drive it through a RefCountedDigitalPin
owned by the board and hand it to MicroNMEALocationProvider, matching what
the heltec variants already do.

Its reset line is asserted HIGH. variant.h defined GPS_RESET_ACTIVE, a name
nothing reads; MicroNMEALocationProvider looks at PIN_GPS_RESET_ACTIVE and
fell back to its LOW default, so the state it drove to "release" reset was
in fact the asserted level and the module sat in reset forever. Rename the
macro and set it to HIGH.

Detection also replaces the fixed 1 s delay with a poll, since a receiver
may take several seconds to say anything after power-on. This board answers
in ~200 ms, but the fixed delay is a trap for slower modules.

Verified on hardware: the receiver now identifies itself over NMEA
(IC=AT6558R, SW=URANUS5) and tracks satellites with a valid UTC timestamp.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
setSettingValue("gps", ...) called start_gps()/stop_gps() unconditionally.
Those paths claim()/release() the GPS power rail through a ref-counted pin,
so a repeated "gps=1" pushed the claim count above 1 and a following single
"gps=0" then failed to actually power the module down. Only act on a real
state transition.

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

Copy link
Copy Markdown
Collaborator

thanks for your PR

#2887 addresses the same issue and I was just waiting some input to merge the PR ... Think I'll do it anyways now event if I don't have my m3 (it stayed in France since january :()

Don't know if I'd merge your PR, no offence there, but I would not let a Claude authored commit enter the repo

@fdlamotte fdlamotte closed this Jul 20, 2026
@ebaschiera

Copy link
Copy Markdown
Author

Sorry, I missed the prior open PR.
Anyway I am happy I gave you some kind of proof that it was on the right path.

For the Claude authored commits: you mean I should remove the co-author in future contributions? Or do you prefer no LLM contribution?

@fdlamotte

Copy link
Copy Markdown
Collaborator

About LLM assistance,

Not having co-authored PR is a minimum. We can't forbid you to get some help, but LLM have tendancy to rewrite lot of things, so try to restrict them to the problem you solve, and do a review work of everything they did.

Ideal would be, take some inspiration from their solution and write your own code ...

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