diff --git a/bin/setup-python-for-esp-debug.sh b/bin/setup-python-for-esp-debug.sh
new file mode 100644
index 0000000000..edba43e72b
--- /dev/null
+++ b/bin/setup-python-for-esp-debug.sh
@@ -0,0 +1,12 @@
+# shellcheck shell=bash
+# (this minor script is actually shell agnostic, and is intended to be sourced rather than run in a subshell)
+
+# This is a little script you can source if you want to make ESP debugging work on a modern (24.04) ubuntu machine
+# It assumes you have built and installed python 2.7 from source with:
+# ./configure --enable-optimizations --enable-shared --enable-unicode=ucs4
+# sudo make clean
+# make
+# sudo make altinstall
+
+export LD_LIBRARY_PATH=$HOME/packages/python-2.7.18/
+export PYTHON_HOME=/usr/local/lib/python2.7/
diff --git a/boards/wio-sdk-wm1110.json b/boards/wio-sdk-wm1110.json
index 882f4443ec..18c87adde9 100644
--- a/boards/wio-sdk-wm1110.json
+++ b/boards/wio-sdk-wm1110.json
@@ -27,7 +27,7 @@
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd"
},
- "frameworks": ["arduino"],
+ "frameworks": ["arduino", "freertos"],
"name": "Seeed WIO WM1110",
"upload": {
"maximum_ram_size": 248832,
diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp
index a7bc18f1a3..72e00810bb 100644
--- a/src/PowerFSM.cpp
+++ b/src/PowerFSM.cpp
@@ -11,6 +11,7 @@
#include "Default.h"
#include "MeshService.h"
#include "NodeDB.h"
+#include "PowerMon.h"
#include "configuration.h"
#include "graphics/Screen.h"
#include "main.h"
@@ -49,6 +50,7 @@ static bool isPowered()
static void sdsEnter()
{
LOG_DEBUG("Enter state: SDS\n");
+ powerMon->setState(meshtastic_PowerMon_State_CPU_DeepSleep);
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false);
}
@@ -68,6 +70,7 @@ static uint32_t secsSlept;
static void lsEnter()
{
LOG_INFO("lsEnter begin, ls_secs=%u\n", config.power.ls_secs);
+ powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(false);
secsSlept = 0; // How long have we been sleeping this time
@@ -87,8 +90,10 @@ static void lsIdle()
// Briefly come out of sleep long enough to blink the led once every few seconds
uint32_t sleepTime = SLEEP_TIME;
+ powerMon->setState(meshtastic_PowerMon_State_CPU_LightSleep);
setLed(false); // Never leave led on while in light sleep
esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL);
+ powerMon->clearState(meshtastic_PowerMon_State_CPU_LightSleep);
switch (wakeCause2) {
case ESP_SLEEP_WAKEUP_TIMER:
@@ -144,6 +149,7 @@ static void lsExit()
static void nbEnter()
{
LOG_DEBUG("Enter state: NB\n");
+ powerMon->clearState(meshtastic_PowerMon_State_BT_On);
screen->setOn(false);
#ifdef ARCH_ESP32
// Only ESP32 should turn off bluetooth
@@ -155,6 +161,8 @@ static void nbEnter()
static void darkEnter()
{
+ powerMon->clearState(meshtastic_PowerMon_State_BT_On);
+ powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
setBluetoothEnable(true);
screen->setOn(false);
}
@@ -162,6 +170,8 @@ static void darkEnter()
static void serialEnter()
{
LOG_DEBUG("Enter state: SERIAL\n");
+ powerMon->clearState(meshtastic_PowerMon_State_BT_On);
+ powerMon->setState(meshtastic_PowerMon_State_Screen_On);
setBluetoothEnable(false);
screen->setOn(true);
screen->print("Serial connected\n");
@@ -170,6 +180,7 @@ static void serialEnter()
static void serialExit()
{
// Turn bluetooth back on when we leave serial stream API
+ powerMon->setState(meshtastic_PowerMon_State_BT_On);
setBluetoothEnable(true);
screen->print("Serial disconnected\n");
}
@@ -182,6 +193,8 @@ static void powerEnter()
LOG_INFO("Loss of power in Powered\n");
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
} else {
+ powerMon->setState(meshtastic_PowerMon_State_BT_On);
+ powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true);
setBluetoothEnable(true);
// within enter() the function getState() returns the state we came from
@@ -205,6 +218,8 @@ static void powerIdle()
static void powerExit()
{
+ powerMon->setState(meshtastic_PowerMon_State_BT_On);
+ powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true);
setBluetoothEnable(true);
@@ -216,6 +231,8 @@ static void powerExit()
static void onEnter()
{
LOG_DEBUG("Enter state: ON\n");
+ powerMon->setState(meshtastic_PowerMon_State_BT_On);
+ powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true);
setBluetoothEnable(true);
}
diff --git a/src/PowerMon.cpp b/src/PowerMon.cpp
new file mode 100644
index 0000000000..3d28715e0c
--- /dev/null
+++ b/src/PowerMon.cpp
@@ -0,0 +1,45 @@
+#include "PowerMon.h"
+#include "NodeDB.h"
+
+// Use the 'live' config flag to figure out if we should be showing this message
+static bool is_power_enabled(uint64_t m)
+{
+ return (m & config.power.powermon_enables) ? true : false;
+}
+
+void PowerMon::setState(_meshtastic_PowerMon_State state, const char *reason)
+{
+#ifdef USE_POWERMON
+ auto oldstates = states;
+ states |= state;
+ if (oldstates != states && is_power_enabled(state)) {
+ emitLog(reason);
+ }
+#endif
+}
+
+void PowerMon::clearState(_meshtastic_PowerMon_State state, const char *reason)
+{
+#ifdef USE_POWERMON
+ auto oldstates = states;
+ states &= ~state;
+ if (oldstates != states && is_power_enabled(state)) {
+ emitLog(reason);
+ }
+#endif
+}
+
+void PowerMon::emitLog(const char *reason)
+{
+#ifdef USE_POWERMON
+ // The nrf52 printf doesn't understand 64 bit ints, so if we ever reach that point this function will need to change.
+ LOG_INFO("S:PM:0x%08lx,%s\n", (uint32_t)states, reason);
+#endif
+}
+
+PowerMon *powerMon;
+
+void powerMonInit()
+{
+ powerMon = new PowerMon();
+}
\ No newline at end of file
diff --git a/src/PowerMon.h b/src/PowerMon.h
new file mode 100644
index 0000000000..e9f5dbd59c
--- /dev/null
+++ b/src/PowerMon.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "configuration.h"
+
+#include "meshtastic/powermon.pb.h"
+
+#ifndef MESHTASTIC_EXCLUDE_POWERMON
+#define USE_POWERMON // FIXME turn this only for certain builds
+#endif
+
+/**
+ * The singleton class for monitoring power consumption of device
+ * subsystems/modes.
+ *
+ * For more information see the PowerMon docs.
+ */
+class PowerMon
+{
+ uint64_t states = 0UL;
+
+ public:
+ PowerMon() {}
+
+ // Mark entry/exit of a power consuming state
+ void setState(_meshtastic_PowerMon_State state, const char *reason = "");
+ void clearState(_meshtastic_PowerMon_State state, const char *reason = "");
+
+ private:
+ // Emit the coded log message
+ void emitLog(const char *reason);
+};
+
+extern PowerMon *powerMon;
+
+void powerMonInit();
\ No newline at end of file
diff --git a/src/configuration.h b/src/configuration.h
index 854d3dadfe..aad4ac4572 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -258,6 +258,7 @@ along with this program. If not, see .
#define MESHTASTIC_EXCLUDE_GPS 1
#define MESHTASTIC_EXCLUDE_SCREEN 1
#define MESHTASTIC_EXCLUDE_MQTT 1
+#define MESHTASTIC_EXCLUDE_POWERMON 1
#endif
// Turn off all optional modules
@@ -278,6 +279,7 @@ along with this program. If not, see .
#define MESHTASTIC_EXCLUDE_WAYPOINT 1
#define MESHTASTIC_EXCLUDE_INPUTBROKER 1
#define MESHTASTIC_EXCLUDE_SERIAL 1
+#define MESHTASTIC_EXCLUDE_POWERSTRESS 1
#endif
// // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled)
diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp
index 5efe962517..ec7d725b83 100644
--- a/src/gps/GPS.cpp
+++ b/src/gps/GPS.cpp
@@ -3,6 +3,7 @@
#include "Default.h"
#include "GPS.h"
#include "NodeDB.h"
+#include "PowerMon.h"
#include "RTC.h"
#include "main.h" // pmu_found
@@ -815,9 +816,12 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
return;
if (on) {
+ powerMon->setState(meshtastic_PowerMon_State_GPS_Active);
clearBuffer(); // drop any old data waiting in the buffer before re-enabling
if (en_gpio)
digitalWrite(en_gpio, on ? GPS_EN_ACTIVE : !GPS_EN_ACTIVE); // turn this on if defined, every time
+ } else {
+ powerMon->clearState(meshtastic_PowerMon_State_GPS_Active);
}
isInPowersave = !on;
if (!standbyOnly && en_gpio != 0 &&
diff --git a/src/main.cpp b/src/main.cpp
index 196eae525b..1e0d998e15 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6,6 +6,7 @@
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
+#include "PowerMon.h"
#include "ReliableRouter.h"
#include "airtime.h"
#include "buzz.h"
@@ -214,6 +215,14 @@ __attribute__((weak, noinline)) bool loopCanSleep()
return true;
}
+/**
+ * Print info as a structured log message (for automated log processing)
+ */
+void printInfo()
+{
+ LOG_INFO("S:B:%d,%s\n", HW_VENDOR, optstr(APP_VERSION));
+}
+
void setup()
{
concurrency::hasBeenSetup = true;
@@ -234,6 +243,7 @@ void setup()
#ifdef DEBUG_PORT
consoleInit(); // Set serial baud rate and init our mesh console
#endif
+ powerMonInit();
serialSinceMsec = millis();
@@ -553,7 +563,7 @@ void setup()
#endif
// Hello
- LOG_INFO("Meshtastic hwvendor=%d, swver=%s\n", HW_VENDOR, optstr(APP_VERSION));
+ printInfo();
#ifdef ARCH_ESP32
esp32Setup();
diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp
index bffca0c448..fc059ec16d 100644
--- a/src/mesh/LR11x0Interface.cpp
+++ b/src/mesh/LR11x0Interface.cpp
@@ -184,6 +184,7 @@ template void LR11x0Interface::setStandby()
activeReceiveStart = 0;
disableInterrupt();
completeSending(); // If we were sending, not anymore
+ RadioLibInterface::setStandby();
}
/**
@@ -223,7 +224,7 @@ template void LR11x0Interface::startReceive()
0); // only RX_DONE IRQ is needed, we'll check for PREAMBLE_DETECTED and HEADER_VALID in isActivelyReceiving
assert(err == RADIOLIB_ERR_NONE);
- isReceiving = true;
+ RadioLibInterface::startReceive();
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0);
diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp
index c5356ad3bd..bd1ebdb0e6 100644
--- a/src/mesh/RF95Interface.cpp
+++ b/src/mesh/RF95Interface.cpp
@@ -25,7 +25,8 @@ typedef struct {
} DACDB;
// Interpolation function
-DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val2) {
+DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val2)
+{
DACDB result;
double fraction = (double)(dbm - dbm1) / (dbm2 - dbm1);
result.dac = (uint8_t)(val1.dac + fraction * (val2.dac - val1.dac));
@@ -34,16 +35,17 @@ DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val
}
// Function to find the correct DAC and DB values based on dBm using interpolation
-DACDB getDACandDB(uint8_t dbm) {
+DACDB getDACandDB(uint8_t dbm)
+{
// Predefined values
static const struct {
uint8_t dbm;
DACDB values;
} dbmToDACDB[] = {
- {20, {168, 2}}, // 100mW
- {24, {148, 6}}, // 250mW
- {27, {128, 9}}, // 500mW
- {30, {90, 12}} // 1000mW
+ {20, {168, 2}}, // 100mW
+ {24, {148, 6}}, // 250mW
+ {27, {128, 9}}, // 500mW
+ {30, {90, 12}} // 1000mW
};
const int numValues = sizeof(dbmToDACDB) / sizeof(dbmToDACDB[0]);
@@ -103,7 +105,7 @@ bool RF95Interface::init()
if (power > RF95_MAX_POWER) // This chip has lower power limits than some
power = RF95_MAX_POWER;
-
+
limitPower();
iface = lora = new RadioLibRF95(&module);
@@ -116,13 +118,13 @@ bool RF95Interface::init()
// enable PA
#ifdef RF95_PA_EN
#if defined(RF95_PA_DAC_EN)
- #ifdef RADIOMASTER_900_BANDIT_NANO
- // Use calculated DAC value
- dacWrite(RF95_PA_EN, powerDAC);
- #else
- // Use Value set in /*/variant.h
- dacWrite(RF95_PA_EN, RF95_PA_LEVEL);
- #endif
+#ifdef RADIOMASTER_900_BANDIT_NANO
+ // Use calculated DAC value
+ dacWrite(RF95_PA_EN, powerDAC);
+#else
+ // Use Value set in /*/variant.h
+ dacWrite(RF95_PA_EN, RF95_PA_LEVEL);
+#endif
#endif
#endif
@@ -254,6 +256,7 @@ void RF95Interface::setStandby()
isReceiving = false; // If we were receiving, not any more
disableInterrupt();
completeSending(); // If we were sending, not anymore
+ RadioLibInterface::setStandby();
}
/** We override to turn on transmitter power as needed.
diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp
index a4ceac9f12..f299ebff2c 100644
--- a/src/mesh/RadioLibInterface.cpp
+++ b/src/mesh/RadioLibInterface.cpp
@@ -1,6 +1,7 @@
#include "RadioLibInterface.h"
#include "MeshTypes.h"
#include "NodeDB.h"
+#include "PowerMon.h"
#include "SPILock.h"
#include "configuration.h"
#include "error.h"
@@ -317,6 +318,7 @@ void RadioLibInterface::handleTransmitInterrupt()
// ignore the transmit interrupt
if (sendingPacket)
completeSending();
+ powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // But our transmitter is deffinitely off now
}
void RadioLibInterface::completeSending()
@@ -412,6 +414,24 @@ void RadioLibInterface::handleReceiveInterrupt()
}
}
+void RadioLibInterface::startReceive()
+{
+ isReceiving = true;
+ powerMon->setState(meshtastic_PowerMon_State_Lora_RXOn);
+}
+
+void RadioLibInterface::configHardwareForSend()
+{
+ powerMon->setState(meshtastic_PowerMon_State_Lora_TXOn);
+}
+
+void RadioLibInterface::setStandby()
+{
+ // neither sending nor receiving
+ powerMon->clearState(meshtastic_PowerMon_State_Lora_RXOn);
+ powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn);
+}
+
/** start an immediate transmit */
void RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
{
@@ -431,6 +451,7 @@ void RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
// This send failed, but make sure to 'complete' it properly
completeSending();
+ powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // Transmitter off now
startReceive(); // Restart receive mode (because startTransmit failed to put us in xmit mode)
}
diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h
index 2c841a19ef..dd01d2037f 100644
--- a/src/mesh/RadioLibInterface.h
+++ b/src/mesh/RadioLibInterface.h
@@ -126,8 +126,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
* Start waiting to receive a message
*
* External functions can call this method to wake the device from sleep.
+ * Subclasses must override and call this base method
*/
- virtual void startReceive() = 0;
+ virtual void startReceive();
/** can we detect a LoRa preamble on the current channel? */
virtual bool isChannelActive() = 0;
@@ -166,8 +167,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
meshtastic_QueueStatus getQueueStatus();
protected:
- /** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */
- virtual void configHardwareForSend() {}
+ /** Do any hardware setup needed on entry into send configuration for the radio.
+ * Subclasses can customize, but must also call this base method */
+ virtual void configHardwareForSend();
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
virtual bool canSendImmediately();
@@ -186,5 +188,8 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) = 0;
- virtual void setStandby() = 0;
+ /**
+ * Subclasses must override, implement and then call into this base class implementation
+ */
+ virtual void setStandby();
};
\ No newline at end of file
diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp
index afaa13b7f0..b564ba287e 100644
--- a/src/mesh/SX126xInterface.cpp
+++ b/src/mesh/SX126xInterface.cpp
@@ -231,6 +231,7 @@ template void SX126xInterface::setStandby()
activeReceiveStart = 0;
disableInterrupt();
completeSending(); // If we were sending, not anymore
+ RadioLibInterface::setStandby();
}
/**
@@ -270,7 +271,7 @@ template void SX126xInterface::startReceive()
LOG_ERROR("Radiolib error %d when attempting SX126X startReceiveDutyCycleAuto!\n", err);
assert(err == RADIOLIB_ERR_NONE);
- isReceiving = true;
+ RadioLibInterface::startReceive();
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0);
diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp
index 9e4fbfa772..fdb2b9a395 100644
--- a/src/mesh/SX128xInterface.cpp
+++ b/src/mesh/SX128xInterface.cpp
@@ -190,6 +190,7 @@ template void SX128xInterface::setStandby()
activeReceiveStart = 0;
disableInterrupt();
completeSending(); // If we were sending, not anymore
+ RadioLibInterface::setStandby();
}
/**
@@ -263,7 +264,7 @@ template void SX128xInterface::startReceive()
LOG_ERROR("Radiolib error %d when attempting SX128X startReceive!\n", err);
assert(err == RADIOLIB_ERR_NONE);
- isReceiving = true;
+ RadioLibInterface::startReceive();
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0);
diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp
index ba1f5c11ea..300afc2460 100644
--- a/src/modules/Modules.cpp
+++ b/src/modules/Modules.cpp
@@ -27,6 +27,9 @@
#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE
#include "modules/RemoteHardwareModule.h"
#endif
+#if !MESHTASTIC_EXCLUDE_POWERSTRESS
+#include "modules/PowerStressModule.h"
+#endif
#include "modules/RoutingModule.h"
#include "modules/TextMessageModule.h"
#if !MESHTASTIC_EXCLUDE_TRACEROUTE
@@ -115,6 +118,9 @@ void setupModules()
#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE
new RemoteHardwareModule();
+#endif
+#if !MESHTASTIC_EXCLUDE_POWERSTRESS
+ new PowerStressModule();
#endif
// Example: Put your module here
// new ReplyModule();
diff --git a/src/modules/PowerStressModule.cpp b/src/modules/PowerStressModule.cpp
new file mode 100644
index 0000000000..c86017ae28
--- /dev/null
+++ b/src/modules/PowerStressModule.cpp
@@ -0,0 +1,77 @@
+#include "PowerStressModule.h"
+#include "MeshService.h"
+#include "NodeDB.h"
+#include "RTC.h"
+#include "Router.h"
+#include "configuration.h"
+#include "main.h"
+
+extern void printInfo();
+
+PowerStressModule::PowerStressModule()
+ : ProtobufModule("powerstress", meshtastic_PortNum_POWERSTRESS_APP, &meshtastic_PowerStressMessage_msg),
+ concurrency::OSThread("PowerStressModule")
+{
+}
+
+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\n", 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\n");
+ break;
+
+ case meshtastic_PowerStressMessage_Opcode_PRINT_INFO:
+ printInfo();
+ break;
+
+ default:
+ if (currentMessage.cmd != meshtastic_PowerStressMessage_Opcode_UNSET)
+ LOG_ERROR("PowerStress operation %d already in progress! Can't start new command\n", 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;
+ } else {
+ 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
+
+ switch (p.cmd) {
+ case meshtastic_PowerStressMessage_Opcode_UNSET: // No need to start a new command
+ break;
+ case meshtastic_PowerStressMessage_Opcode_LED_ON:
+ break;
+ default:
+ LOG_ERROR("PowerStress operation %d not yet implemented!\n", p.cmd);
+ sleep_msec = 0; // Don't do whatever sleep was requested...
+ break;
+ }
+ }
+ return sleep_msec;
+}
\ No newline at end of file
diff --git a/src/modules/PowerStressModule.h b/src/modules/PowerStressModule.h
new file mode 100644
index 0000000000..2d449f690c
--- /dev/null
+++ b/src/modules/PowerStressModule.h
@@ -0,0 +1,38 @@
+#pragma once
+#include "ProtobufModule.h"
+#include "concurrency/OSThread.h"
+#include "mesh/generated/meshtastic/powermon.pb.h"
+
+/**
+ * A module that provides easy low-level remote access to device hardware.
+ */
+class PowerStressModule : public ProtobufModule, private concurrency::OSThread
+{
+ meshtastic_PowerStressMessage currentMessage = meshtastic_PowerStressMessage_init_default;
+ bool isRunningCommand = false;
+
+ public:
+ /** Constructor
+ * name is for debugging output
+ */
+ PowerStressModule();
+
+ protected:
+ /** Called to handle a particular incoming message
+
+ @return true if you've guaranteed you've handled this message and no other handlers should be considered for it
+ */
+ virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_PowerStressMessage *p) override;
+
+ /**
+ * Periodically read the gpios we have been asked to WATCH, if they have changed,
+ * broadcast a message with the change information.
+ *
+ * The method that will be called each time our thread gets a chance to run
+ *
+ * Returns desired period for next invocation (or RUN_SAME for no change)
+ */
+ virtual int32_t runOnce() override;
+};
+
+extern PowerStressModule powerStressModule;
\ No newline at end of file
diff --git a/src/sleep.cpp b/src/sleep.cpp
index 735ebcf6ad..e2c9549f3b 100644
--- a/src/sleep.cpp
+++ b/src/sleep.cpp
@@ -8,6 +8,7 @@
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
+#include "PowerMon.h"
#include "detect/LoRaRadioType.h"
#include "error.h"
#include "main.h"
@@ -85,6 +86,11 @@ void setCPUFast(bool on)
void setLed(bool ledOn)
{
+ if (ledOn)
+ powerMon->setState(meshtastic_PowerMon_State_LED_On);
+ else
+ powerMon->clearState(meshtastic_PowerMon_State_LED_On);
+
#ifdef LED_PIN
// toggle the led so we can get some rough sense of how often loop is pausing
digitalWrite(LED_PIN, ledOn ^ LED_INVERTED);