From 5d956d6bdcd7f012b8246d30eb98a047db54abe3 Mon Sep 17 00:00:00 2001 From: m1nl Date: Thu, 24 Apr 2025 00:36:08 +0200 Subject: [PATCH] prevent light sleep when modules are busy for more than one timer tick --- src/mesh/PhoneAPI.cpp | 4 ++ src/mesh/PhoneAPI.h | 7 +++- src/mesh/RadioLibInterface.cpp | 5 ++- src/mesh/Router.cpp | 10 +++++ src/mesh/Router.h | 10 +++++ src/mesh/StreamAPI.cpp | 4 +- src/mesh/http/ContentHandler.cpp | 40 +++++++++++++++++-- src/mesh/http/ContentHandler.h | 7 +++- .../Telemetry/EnvironmentTelemetry.cpp | 6 +++ src/modules/Telemetry/PowerTelemetry.cpp | 3 ++ 10 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 287de38fa..cceac8dfe 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -16,6 +16,7 @@ #include "SPILock.h" #include "TypeConversions.h" #include "main.h" +#include "sleep.h" #include "xmodem.h" #if FromRadio_size > MAX_TO_FROM_RADIO_SIZE @@ -35,10 +36,13 @@ PhoneAPI::PhoneAPI() { lastContactMsec = millis(); std::fill(std::begin(recentToRadioPacketIds), std::end(recentToRadioPacketIds), 0); + + preflightSleepObserver.observe(&preflightSleep); } PhoneAPI::~PhoneAPI() { + preflightSleepObserver.unobserve(&preflightSleep); close(); } diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 0d7772d17..cd475535e 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -172,4 +172,9 @@ class PhoneAPI /// If the mesh service tells us fromNum has changed, tell the phone virtual int onNotify(uint32_t newValue) override; -}; + + int preflightSleepCb(void *unused = NULL) { return available(); } + + CallbackObserver preflightSleepObserver = + CallbackObserver(this, &PhoneAPI::preflightSleepCb); +}; \ No newline at end of file diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index e3ef58f14..c6057231c 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 "PowerFSM.h" #include "PowerMon.h" #include "SPILock.h" #include "Throttle.h" @@ -245,6 +246,8 @@ currently active. */ void RadioLibInterface::onNotify(uint32_t notification) { + powerFSM.trigger(EVENT_LORA_INTERRUPT); + switch (notification) { case ISR_TX: handleTransmitInterrupt(); @@ -531,4 +534,4 @@ bool RadioLibInterface::startSend(meshtastic_MeshPacket *txp) return res == RADIOLIB_ERR_NONE; } -} \ No newline at end of file +} diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 02968513c..15cf2424b 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -11,6 +11,7 @@ #include "mesh-pb-constants.h" #include "meshUtils.h" #include "modules/RoutingModule.h" +#include "sleep.h" #if !MESHTASTIC_EXCLUDE_MQTT #include "mqtt/MQTT.h" #endif @@ -56,6 +57,13 @@ Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRA // init Lockguard for crypt operations assert(!cryptLock); cryptLock = new concurrency::Lock(); + + preflightSleepObserver.observe(&preflightSleep); +} + +Router::~Router() +{ + preflightSleepObserver.unobserve(&preflightSleep); } /** @@ -65,6 +73,7 @@ Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRA int32_t Router::runOnce() { meshtastic_MeshPacket *mp; + while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) { // printPacket("handle fromRadioQ", mp); perhapsHandleReceived(mp); @@ -150,6 +159,7 @@ void Router::abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket void Router::setReceivedMessage() { // LOG_DEBUG("set interval to ASAP"); + powerFSM.trigger(EVENT_WAKE_TIMER); setInterval(0); // Run ASAP, so we can figure out our correct sleep time runASAP = true; } diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 58ca50f3d..d6d2869b4 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -29,6 +29,12 @@ class Router : protected concurrency::OSThread, protected PacketHistory */ Router(); + /** + * Destructor + * + */ + ~Router(); + /** * Currently we only allow one interface, that may change in the future */ @@ -138,6 +144,10 @@ class Router : protected concurrency::OSThread, protected PacketHistory /** Frees the provided packet, and generates a NAK indicating the specifed error while sending */ void abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket *p); + + int preflightSleepCb(void *unused = NULL) { return !fromRadioQueue.isEmpty(); } + + CallbackObserver preflightSleepObserver = CallbackObserver(this, &Router::preflightSleepCb); }; enum DecodeState { DECODE_SUCCESS, DECODE_FAILURE, DECODE_FATAL }; diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp index 4a42e5197..6f309cc50 100644 --- a/src/mesh/StreamAPI.cpp +++ b/src/mesh/StreamAPI.cpp @@ -97,6 +97,8 @@ void StreamAPI::writeStream() void StreamAPI::emitTxBuffer(size_t len) { if (len != 0) { + powerFSM.trigger(EVENT_WAKE_TIMER); + txBuf[0] = START1; txBuf[1] = START2; txBuf[2] = (len >> 8) & 0xff; @@ -150,4 +152,4 @@ void StreamAPI::onConnectionChanged(bool connected) // received a packet in a while powerFSM.trigger(EVENT_SERIAL_DISCONNECTED); } -} \ No newline at end of file +} diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 42ebb8417..c1c4512e6 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -148,7 +148,6 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res) { - LOG_DEBUG("webAPI handleAPIv1FromRadio"); /* @@ -163,6 +162,8 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res) // std::string paramAll = "all"; std::string valueAll; + powerFSM.trigger(EVENT_WEB_REQUEST); + // Status code is 200 OK by default. res->setHeader("Content-Type", "application/x-protobuf"); res->setHeader("Access-Control-Allow-Origin", "*"); @@ -207,6 +208,8 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res) { LOG_DEBUG("webAPI handleAPIv1ToRadio"); + powerFSM.trigger(EVENT_WEB_REQUEST); + /* For documentation, see: https://meshtastic.org/docs/development/device/http-api @@ -316,6 +319,8 @@ JSONArray htmlListDir(const char *dirname, uint8_t levels) void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res) { + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Content-Type", "application/json"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); @@ -349,6 +354,8 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res) ResourceParameters *params = req->getParams(); std::string paramValDelete; + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Content-Type", "application/json"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "DELETE"); @@ -384,6 +391,9 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res) ResourceParameters *params = req->getParams(); std::string parameter1; + + powerFSM.trigger(EVENT_WEB_REQUEST); + // Print the first parameter value if (params->getPathParameter(0, parameter1)) { @@ -469,8 +479,10 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res) void handleFormUpload(HTTPRequest *req, HTTPResponse *res) { - LOG_DEBUG("Form Upload - Disable keep-alive"); + + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Connection", "close"); // First, we need to check the encoding of the form that we have received. @@ -597,6 +609,8 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) ResourceParameters *params = req->getParams(); std::string content; + powerFSM.trigger(EVENT_WEB_REQUEST); + if (!params->getQueryParameter("content", content)) { content = "json"; } @@ -701,6 +715,8 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res) ResourceParameters *params = req->getParams(); std::string content; + powerFSM.trigger(EVENT_WEB_REQUEST); + if (!params->getQueryParameter("content", content)) { content = "json"; } @@ -774,6 +790,8 @@ void handleHotspot(HTTPRequest *req, HTTPResponse *res) { LOG_INFO("Hotspot Request"); + powerFSM.trigger(EVENT_WEB_REQUEST); + /* If we don't do a redirect, be sure to return a "Success" message otherwise iOS will have trouble detecting that the connection to the SoftAP worked. @@ -791,6 +809,8 @@ void handleHotspot(HTTPRequest *req, HTTPResponse *res) void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res) { + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Content-Type", "text/html"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); @@ -808,6 +828,8 @@ void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res) void handleAdmin(HTTPRequest *req, HTTPResponse *res) { + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Content-Type", "text/html"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); @@ -820,6 +842,8 @@ void handleAdmin(HTTPRequest *req, HTTPResponse *res) void handleAdminSettings(HTTPRequest *req, HTTPResponse *res) { + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Content-Type", "text/html"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); @@ -842,6 +866,8 @@ void handleAdminSettings(HTTPRequest *req, HTTPResponse *res) void handleAdminSettingsApply(HTTPRequest *req, HTTPResponse *res) { + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Content-Type", "text/html"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "POST"); @@ -854,6 +880,8 @@ void handleAdminSettingsApply(HTTPRequest *req, HTTPResponse *res) void handleFs(HTTPRequest *req, HTTPResponse *res) { + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Content-Type", "text/html"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); @@ -866,6 +894,8 @@ void handleFs(HTTPRequest *req, HTTPResponse *res) void handleRestart(HTTPRequest *req, HTTPResponse *res) { + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Content-Type", "text/html"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); @@ -879,6 +909,8 @@ void handleRestart(HTTPRequest *req, HTTPResponse *res) void handleBlinkLED(HTTPRequest *req, HTTPResponse *res) { + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Content-Type", "application/json"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "POST"); @@ -917,6 +949,8 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res) void handleScanNetworks(HTTPRequest *req, HTTPResponse *res) { + powerFSM.trigger(EVENT_WEB_REQUEST); + res->setHeader("Content-Type", "application/json"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); @@ -956,4 +990,4 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res) res->print(value->Stringify().c_str()); delete value; } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/http/ContentHandler.h b/src/mesh/http/ContentHandler.h index 2066a6d57..1c62ffd29 100644 --- a/src/mesh/http/ContentHandler.h +++ b/src/mesh/http/ContentHandler.h @@ -1,4 +1,7 @@ #pragma once + +#define HTTP_API_SESSION_TIMEOUT_MS 5000 + void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer); // Declare some handler functions for the various URLs on the server @@ -33,5 +36,5 @@ class HttpAPI : public PhoneAPI protected: /// Check the current underlying physical link to see if the client is currently connected - virtual bool checkIsConnected() override { return true; } // FIXME, be smarter about this -}; \ No newline at end of file + virtual bool checkIsConnected() override { return millis() - lastContactMsec < HTTP_API_SESSION_TIMEOUT_MS; } +}; diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index d1b10fa82..f1158a1eb 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -536,6 +536,9 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m { bool valid = true; bool hasSensor = false; + + powerFSM.trigger(EVENT_WAKE_TIMER); // ensure we're not light-sleeping + m->time = getTime(); m->which_variant = meshtastic_Telemetry_environment_metrics_tag; m->variant.environment_metrics = meshtastic_EnvironmentMetrics_init_zero; @@ -721,6 +724,9 @@ meshtastic_MeshPacket *EnvironmentTelemetryModule::allocReply() bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) { meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; + + powerFSM.trigger(EVENT_WAKE_TIMER); // ensure we're not light-sleeping + m.which_variant = meshtastic_Telemetry_environment_metrics_tag; m.time = getTime(); #ifdef T1000X_SENSOR_EN diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index a92013d01..b56064a71 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -190,6 +190,9 @@ bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &m bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m) { bool valid = false; + + powerFSM.trigger(EVENT_WAKE_TIMER); // ensure we're not light-sleeping + m->time = getTime(); m->which_variant = meshtastic_Telemetry_power_metrics_tag;