Skip to content

Allow external battery packs to drive battery percentage on RAK boards - #3338

Draft
MFornander wants to merge 1 commit into
meshcore-dev:devfrom
MFornander:dev-mf/rak13302-battery-telemetry-ca3ae3
Draft

Allow external battery packs to drive battery percentage on RAK boards#3338
MFornander wants to merge 1 commit into
meshcore-dev:devfrom
MFornander:dev-mf/rak13302-battery-telemetry-ca3ae3

Conversation

@MFornander

@MFornander MFornander commented Sep 1, 2026

Copy link
Copy Markdown

Summary

Makes the RAK4631/RAK3401 battery sense overridable from build flags, so nodes powered by an external pack can report the pack's real charge instead of the (meaningless) on-board charger float voltage.

The motivating setup: a Voltaic V25/V50/V75 pack reports its charge level on the USB-C SBU pins as 1/2 of its internal cell voltage (Voltaic write-up). Wire that SBU pin to the RAK19007 J11 header pin 1 (AIN1 = P0.31), build with -D PIN_VBAT_READ=31 -D ADC_MULTIPLIER=7200, and the node reports the pack's true cell voltage (~3.2V empty to ~4.2V full) through every existing path (stats-core, telemetry LPP voltage, companion app).

Incorporates and supersedes #3328 — the variants/rak4631 hunks here are byte-identical to that PR. Credit to @andyshinn for the override mechanism; this PR extends it to the RAK3401 (1W Booster Kit) variant, where it needs one extra fix to work at all (see below), and adds a power-management escape hatch plus a documented example env.

What changed

  • variants/rak4631/RAK4631Board.h, variants/rak3401/RAK3401Board.hPIN_VBAT_READ / ADC_MULTIPLIER wrapped in #ifndef guards (same pattern as the rak3112 / heltec_v3 variants). Defaults unchanged. ADC_MULTIPLIER semantics: reported millivolts at ADC full scale (3.6V), so 3600 reports the pin voltage as-is and 7200 doubles it for half-scale sources like the SBU pin.
  • variants/rak4631/variant.h, variants/rak3401/variant.hPWRMGT_VOLTAGE_BOOTLOCK, PWRMGT_LPCOMP_AIN, PWRMGT_LPCOMP_REFSEL wrapped in #ifndef guards (the bootlock guard makes the already-documented "set to 0 to disable" actually settable per-env), plus a comment block decoding the LPCOMP REFSEL register values (0-6 = n/8 VDD, 7 = AREF, 8-15 = odd sixteenths).
  • variants/rak3401/variant.h — removed the unused Meshtastic-leftover battery block (BATTERY_PIN, BATTERY_SENSE_RESOLUTION*, AREF_VOLTAGE, VBAT_AR_INTERNAL, ADC_MULTIPLIER 1.73). This is required, not cosmetic — see the safety analysis below.
  • variants/rak3401/platformio.ini — new documented RAK_3401_repeater_voltaic example env.

Why deleting the rak3401 variant.h battery block is safe (and necessary)

  • Necessary: variant.h is included via Arduino.h before the board header in every translation unit, and its unconditional #define ADC_MULTIPLIER 1.73 therefore wins against both the new #ifndef default (stock battery reads would become raw × 1.73 / 4096 ≈ 2mV) and any -D ADC_MULTIPLIER=... build flag (an in-file #define overrides a command-line define; the redefinition warning is hidden by the project-wide -w). Pre-PR the same shadowing existed harmlessly in the other direction — the board header's unconditional define silently replaced 1.73.
  • The repo copy is the one compiled: a -H include trace on the real build command shows exactly one variant.h opened — variants/rak3401/variant.h (the pinned meshcore-dev Adafruit_nRF52 framework fork contains no RAK variant to shadow it).
  • Zero references in the whole compile closure: grepping all five symbol families across the framework fork's cores/ + libraries/, every library in .pio/libdeps/, and the repo's src/ + examples/ finds nothing. The only occurrences anywhere are other variants' own copies (t1000-e, xiao, ikoka, heltec) in their own directories, never in a rak3401 build's include path.
  • Provenance: the block arrived wholesale in the variant's initial commit (copied from Meshtastic's rak4631 variant, whose arch code consumes those names — MeshCore has no such consumer) and was never touched since. MeshCore's own rak4631/variant.h never had it; removing it restores parity.
  • Binary proof: stock RAK_3401_repeater firmware built before and after this PR differs by exactly 3 bytes — RTClib's embedded __DATE__ __TIME__ compile timestamp, which differs between any two builds of identical source. Section sizes and the full symbol table are identical.

The example env

RAK_3401_repeater_voltaic extends RAK_3401_repeater and keeps nRF52 power management enabled, rerouted to the SBU input: below 3.4V pack cell voltage the node enters SYSTEMOFF instead of boot-flapping on a drained pack (weak morning sun → boot → 1W TX sags the pack → brownout → repeat), and LPCOMP wakes it once the pack recharges past ~3.7V cell (9/16 VDD at the pin; ~half charge, not full — the only usable REFSEL step between 3.3V and 4.1V given the SBU's ÷2 scaling). configureVoltageWake also arms USB-detect, so plugging in wakes it for maintenance. Note the boot-voltage check only engages when isExternalPowered() is false, i.e. when the node is not powered via USB VBUS (true for e.g. the RAK13302 external-5V input).

Testing

  • RAK_3401_repeater, RAK_3401_repeater_voltaic, and RAK_4631_repeater all build clean; pio run -t envdump confirms every override lands in CPPDEFINES.
  • Stock-env binary identity verified as described above (only the RTClib timestamp bytes differ).
  • Hardware validation on the motivating setup (RAK19007 + RAK3401 + RAK13302 1W, Voltaic V25 on the SBU line) is in progress; the readings path is the existing getBattMilliVolts() code with different constants.

Wrap PIN_VBAT_READ / ADC_MULTIPLIER and the PWRMGT_* defines in #ifndef
guards so build flags can redirect battery telemetry to another analog
pin, e.g. a Voltaic V25/V50/V75 pack's USB-C SBU output (1/2 cell
voltage) wired to the base board J11 AIN1 pin.

Remove the unused Meshtastic-leftover battery defines from the rak3401
variant.h: nothing in the MeshCore, framework, or library compile
closure references them, and the unconditional ADC_MULTIPLIER there
would defeat both the new #ifndef default and any -D build flag
(variant.h is included via Arduino.h before the board headers).

Add a documented RAK_3401_repeater_voltaic example env that reports the
pack's true cell voltage and keeps nRF52 power management active with
LPCOMP wake rerouted to the SBU pin: sleep below 3.4V cell, wake once
the pack recharges past ~3.7V.

The rak4631 hunks are byte-identical to PR meshcore-dev#3328 by @andyshinn, which
this change incorporates and extends.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@MFornander
MFornander force-pushed the dev-mf/rak13302-battery-telemetry-ca3ae3 branch from 34d0618 to 846093e Compare September 1, 2026 20:53
@MFornander
MFornander changed the base branch from main to dev September 1, 2026 20:53
@MFornander

Copy link
Copy Markdown
Author

Doing one more manual bench test before submitting PR:

  1. Meter first: SBU at the breakout should read 1.6–2.1V (a mid-charge pack ≈ 1.85–1.95V). If it reads 0, the cable path isn't carrying SBU (needs full-featured USB-C) or a pull-down is loading it.
  2. Flash: double-tap reset → copy .pio/build/RAK_3401_repeater_voltaic/firmware.uf2 onto the boot drive.
  3. Verify: stats-core on the USB console → battery_mv should be ~2× the metered SBU. Consistently a few percent low usually means SBU source impedance sagging under the ADC — the 100nF cap on J11 pin 1 fixes it, or bump ADC_MULTIPLIER (7300-ish) to calibrate it out.
  4. Confirm in the app: repeater status/telemetry should show the same voltage, and it should track the pack across a charge cycle rather than sitting pinned at ~4.2V like the old charger-float reading.
  5. Optional sleep-path test (safe version): temporarily build with PWRMGT_VOLTAGE_BOOTLOCK=4300 and PWRMGT_LPCOMP_REFSEL=14 — it should boot-protect into SYSTEMOFF immediately and wake only when you plug USB in. Don't test with just the bootlock raised: with a charged pack the LPCOMP threshold would already be exceeded and it'll wake-loop until reflashed.

@MFornander MFornander changed the title Allow overriding battery sense on RAK4631/RAK3401 for external packs Allow external battery packs to drive battery percentage on RAK boards Sep 1, 2026
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