Files
firmware/src/modules/PowerStressModule.cpp
T
Andrew YongandBen Meadors b71c1adb26 stm32wl: add hardware RTC support (rak3172) (#10961)
* stm32wl: add hardware RTC support infrastructure

Wires the STM32WL chip's internal RTC (running off the LSE 32.768kHz
crystal) into meshtastic's existing time-of-day framework
(perhapsSetRTC()/readFromRTC()), following the same pattern already
used for I2C RTC chips (RV3028, PCF8563/85063, RX8130CE).

LSE is started and polled manually before ever calling into the
STM32RTC library, with our own bounded timeout - the library's own
internal LSE startup path has no bounded fallback and hangs forever
via Error_Handler() if the crystal never locks, so this is required
for a board with a missing/faulty crystal to boot normally rather
than hang.

Gated behind a new HAS_LSE variant flag (currently unset everywhere,
so this is inert until a variant opts in - see follow-up commit).

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* gps: qualify RTC.h includes to avoid case-insensitive filesystem collision with STM32RTC

The stm32duino STM32RTC library (added to lib_deps in a follow-up
commit) ships its own src/rtc.h. On case-insensitive filesystems
(the macOS default), an unqualified #include "RTC.h"/<RTC.h> from
any file outside src/gps/ resolves to the library's rtc.h instead of
src/gps/RTC.h, since PlatformIO's LDF puts lib_deps include paths
ahead of the project's own -Isrc/gps.

Qualify every include as gps/RTC.h so it can't collide with any
same-named header a future dependency might ship, regardless of
filesystem case sensitivity. Purely mechanical, no behavior change.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* stm32wl(rak3172): enable hardware RTC support

Opts rak3172 into the HAS_LSE infrastructure added previously: sets
STM32WL_LSE_DRIVE to a conservative default and pulls in the
STM32RTC library. rak3172 has ~63KB flash headroom going in;
build-verified at 76.7% flash usage after this change (up from a
73.8% baseline), well within budget.

wio-e5 is not opted in here despite sharing the same STM32WLE5 chip
- it's already at 96.8% flash usage today (GPS + I2C sensor support
compiled in, unlike rak3172), leaving too little headroom to safely
add STM32RTC without first trimming something else.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* stm32wl: add docstrings for LSE/RTC setup functions

Addresses CodeRabbit's docstring coverage check on PR #10961.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* stm32wl: address CodeRabbit nitpicks on PR #10961

- Brace the single-statement HAS_LSE branch in perhapsSetRTC() to
  match the sibling readFromRTC() branch's style.
- Quote the RTC.h include in PhoneAPI.cpp for consistency with every
  other qualified include site.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-07-16 19:00:45 -05:00

135 lines
5.0 KiB
C++

#include "PowerStressModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerMon.h"
#include "Router.h"
#include "configuration.h"
#include "gps/RTC.h"
#include "main.h"
#include "sleep.h"
#include "target_specific.h"
#include <Throttle.h>
extern void printInfo();
PowerStressModule::PowerStressModule()
: ProtobufModule("powerstress", meshtastic_PortNum_POWERSTRESS_APP, &meshtastic_PowerStressMessage_msg),
concurrency::OSThread("PowerStress")
{
}
bool PowerStressModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, meshtastic_PowerStressMessage *pptr)
{
// We only respond to messages if powermon debugging is already on
if (config.power.powermon_enables) {
auto p = *pptr;
LOG_INFO("Received PowerStress cmd=%d", p.cmd);
// Some commands we can handle immediately, anything else gets deferred to be handled by our thread
switch (p.cmd) {
case meshtastic_PowerStressMessage_Opcode_UNSET:
LOG_ERROR("PowerStress operation unset");
break;
case meshtastic_PowerStressMessage_Opcode_PRINT_INFO:
printInfo();
// Now that we know we are actually doing power stress testing, go ahead and turn on all enables (so the log is fully
// detailed)
powerMon->force_enabled = true;
break;
default:
if (currentMessage.cmd != meshtastic_PowerStressMessage_Opcode_UNSET)
LOG_ERROR("PowerStress operation %d already in progress! Can't start new command", currentMessage.cmd);
else
currentMessage = p; // copy for use by thread (the message provided to us will be getting freed)
break;
}
}
return true;
}
int32_t PowerStressModule::runOnce()
{
if (!config.power.powermon_enables) {
// Powermon not enabled - stop using CPU/stop this thread
return disable();
}
int32_t sleep_msec = 10; // when not active check for new messages every 10ms
auto &p = currentMessage;
if (isRunningCommand) {
// Done with the previous command - our sleep must have finished
p.cmd = meshtastic_PowerStressMessage_Opcode_UNSET;
p.num_seconds = 0;
isRunningCommand = false;
LOG_INFO("S:PS:%u", p.cmd);
} else {
if (p.cmd != meshtastic_PowerStressMessage_Opcode_UNSET) {
sleep_msec = (int32_t)(p.num_seconds * 1000);
isRunningCommand = !!sleep_msec; // if the command wants us to sleep, make sure to mark that we have something running
LOG_INFO(
"S:PS:%u",
p.cmd); // Emit a structured log saying we are starting a powerstress state (to make it easier to parse later)
switch (p.cmd) {
case meshtastic_PowerStressMessage_Opcode_LED_ON:
// FIXME - implement
// ledForceOn.set(true);
break;
case meshtastic_PowerStressMessage_Opcode_LED_OFF:
// FIXME - implement
// ledForceOn.set(false);
break;
case meshtastic_PowerStressMessage_Opcode_GPS_ON:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_GPS_OFF:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_LORA_OFF:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_LORA_RX:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_LORA_TX:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_SCREEN_OFF:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_SCREEN_ON:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_BT_OFF:
setBluetoothEnable(false);
break;
case meshtastic_PowerStressMessage_Opcode_BT_ON:
setBluetoothEnable(true);
break;
case meshtastic_PowerStressMessage_Opcode_CPU_DEEPSLEEP:
doDeepSleep(sleep_msec, true, true);
break;
case meshtastic_PowerStressMessage_Opcode_CPU_FULLON: {
uint32_t start_msec = millis();
while (Throttle::isWithinTimespanMs(start_msec, sleep_msec))
; // Don't let CPU idle at all
sleep_msec = 0; // we already slept
break;
}
case meshtastic_PowerStressMessage_Opcode_CPU_IDLE:
// FIXME - implement
break;
default:
LOG_ERROR("PowerStress operation %d not yet implemented!", p.cmd);
sleep_msec = 0; // Don't do whatever sleep was requested...
break;
}
}
}
return sleep_msec;
}