From d40b66beac79b0af5e09cd8142876f2b77ec3b3f Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 1 Aug 2021 11:20:38 -0700 Subject: [PATCH 01/12] Allow plugins to write to the parsed protobuf (minimizes copies in some cases) --- src/mesh/ProtobufPlugin.h | 2 +- src/plugins/NodeInfoPlugin.cpp | 2 +- src/plugins/NodeInfoPlugin.h | 2 +- src/plugins/PositionPlugin.cpp | 2 +- src/plugins/PositionPlugin.h | 2 +- src/plugins/RemoteHardwarePlugin.cpp | 2 +- src/plugins/RemoteHardwarePlugin.h | 2 +- src/plugins/RoutingPlugin.cpp | 2 +- src/plugins/RoutingPlugin.h | 2 +- src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp | 2 +- src/plugins/esp32/EnvironmentalMeasurementPlugin.h | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/mesh/ProtobufPlugin.h b/src/mesh/ProtobufPlugin.h index 6984cf62..1b7ca599 100644 --- a/src/mesh/ProtobufPlugin.h +++ b/src/mesh/ProtobufPlugin.h @@ -28,7 +28,7 @@ template class ProtobufPlugin : protected SinglePortPlugin * In general decoded will always be !NULL. But in some special applications (where you have handling packets * for multiple port numbers, decoding will ONLY be attempted for packets where the portnum matches our expected ourPortNum. */ - virtual bool handleReceivedProtobuf(const MeshPacket &mp, const T *decoded) = 0; + virtual bool handleReceivedProtobuf(const MeshPacket &mp, T *decoded) = 0; /** * Return a mesh packet which has been preinited with a particular protobuf data payload and port number. diff --git a/src/plugins/NodeInfoPlugin.cpp b/src/plugins/NodeInfoPlugin.cpp index ea8a97c9..51303ac5 100644 --- a/src/plugins/NodeInfoPlugin.cpp +++ b/src/plugins/NodeInfoPlugin.cpp @@ -8,7 +8,7 @@ NodeInfoPlugin *nodeInfoPlugin; -bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User *pptr) +bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, User *pptr) { auto p = *pptr; diff --git a/src/plugins/NodeInfoPlugin.h b/src/plugins/NodeInfoPlugin.h index eb2a16da..d2b29c91 100644 --- a/src/plugins/NodeInfoPlugin.h +++ b/src/plugins/NodeInfoPlugin.h @@ -26,7 +26,7 @@ class NodeInfoPlugin : public ProtobufPlugin, private concurrency::OSThrea @return true if you've guaranteed you've handled this message and no other handlers should be considered for it */ - virtual bool handleReceivedProtobuf(const MeshPacket &mp, const User *p); + virtual bool handleReceivedProtobuf(const MeshPacket &mp, User *p); /** Messages can be received that have the want_response bit set. If set, this callback will be invoked * so that subclasses can (optionally) send a response back to the original sender. */ diff --git a/src/plugins/PositionPlugin.cpp b/src/plugins/PositionPlugin.cpp index adf6a05c..9dfe07d5 100644 --- a/src/plugins/PositionPlugin.cpp +++ b/src/plugins/PositionPlugin.cpp @@ -14,7 +14,7 @@ PositionPlugin::PositionPlugin() setIntervalFromNow(60 * 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup) } -bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position *pptr) +bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, Position *pptr) { auto p = *pptr; diff --git a/src/plugins/PositionPlugin.h b/src/plugins/PositionPlugin.h index 45c4884a..3e4034d4 100644 --- a/src/plugins/PositionPlugin.h +++ b/src/plugins/PositionPlugin.h @@ -33,7 +33,7 @@ class PositionPlugin : public ProtobufPlugin, private concurrency::OST @return true if you've guaranteed you've handled this message and no other handlers should be considered for it */ - virtual bool handleReceivedProtobuf(const MeshPacket &mp, const Position *p); + virtual bool handleReceivedProtobuf(const MeshPacket &mp, Position *p); /** Messages can be received that have the want_response bit set. If set, this callback will be invoked * so that subclasses can (optionally) send a response back to the original sender. */ diff --git a/src/plugins/RemoteHardwarePlugin.cpp b/src/plugins/RemoteHardwarePlugin.cpp index 9068b14d..60ef816d 100644 --- a/src/plugins/RemoteHardwarePlugin.cpp +++ b/src/plugins/RemoteHardwarePlugin.cpp @@ -48,7 +48,7 @@ RemoteHardwarePlugin::RemoteHardwarePlugin() { } -bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage *pptr) +bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, HardwareMessage *pptr) { auto p = *pptr; DEBUG_MSG("Received RemoteHardware typ=%d\n", p.typ); diff --git a/src/plugins/RemoteHardwarePlugin.h b/src/plugins/RemoteHardwarePlugin.h index fad6e838..86a0bbd3 100644 --- a/src/plugins/RemoteHardwarePlugin.h +++ b/src/plugins/RemoteHardwarePlugin.h @@ -27,7 +27,7 @@ class RemoteHardwarePlugin : public ProtobufPlugin, private con @return true if you've guaranteed you've handled this message and no other handlers should be considered for it */ - virtual bool handleReceivedProtobuf(const MeshPacket &mp, const HardwareMessage *p); + virtual bool handleReceivedProtobuf(const MeshPacket &mp, HardwareMessage *p); /** * Periodically read the gpios we have been asked to WATCH, if they have changed, diff --git a/src/plugins/RoutingPlugin.cpp b/src/plugins/RoutingPlugin.cpp index d4f6f925..82b0403f 100644 --- a/src/plugins/RoutingPlugin.cpp +++ b/src/plugins/RoutingPlugin.cpp @@ -7,7 +7,7 @@ RoutingPlugin *routingPlugin; -bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Routing *r) +bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, Routing *r) { printPacket("Routing sniffing", &mp); router->sniffReceived(&mp, r); diff --git a/src/plugins/RoutingPlugin.h b/src/plugins/RoutingPlugin.h index ff659de2..c0288f60 100644 --- a/src/plugins/RoutingPlugin.h +++ b/src/plugins/RoutingPlugin.h @@ -22,7 +22,7 @@ class RoutingPlugin : public ProtobufPlugin @return true if you've guaranteed you've handled this message and no other handlers should be considered for it */ - virtual bool handleReceivedProtobuf(const MeshPacket &mp, const Routing *p); + virtual bool handleReceivedProtobuf(const MeshPacket &mp, Routing *p); /** Messages can be received that have the want_response bit set. If set, this callback will be invoked * so that subclasses can (optionally) send a response back to the original sender. */ diff --git a/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp b/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp index 69482dbc..5e5ad815 100644 --- a/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp +++ b/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp @@ -204,7 +204,7 @@ void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplay } -bool EnvironmentalMeasurementPlugin::handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *p) +bool EnvironmentalMeasurementPlugin::handleReceivedProtobuf(const MeshPacket &mp, EnvironmentalMeasurement *p) { if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){ // If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume diff --git a/src/plugins/esp32/EnvironmentalMeasurementPlugin.h b/src/plugins/esp32/EnvironmentalMeasurementPlugin.h index 3123cc0f..88306ab2 100644 --- a/src/plugins/esp32/EnvironmentalMeasurementPlugin.h +++ b/src/plugins/esp32/EnvironmentalMeasurementPlugin.h @@ -18,7 +18,7 @@ class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public Pro /** 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 MeshPacket &mp, const EnvironmentalMeasurement *p); + virtual bool handleReceivedProtobuf(const MeshPacket &mp, EnvironmentalMeasurement *p); virtual int32_t runOnce(); /** * Send our EnvironmentalMeasurement into the mesh From 5f323e8bd18cac32c2f889a06fd8264ca2b3db7b Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 1 Aug 2021 11:21:36 -0700 Subject: [PATCH 02/12] fix leakage of wifi password reported by @vodkin --- src/plugins/AdminPlugin.cpp | 25 +++++++++++++++++++++++-- src/plugins/AdminPlugin.h | 4 ++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/plugins/AdminPlugin.cpp b/src/plugins/AdminPlugin.cpp index f998a3de..f642308d 100644 --- a/src/plugins/AdminPlugin.cpp +++ b/src/plugins/AdminPlugin.cpp @@ -12,6 +12,24 @@ AdminPlugin *adminPlugin; +/// A special reserved string to indicate strings we can not share with external nodes. We will use this 'reserved' word instead. +/// Also, to make setting work correctly, if someone tries to set a string to this reserved value we assume they don't really want a change. +static const char *secretReserved = "sekrit"; + +/// If buf is !empty, change it to secret +static void hideSecret(char *buf) { + if(*buf) { + strcpy(buf, secretReserved); + } +} + +/// If buf is the reserved secret word, replace the buffer with currentVal +static void writeSecret(char *buf, const char *currentVal) { + if(strcmp(buf, secretReserved) == 0) { + strcpy(buf, currentVal); + } +} + void AdminPlugin::handleGetChannel(const MeshPacket &req, uint32_t channelIndex) { if (req.decoded.want_response) { @@ -35,13 +53,15 @@ void AdminPlugin::handleGetRadio(const MeshPacket &req) // using to the app (so that even old phone apps work with new device loads). r.get_radio_response.preferences.ls_secs = getPref_ls_secs(); r.get_radio_response.preferences.phone_timeout_secs = getPref_phone_timeout_secs(); + // hideSecret(r.get_radio_response.preferences.wifi_ssid); // hmm - leave public for now, because only minimally private and useful for users to know current provisioning) + hideSecret(r.get_radio_response.preferences.wifi_password); r.which_variant = AdminMessage_get_radio_response_tag; myReply = allocDataProtobuf(r); } } -bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessage *r) +bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r) { assert(r); switch (r->which_variant) { @@ -139,8 +159,9 @@ void AdminPlugin::handleSetChannel(const Channel &cc) } } -void AdminPlugin::handleSetRadio(const RadioConfig &r) +void AdminPlugin::handleSetRadio(RadioConfig &r) { + writeSecret(r.preferences.wifi_password, radioConfig.preferences.wifi_password); radioConfig = r; service.reloadConfig(); diff --git a/src/plugins/AdminPlugin.h b/src/plugins/AdminPlugin.h index e4fc91d6..bbd0d14a 100644 --- a/src/plugins/AdminPlugin.h +++ b/src/plugins/AdminPlugin.h @@ -17,12 +17,12 @@ class AdminPlugin : public ProtobufPlugin @return true if you've guaranteed you've handled this message and no other handlers should be considered for it */ - virtual bool handleReceivedProtobuf(const MeshPacket &mp, const AdminMessage *p); + virtual bool handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *p); private: void handleSetOwner(const User &o); void handleSetChannel(const Channel &cc); - void handleSetRadio(const RadioConfig &r); + void handleSetRadio(RadioConfig &r); void handleGetChannel(const MeshPacket &req, uint32_t channelIndex); void handleGetRadio(const MeshPacket &req); From 596befff74e0b1455641081f15504b29f762b1ae Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 2 Aug 2021 10:41:31 -0700 Subject: [PATCH 03/12] Fix invalid heap reference fixed by @flux242 --- src/mqtt/MQTT.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 33e0ee7c..450c8441 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -62,7 +62,8 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient) void MQTT::reconnect() { // pubSub.setServer("devsrv.ezdevice.net", 1883); or 192.168.10.188 - const char *serverAddr = "mqtt.meshtastic.org:1883"; // default hostname + const char *serverAddr = "mqtt.meshtastic.org"; // default hostname + int serverPort = 1883; // default server port if (*radioConfig.preferences.mqtt_server) serverAddr = radioConfig.preferences.mqtt_server; // Override the default @@ -70,15 +71,16 @@ void MQTT::reconnect() String server = String(serverAddr); int delimIndex = server.indexOf(':'); if (delimIndex > 0) { - String host = server.substring(0, delimIndex); String port = server.substring(delimIndex+1, server.length()); - pubSub.setServer(host.c_str(), port.toInt()); + server[delimIndex] = 0; + serverPort = port.toInt(); + pubSub.setServer(server.c_str(), serverPort); } else { - pubSub.setServer(serverAddr, 1883); + pubSub.setServer(serverAddr, serverPort); } - DEBUG_MSG("Connecting to MQTT server\n", serverAddr); + DEBUG_MSG("Connecting to MQTT server %s, port: %d\n", server.c_str(), serverPort); auto myStatus = (statusTopic + owner.id); bool connected = pubSub.connect(owner.id, "meshdev", "large4cats", myStatus.c_str(), 1, true, "offline"); if (connected) { From 2fe11d4fe8b0b2f3540494ef5774bd19475be417 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 2 Aug 2021 10:50:28 -0700 Subject: [PATCH 04/12] don't break strict-aliasing rules --- src/mesh/CryptoEngine.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 9783f625..bce9bb02 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -32,6 +32,10 @@ void CryptoEngine::decrypt(uint32_t fromNode, uint64_t packetNum, size_t numByte void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetNum) { memset(nonce, 0, sizeof(nonce)); - *((uint64_t *)&nonce[0]) = packetNum; - *((uint32_t *)&nonce[8]) = fromNode; + + // use memcpy to avoid breaking strict-aliasing + memcpy(nonce, &packetNum, sizeof(uint64_t)); + memcpy(nonce + sizeof(uint64_t), &fromNode, sizeof(uint32_t)); + //*((uint64_t *)&nonce[0]) = packetNum; + //*((uint32_t *)&nonce[8]) = fromNode; } \ No newline at end of file From 72807f0fa06e3611f3d399f0d9dc15d72af19d74 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 2 Aug 2021 10:50:55 -0700 Subject: [PATCH 05/12] CSE to cleanup mqtt addr setting --- src/mqtt/MQTT.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 450c8441..674f13e6 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -74,11 +74,9 @@ void MQTT::reconnect() String port = server.substring(delimIndex+1, server.length()); server[delimIndex] = 0; serverPort = port.toInt(); - pubSub.setServer(server.c_str(), serverPort); - } - else { - pubSub.setServer(serverAddr, serverPort); + serverAddr = server.c_str(); } + pubSub.setServer(serverAddr, serverPort); DEBUG_MSG("Connecting to MQTT server %s, port: %d\n", server.c_str(), serverPort); auto myStatus = (statusTopic + owner.id); From 39df7108a889bf0a07dc98a3c1a920864a59aef2 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 2 Aug 2021 11:28:57 -0700 Subject: [PATCH 06/12] fix wifi hang when bad password used, cleanup wifi in general --- src/mesh/http/WiFiAPClient.cpp | 59 ++++++++++++++++++---------------- src/mesh/http/WiFiAPClient.h | 2 -- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/mesh/http/WiFiAPClient.cpp b/src/mesh/http/WiFiAPClient.cpp index 6cccd6ac..13d4cd44 100644 --- a/src/mesh/http/WiFiAPClient.cpp +++ b/src/mesh/http/WiFiAPClient.cpp @@ -1,5 +1,6 @@ #include "mesh/http/WiFiAPClient.h" #include "NodeDB.h" +#include "concurrency/Periodic.h" #include "configuration.h" #include "main.h" #include "mesh/http/WebServer.h" @@ -9,6 +10,8 @@ #include #include +using namespace concurrency; + static void WiFiEvent(WiFiEvent_t event); // DNS Server for the Captive Portal @@ -23,6 +26,32 @@ bool forcedSoftAP = 0; bool APStartupComplete = 0; +static bool needReconnect = true; // If we create our reconnector, run it once at the beginning + +static int32_t reconnectWiFi() +{ + if (radioConfig.has_preferences && needReconnect) { + + const char *wifiName = radioConfig.preferences.wifi_ssid; + const char *wifiPsw = radioConfig.preferences.wifi_password; + + if (!*wifiPsw) // Treat empty password as no password + wifiPsw = NULL; + + if (*wifiName) { + needReconnect = false; + + DEBUG_MSG("... Reconnecting to WiFi access point"); + WiFi.mode(WIFI_MODE_STA); + WiFi.begin(wifiName, wifiPsw); + } + } + + return 30 * 1000; // every 30 seconds +} + +static Periodic *wifiReconnect; + bool isSoftAPForced() { return forcedSoftAP; @@ -155,12 +184,8 @@ void initWifi(bool forceSoftAP) }, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); - DEBUG_MSG("JOINING WIFI: ssid=%s\n", wifiName); - if (WiFi.begin(wifiName, wifiPsw) == WL_CONNECTED) { - DEBUG_MSG("MY IP ADDRESS: %s\n", WiFi.localIP().toString().c_str()); - } else { - DEBUG_MSG("Started Joining WIFI\n"); - } + DEBUG_MSG("JOINING WIFI soon: ssid=%s\n", wifiName); + wifiReconnect = new Periodic("WifiConnect", reconnectWiFi); } } @@ -205,7 +230,7 @@ static void WiFiEvent(WiFiEvent_t event) DEBUG_MSG("Disconnected from WiFi access point\n"); // Event 5 - reconnectWiFi(); + needReconnect = true; break; case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: DEBUG_MSG("Authentication mode of access point has changed\n"); @@ -302,26 +327,6 @@ void handleDNSResponse() } } -void reconnectWiFi() -{ - if (radioConfig.has_preferences) { - - const char *wifiName = radioConfig.preferences.wifi_ssid; - const char *wifiPsw = radioConfig.preferences.wifi_password; - - if (!*wifiPsw) // Treat empty password as no password - wifiPsw = NULL; - - if (*wifiName) { - - DEBUG_MSG("... Reconnecting to WiFi access point"); - - WiFi.mode(WIFI_MODE_STA); - WiFi.begin(wifiName, wifiPsw); - } - } -} - uint8_t getWifiDisconnectReason() { return wifiDisconnectReason; diff --git a/src/mesh/http/WiFiAPClient.h b/src/mesh/http/WiFiAPClient.h index 9e2f8ad7..cb27bb4a 100644 --- a/src/mesh/http/WiFiAPClient.h +++ b/src/mesh/http/WiFiAPClient.h @@ -16,8 +16,6 @@ bool isWifiAvailable(); void handleDNSResponse(); -void reconnectWiFi(); - bool isSoftAPForced(); uint8_t getWifiDisconnectReason(); From 99d529be51113a71bfd2771ac105f282cbafb884 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 2 Aug 2021 17:42:44 -0700 Subject: [PATCH 07/12] While connected to MQTT server, veto light-sleep (to keep wifi working) --- src/mesh/http/WiFiAPClient.cpp | 19 +++++++++++++++++-- src/mqtt/MQTT.cpp | 6 ++++-- src/mqtt/MQTT.h | 6 ++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/mesh/http/WiFiAPClient.cpp b/src/mesh/http/WiFiAPClient.cpp index 13d4cd44..ed4cd355 100644 --- a/src/mesh/http/WiFiAPClient.cpp +++ b/src/mesh/http/WiFiAPClient.cpp @@ -28,6 +28,21 @@ bool APStartupComplete = 0; static bool needReconnect = true; // If we create our reconnector, run it once at the beginning +// FIXME, veto light sleep if we have a TCP server running +#if 0 +class WifiSleepObserver : public Observer { +protected: + + /// Return 0 if sleep is okay + virtual int onNotify(uint32_t newValue) { + + } +}; + +static WifiSleepObserver wifiSleepObserver; +//preflightSleepObserver.observe(&preflightSleep); +#endif + static int32_t reconnectWiFi() { if (radioConfig.has_preferences && needReconnect) { @@ -218,10 +233,10 @@ static void WiFiEvent(WiFiEvent_t event) DEBUG_MSG("Completed scan for access points\n"); break; case SYSTEM_EVENT_STA_START: - DEBUG_MSG("WiFi client started\n"); + DEBUG_MSG("WiFi station started\n"); break; case SYSTEM_EVENT_STA_STOP: - DEBUG_MSG("WiFi clients stopped\n"); + DEBUG_MSG("WiFi station stopped\n"); break; case SYSTEM_EVENT_STA_CONNECTED: DEBUG_MSG("Connected to access point\n"); diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 674f13e6..4b4d3a0c 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -4,6 +4,7 @@ #include "mesh/Channels.h" #include "mesh/Router.h" #include "mesh/generated/mqtt.pb.h" +#include "sleep.h" #include #include @@ -57,11 +58,12 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient) mqtt = this; pubSub.setCallback(mqttCallback); + + preflightSleepObserver.observe(&preflightSleep); } void MQTT::reconnect() { - // pubSub.setServer("devsrv.ezdevice.net", 1883); or 192.168.10.188 const char *serverAddr = "mqtt.meshtastic.org"; // default hostname int serverPort = 1883; // default server port @@ -78,7 +80,7 @@ void MQTT::reconnect() } pubSub.setServer(serverAddr, serverPort); - DEBUG_MSG("Connecting to MQTT server %s, port: %d\n", server.c_str(), serverPort); + DEBUG_MSG("Connecting to MQTT server %s, port: %d\n", serverAddr, serverPort); auto myStatus = (statusTopic + owner.id); bool connected = pubSub.connect(owner.id, "meshdev", "large4cats", myStatus.c_str(), 1, true, "offline"); if (connected) { diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index 2f98c311..fd02db61 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -19,6 +19,9 @@ class MQTT : private concurrency::OSThread WiFiClient mqttClient; PubSubClient pubSub; + CallbackObserver preflightSleepObserver = + CallbackObserver(this, &MQTT::preflightSleepCb); + public: MQTT(); @@ -53,6 +56,9 @@ class MQTT : private concurrency::OSThread /// Called when a new publish arrives from the MQTT server void onPublish(char *topic, byte *payload, unsigned int length); + + /// Return 0 if sleep is okay, veto sleep if we are connected to pubsub server + int preflightSleepCb(void *unused = NULL) { return pubSub.connected() ? 1 : 0; } }; void mqttInit(); From 2af4c619e1dc085033f40bc5ca42f1a402fbdc9d Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 2 Aug 2021 21:07:32 -0700 Subject: [PATCH 08/12] fix #801 (I think) we were sometimes dropping packets in light sleep Because of failure to enter the NB state packets were not getting queued for sending. --- src/PowerFSM.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 53ba66aa..3412bdf3 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -41,7 +41,7 @@ static void lsEnter() screen->setOn(false); secsSlept = 0; // How long have we been sleeping this time - DEBUG_MSG("lsEnter end\n"); + // DEBUG_MSG("lsEnter end\n"); } static void lsIdle() @@ -226,8 +226,8 @@ void PowerFSM_setup() // if we are a router node, we go to NB (no need for bluetooth) otherwise we go to DARK (so we can send message to phone) powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_WAKE_TIMER, NULL, "Wake timer"); - // Note we don't really use this transition, because when we wake from light sleep we _always_ transition to NB or dark and - // then it handles things powerFSM.add_transition(&stateLS, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet"); + // We need this transition, because we might not transition if we were waiting to enter light-sleep, because when we wake from light sleep we _always_ transition to NB or dark and + powerFSM.add_transition(&stateLS, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet"); powerFSM.add_transition(&stateNB, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet, resetting win wake"); From 28af18389bf5a5dcb775de7d1c350fa2bb7a4e26 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 2 Aug 2021 21:34:14 -0700 Subject: [PATCH 09/12] If MQTT connected don't let the board enter LS state --- platformio.ini | 2 +- src/PowerFSM.cpp | 10 +++++++++- src/mqtt/MQTT.cpp | 4 +++- src/mqtt/MQTT.h | 6 +++--- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/platformio.ini b/platformio.ini index 279e5e46..4e3045c2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -70,7 +70,7 @@ lib_deps = https://github.com/meshtastic/esp8266-oled-ssd1306.git#35d796226b853b0c0ff818b2f1aa3d35e7296a96 ; ESP8266_SSD1306 https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce 1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib - https://github.com/meshtastic/arduino-fsm.git#829e967b8a95c094f73c60ef8dacfe66eae38940 + https://github.com/meshtastic/arduino-fsm.git https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad https://github.com/meshtastic/RadioLib.git#80ed10d689a0568782c5bd152906b0f97d2bce93 https://github.com/meshtastic/TinyGPSPlus.git#f0f47067ef2f67c856475933188251c1ef615e79 diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 3412bdf3..38b2672b 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -27,6 +27,7 @@ static bool isPowered() static void sdsEnter() { + DEBUG_MSG("Enter state: SDS\n"); // FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw doDeepSleep(getPref_sds_secs() * 1000LL); } @@ -112,6 +113,7 @@ static void lsIdle() static void lsExit() { + DEBUG_MSG("Exit state: LS\n"); // setGPSPower(true); // restore GPS power if (gps) gps->forceWake(true); @@ -119,6 +121,7 @@ static void lsExit() static void nbEnter() { + DEBUG_MSG("Enter state: NB\n"); screen->setOn(false); setBluetoothEnable(false); @@ -133,6 +136,7 @@ static void darkEnter() static void serialEnter() { + DEBUG_MSG("Enter state: SERIAL\n"); setBluetoothEnable(false); screen->setOn(true); screen->print("Serial connected\n"); @@ -145,6 +149,7 @@ static void serialExit() static void powerEnter() { + DEBUG_MSG("Enter state: POWER\n"); if (!isPowered()) { // If we got here, we are in the wrong state - we should be in powered, let that state ahndle things DEBUG_MSG("Loss of power in Powered\n"); @@ -174,6 +179,7 @@ static void powerExit() static void onEnter() { + DEBUG_MSG("Enter state: ON\n"); screen->setOn(true); setBluetoothEnable(true); @@ -202,7 +208,9 @@ static void screenPress() screen->onPress(); } -static void bootEnter() {} +static void bootEnter() { + DEBUG_MSG("Enter state: BOOT\n"); +} State stateSDS(sdsEnter, NULL, NULL, "SDS"); State stateLS(lsEnter, lsIdle, lsExit, "LS"); diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 4b4d3a0c..7e92919f 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -4,6 +4,7 @@ #include "mesh/Channels.h" #include "mesh/Router.h" #include "mesh/generated/mqtt.pb.h" +#include "PowerFSM.h" #include "sleep.h" #include #include @@ -59,7 +60,7 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient) pubSub.setCallback(mqttCallback); - preflightSleepObserver.observe(&preflightSleep); + // preflightSleepObserver.observe(&preflightSleep); } void MQTT::reconnect() @@ -151,6 +152,7 @@ int32_t MQTT::runOnce() pubSub.disconnect(); } + powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // Suppress entering light sleep (because that would turn off bluetooth) return 20; } } diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index fd02db61..f5ef9bf1 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -19,8 +19,8 @@ class MQTT : private concurrency::OSThread WiFiClient mqttClient; PubSubClient pubSub; - CallbackObserver preflightSleepObserver = - CallbackObserver(this, &MQTT::preflightSleepCb); + // instead we supress sleep from our runOnce() callback + // CallbackObserver preflightSleepObserver = CallbackObserver(this, &MQTT::preflightSleepCb); public: MQTT(); @@ -58,7 +58,7 @@ class MQTT : private concurrency::OSThread void onPublish(char *topic, byte *payload, unsigned int length); /// Return 0 if sleep is okay, veto sleep if we are connected to pubsub server - int preflightSleepCb(void *unused = NULL) { return pubSub.connected() ? 1 : 0; } + // int preflightSleepCb(void *unused = NULL) { return pubSub.connected() ? 1 : 0; } }; void mqttInit(); From 057b04a88a02431a1cbae50bb0d09193cbd4e013 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 2 Aug 2021 22:07:39 -0700 Subject: [PATCH 10/12] treat RECEIVED_PACKET like PACKET_FOR_PHONE --- src/PowerFSM.cpp | 10 +++++----- src/PowerFSM.h | 2 +- src/mesh/MeshService.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 38b2672b..b724f445 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -235,9 +235,8 @@ void PowerFSM_setup() powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_WAKE_TIMER, NULL, "Wake timer"); // We need this transition, because we might not transition if we were waiting to enter light-sleep, because when we wake from light sleep we _always_ transition to NB or dark and - powerFSM.add_transition(&stateLS, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet"); - - powerFSM.add_transition(&stateNB, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet, resetting win wake"); + powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, exiting light sleep"); + powerFSM.add_transition(&stateNB, &stateNB, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, resetting win wake"); // Handle press events - note: we ignore button presses when in API mode powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL, "Press"); @@ -261,6 +260,9 @@ void PowerFSM_setup() // if we are a router we don't turn the screen on for these things if (!isRouter) { + // if any packet destined for phone arrives, turn on bluetooth at least + powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone"); + // show the latest node when we get a new node db update powerFSM.add_transition(&stateNB, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update"); powerFSM.add_transition(&stateDARK, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update"); @@ -300,8 +302,6 @@ void PowerFSM_setup() powerFSM.add_transition(&stateDARK, &stateON, EVENT_FIRMWARE_UPDATE, NULL, "Got firmware update"); powerFSM.add_transition(&stateON, &stateON, EVENT_FIRMWARE_UPDATE, NULL, "Got firmware update"); - powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone"); - powerFSM.add_timed_transition(&stateON, &stateDARK, getPref_screen_on_secs() * 1000, NULL, "Screen-on timeout"); // On most boards we use light-sleep to be our main state, but on NRF52 we just stay in DARK diff --git a/src/PowerFSM.h b/src/PowerFSM.h index e48b55d8..5541385a 100644 --- a/src/PowerFSM.h +++ b/src/PowerFSM.h @@ -6,7 +6,7 @@ #define EVENT_PRESS 1 #define EVENT_WAKE_TIMER 2 -#define EVENT_RECEIVED_PACKET 3 +// #define EVENT_RECEIVED_PACKET 3 #define EVENT_PACKET_FOR_PHONE 4 #define EVENT_RECEIVED_TEXT_MSG 5 // #define EVENT_BOOT 6 // now done with a timed transition diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index fbc4031b..c8865030 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -66,7 +66,7 @@ void MeshService::init() int MeshService::handleFromRadio(const MeshPacket *mp) { - powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping + powerFSM.trigger(EVENT_PACKET_FOR_PHONE); // Possibly keep the node from sleeping printPacket("Forwarding to phone", mp); nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio From 6d2cd73599c6984e6bad6697000bbfafde43c521 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 4 Aug 2021 09:10:34 -0700 Subject: [PATCH 11/12] show a max of four node screens in the scrolling list --- src/graphics/Screen.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 47688094..f2fc1e1e 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -946,7 +946,9 @@ void Screen::setFrames() normalFrames[numframes++] = drawTextMessageFrame; // then all the nodes - for (size_t i = 0; i < numnodes; i++) + // We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens + size_t numToShow = min(numnodes, 4U); + for (size_t i = 0; i < numToShow; i++) normalFrames[numframes++] = drawNodeInfo; // then the debug info From 04d3f441796326e3965ffcc732aad786dc773aec Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 12 Aug 2021 15:50:54 -0700 Subject: [PATCH 12/12] Updates to work with latest adafruit nrf52 arduino --- platformio.ini | 19 ++++++++++++------- src/nrf52/main-nrf52.cpp | 6 ++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/platformio.ini b/platformio.ini index 4e3045c2..2ed1d992 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,14 +9,14 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = tbeam +;default_envs = tbeam ;default_envs = tbeam0.7 ;default_envs = heltec-v2.0 ;default_envs = tlora-v1 ;default_envs = tlora_v1_3 ;default_envs = tlora-v2 ;default_envs = lora-relay-v1 # nrf board -;default_envs = t-echo +default_envs = t-echo ;default_envs = nrf52840dk-geeksville ;default_envs = native # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here ;default_envs = rak4631 @@ -218,7 +218,7 @@ src_filter = ${arduino_base.src_filter} - - - - - - lib_ignore = BluetoothOTA -monitor_port = /dev/ttyACM1 +; monitor_port = /dev/ttyACM1 # we pass in options to jlink so it can understand freertos (note: we don't use "jlink" as the tool) ;debug_tool = jlink @@ -255,9 +255,12 @@ debug_init_break = [nrf52840_base] ; Common base class for all nrf52840 based targets extends = nrf52_base +; was -DTINY_USB +build_flags = ${nrf52_base.build_flags} lib_deps = ${arduino_base.lib_deps} Adafruit nRFCrypto + # Adafruit TinyUSB Arduino # add Adafruit nRFCrypto platform IO automated scan is broken [env:lora_isp4520] @@ -315,7 +318,7 @@ extends = nrf52840_base board = wiscore_rak4631 # add our variants files to the include and src paths # define build flags for the TFT_eSPI library -build_flags = ${nrf52_base.build_flags} -Ivariants/WisCore_RAK4631_Board +build_flags = ${nrf52840_base.build_flags} -Ivariants/WisCore_RAK4631_Board src_filter = ${nrf52_base.src_filter} +<../variants/WisCore_RAK4631_Board> debug_tool = jlink ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) @@ -354,10 +357,12 @@ lib_deps = [env:t-echo] extends = nrf52840_base board = t-echo +debug_tool = jlink +upload_protocol = jlink # add our variants files to the include and src paths # define build flags for the TFT_eSPI library - NOTE: WE NOT LONGER USE TFT_eSPI, it was for an earlier version of the TTGO eink screens # -DBUSY_PIN=3 -DRST_PIN=2 -DDC_PIN=28 -DCS_PIN=30 -build_flags = ${nrf52_base.build_flags} -Ivariants/t-echo +build_flags = ${nrf52840_base.build_flags} -Ivariants/t-echo src_filter = ${nrf52_base.src_filter} +<../variants/t-echo> lib_deps = ${nrf52840_base.lib_deps} @@ -385,7 +390,7 @@ extends = nrf52840_base board = lora-relay-v1 # add our variants files to the include and src paths # define build flags for the TFT_eSPI library -build_flags = ${nrf52_base.build_flags} -Ivariants/lora_relay_v1 +build_flags = ${nrf52840_base.build_flags} -Ivariants/lora_relay_v1 -DUSER_SETUP_LOADED -DTFT_WIDTH=80 -DTFT_HEIGHT=160 @@ -407,7 +412,7 @@ extends = nrf52840_base board = lora-relay-v2 # add our variants files to the include and src paths # define build flags for the TFT_eSPI library -build_flags = ${nrf52_base.build_flags} -Ivariants/lora_relay_v2 +build_flags = ${nrf52840_base.build_flags} -Ivariants/lora_relay_v2 -DUSER_SETUP_LOADED -DTFT_WIDTH=80 -DTFT_HEIGHT=160 diff --git a/src/nrf52/main-nrf52.cpp b/src/nrf52/main-nrf52.cpp index 6325bac8..647aefe2 100644 --- a/src/nrf52/main-nrf52.cpp +++ b/src/nrf52/main-nrf52.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "NRF52Bluetooth.h" #include "error.h" @@ -20,7 +21,8 @@ static inline void debugger_break(void) } bool loopCanSleep() { - return !tud_cdc_connected(); + // turn off sleep only while connected via USB + return !(TinyUSBDevice.mounted() && !TinyUSBDevice.suspended()); } // handle standard gcc assert failures @@ -37,7 +39,7 @@ void getMacAddr(uint8_t *dmac) ble_gap_addr_t addr; if (sd_ble_gap_addr_get(&addr) == NRF_SUCCESS) { memcpy(dmac, addr.addr, 6); - } else { + } else { const uint8_t *src = (const uint8_t *)NRF_FICR->DEVICEADDR; dmac[5] = src[0]; dmac[4] = src[1];