From b20930c11135d019fce89898aafa724296e2ac16 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 25 Mar 2021 06:15:15 +0800 Subject: [PATCH 01/15] move streamapi into a thread, saves power and increases responsiveness --- src/SerialConsole.cpp | 21 ++++--- src/SerialConsole.h | 8 +-- src/concurrency/BinarySemaphoreFreeRTOS.h | 3 +- src/configuration.h | 2 +- src/main.cpp | 26 ++++----- src/mesh/MeshService.cpp | 9 ++- src/mesh/MeshService.h | 5 +- src/mesh/PhoneAPI.cpp | 33 ++++++----- src/mesh/PhoneAPI.h | 10 ++-- src/mesh/StreamAPI.cpp | 70 ++++++++++++++--------- src/mesh/StreamAPI.h | 15 +++-- src/mesh/wifi/WiFiServerAPI.cpp | 27 +++------ src/mesh/wifi/WiFiServerAPI.h | 6 +- 13 files changed, 121 insertions(+), 114 deletions(-) diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index ad1cf642a..11534b1ee 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -6,25 +6,28 @@ #define Port Serial -SerialConsole console; +SerialConsole *console; + +void consoleInit() +{ + new SerialConsole(); // Must be dynamically allocated because we are now inheriting from thread +} void consolePrintf(const char *format, ...) { va_list arg; va_start(arg, format); - console.vprintf(format, arg); + console->vprintf(format, arg); va_end(arg); } SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port) { + assert(!console); + console = this; canWrite = false; // We don't send packets to our port until it has talked to us first - // setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks -} + // setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks -/// Do late init that can't happen at constructor time -void SerialConsole::init() -{ Port.begin(SERIAL_BAUD); StreamAPI::init(); emitRebooted(); @@ -34,14 +37,14 @@ void SerialConsole::init() * we override this to notice when we've received a protobuf over the serial * stream. Then we shunt off debug serial output. */ -void SerialConsole::handleToRadio(const uint8_t *buf, size_t len) +bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) { // Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets if (!radioConfig.preferences.debug_log_enabled) setDestination(&noopPrint); canWrite = true; - StreamAPI::handleToRadio(buf, len); + return StreamAPI::handleToRadio(buf, len); } /// Hookable to find out when connection changes diff --git a/src/SerialConsole.h b/src/SerialConsole.h index b4f076286..b057c1931 100644 --- a/src/SerialConsole.h +++ b/src/SerialConsole.h @@ -11,14 +11,11 @@ class SerialConsole : public StreamAPI, public RedirectablePrint public: SerialConsole(); - /// Do late init that can't happen at constructor time - virtual void init(); - /** * we override this to notice when we've received a protobuf over the serial stream. Then we shunt off * debug serial output. */ - virtual void handleToRadio(const uint8_t *buf, size_t len); + virtual bool handleToRadio(const uint8_t *buf, size_t len); virtual size_t write(uint8_t c) { @@ -34,5 +31,6 @@ class SerialConsole : public StreamAPI, public RedirectablePrint // A simple wrapper to allow non class aware code write to the console void consolePrintf(const char *format, ...); +void consoleInit(); -extern SerialConsole console; +extern SerialConsole *console; diff --git a/src/concurrency/BinarySemaphoreFreeRTOS.h b/src/concurrency/BinarySemaphoreFreeRTOS.h index b5e488fd2..2883151d8 100644 --- a/src/concurrency/BinarySemaphoreFreeRTOS.h +++ b/src/concurrency/BinarySemaphoreFreeRTOS.h @@ -1,6 +1,5 @@ #pragma once -#include "configuration.h" #include "../freertosinc.h" namespace concurrency @@ -28,4 +27,4 @@ class BinarySemaphoreFreeRTOS #endif -} \ No newline at end of file +} // namespace concurrency \ No newline at end of file diff --git a/src/configuration.h b/src/configuration.h index 1704ad14a..f0d3a0a53 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -457,7 +457,7 @@ along with this program. If not, see . #include "SerialConsole.h" -#define DEBUG_PORT console // Serial debug port +#define DEBUG_PORT (*console) // Serial debug port // What platforms should use SEGGER? #ifdef NRF52_SERIES diff --git a/src/main.cpp b/src/main.cpp index 6a4caf330..1621d081f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -177,6 +177,7 @@ class ButtonThread : public OSThread OneButton userButtonAlt; #endif static bool shutdown_on_long_stop; + public: static uint32_t longPressTime; @@ -250,15 +251,15 @@ class ButtonThread : public OSThread power->shutdown(); } #elif NRF52_SERIES - // Do actual shutdown when button released, otherwise the button release - // may wake the board immediatedly. - if (!shutdown_on_long_stop) { - DEBUG_MSG("Shutdown from long press"); - playBeep(); - ledOff(PIN_LED1); - ledOff(PIN_LED2); - shutdown_on_long_stop = true; - } + // Do actual shutdown when button released, otherwise the button release + // may wake the board immediatedly. + if (!shutdown_on_long_stop) { + DEBUG_MSG("Shutdown from long press"); + playBeep(); + ledOff(PIN_LED1); + ledOff(PIN_LED2); + shutdown_on_long_stop = true; + } #endif } else { // DEBUG_MSG("Long press %u\n", (millis() - longPressTime)); @@ -315,9 +316,8 @@ void setup() SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_TRIM); #endif -// Debug #ifdef DEBUG_PORT - DEBUG_PORT.init(); // Set serial baud rate and init our mesh console + consoleInit(); // Set serial baud rate and init our mesh console #endif initDeepSleep(); @@ -580,10 +580,6 @@ void loop() { // axpDebugOutput.loop(); -#ifdef DEBUG_PORT - DEBUG_PORT.loop(); // Send/receive protobufs over the serial port -#endif - // heap_caps_check_integrity_all(true); // FIXME - disable this expensive check #ifndef NO_ESP32 diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index d542b21cf..e270b5a2c 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -114,7 +114,7 @@ bool MeshService::reloadConfig() void MeshService::reloadOwner() { assert(nodeInfoPlugin); - if(nodeInfoPlugin) + if (nodeInfoPlugin) nodeInfoPlugin->sendOurNodeInfo(); nodeDB.saveToDisk(); } @@ -172,13 +172,12 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies) assert(node); if (node->has_position) { - if(positionPlugin) { + if (positionPlugin) { DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies); positionPlugin->sendOurPosition(dest, wantReplies); } - } - else { - if(nodeInfoPlugin) { + } else { + if (nodeInfoPlugin) { DEBUG_MSG("Sending nodeinfo ping to 0x%x, wantReplies=%d\n", dest, wantReplies); nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies); } diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index 0f2e772b3..7d22a3004 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -84,13 +84,12 @@ class MeshService NodeInfo *refreshMyNodeInfo(); private: - /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh /// returns 0 to allow futher processing int onGPSChanged(const meshtastic::GPSStatus *arg); - /// Handle a packet that just arrived from the radio. This method does _ReliableRouternot_ free the provided packet. If it needs - /// to keep the packet around it makes a copy + /// Handle a packet that just arrived from the radio. This method does _ReliableRouternot_ free the provided packet. If it + /// needs to keep the packet around it makes a copy int handleFromRadio(const MeshPacket *p); friend class RoutingPlugin; }; diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index b50381e70..3eaa60674 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -1,10 +1,10 @@ #include "PhoneAPI.h" +#include "Channels.h" #include "GPS.h" #include "MeshService.h" #include "NodeDB.h" #include "PowerFSM.h" #include "RadioInterface.h" -#include "Channels.h" #include #if FromRadio_size > MAX_TO_FROM_RADIO_SIZE @@ -22,16 +22,18 @@ void PhoneAPI::init() observe(&service.fromNumChanged); } -PhoneAPI::~PhoneAPI() { +PhoneAPI::~PhoneAPI() +{ close(); } -void PhoneAPI::close() { +void PhoneAPI::close() +{ unobserve(); state = STATE_SEND_NOTHING; bool oldConnected = isConnected; isConnected = false; - if(oldConnected != isConnected) + if (oldConnected != isConnected) onConnectionChanged(isConnected); } @@ -49,7 +51,7 @@ void PhoneAPI::checkConnectionTimeout() /** * Handle a ToRadio protobuf */ -void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) +bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) { powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep lastContactMsec = millis(); @@ -62,12 +64,9 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) memset(&toRadioScratch, 0, sizeof(toRadioScratch)); if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) { switch (toRadioScratch.which_payloadVariant) { - case ToRadio_packet_tag: { - MeshPacket &p = toRadioScratch.packet; - printPacket("PACKET FROM PHONE", &p); - service.handleToRadio(p); - break; - } + case ToRadio_packet_tag: + return handleToRadioPacket(toRadioScratch.packet); + case ToRadio_want_config_id_tag: config_nonce = toRadioScratch.want_config_id; DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce); @@ -86,6 +85,8 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) } else { DEBUG_MSG("Error: ignoring malformed toradio\n"); } + + return false; } /** @@ -127,7 +128,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) fromRadioScratch.my_info = myNodeInfo; state = STATE_SEND_NODEINFO; - service.refreshMyNodeInfo(); // Update my NodeInfo because the client will be asking for it soon. + service.refreshMyNodeInfo(); // Update my NodeInfo because the client will be asking for it soon. break; case STATE_SEND_NODEINFO: { @@ -226,7 +227,13 @@ bool PhoneAPI::available() /** * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool */ -void PhoneAPI::handleToRadioPacket(MeshPacket *p) {} +bool PhoneAPI::handleToRadioPacket(MeshPacket &p) +{ + printPacket("PACKET FROM PHONE", &p); + service.handleToRadio(p); + + return true; +} /// If the mesh service tells us fromNum has changed, tell the phone int PhoneAPI::onNotify(uint32_t newValue) diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 6d03d2c80..dc7de3e18 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -67,8 +67,9 @@ class PhoneAPI /** * Handle a ToRadio protobuf + * @return true true if a packet was queued for sending (so that caller can yield) */ - virtual void handleToRadio(const uint8_t *buf, size_t len); + virtual bool handleToRadio(const uint8_t *buf, size_t len); /** * Get the next packet we want to send to the phone @@ -93,7 +94,7 @@ class PhoneAPI /// Hookable to find out when connection changes virtual void onConnectionChanged(bool connected) {} - /// If we haven't heard from the other side in a while then say not connected + /// If we haven't heard from the other side in a while then say not connected void checkConnectionTimeout(); /** @@ -103,9 +104,10 @@ class PhoneAPI private: /** - * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool + * Handle a packet that the phone wants us to send. We can write to it but can not keep a reference to it + * @return true true if a packet was queued for sending */ - void handleToRadioPacket(MeshPacket *p); + bool handleToRadioPacket(MeshPacket &p); /// If the mesh service tells us fromNum has changed, tell the phone virtual int onNotify(uint32_t newValue); diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp index a5e36d463..2bc82b084 100644 --- a/src/mesh/StreamAPI.cpp +++ b/src/mesh/StreamAPI.cpp @@ -5,48 +5,62 @@ #define START2 0xc3 #define HEADER_LEN 4 -void StreamAPI::loop() +int32_t StreamAPI::runOnce() { + auto result = readStream(); writeStream(); - readStream(); checkConnectionTimeout(); + return result; } /** * Read any rx chars from the link and call handleToRadio */ -void StreamAPI::readStream() +int32_t StreamAPI::readStream() { - while (stream->available()) { // Currently we never want to block - uint8_t c = stream->read(); + uint32_t now = millis(); + if (!stream->available()) { + // Nothing available this time, if the computer has talked to us recently, poll often, otherwise let CPU sleep a long time + bool recentRx = (now - lastRxMsec) < 2000; + return recentRx ? 5 : 250; + } else { + while (stream->available()) { // Currently we never want to block + uint8_t c = stream->read(); - // Use the read pointer for a little state machine, first look for framing, then length bytes, then payload - size_t ptr = rxPtr++; // assume we will probably advance the rxPtr + // Use the read pointer for a little state machine, first look for framing, then length bytes, then payload + size_t ptr = rxPtr++; // assume we will probably advance the rxPtr - rxBuf[ptr] = c; // store all bytes (including framing) + rxBuf[ptr] = c; // store all bytes (including framing) - if (ptr == 0) { // looking for START1 - if (c != START1) - rxPtr = 0; // failed to find framing - } else if (ptr == 1) { // looking for START2 - if (c != START2) - rxPtr = 0; // failed to find framing - } else if (ptr >= HEADER_LEN) { // we have at least read our 4 byte framing - uint32_t len = (rxBuf[2] << 8) + rxBuf[3]; // big endian 16 bit length follows framing + if (ptr == 0) { // looking for START1 + if (c != START1) + rxPtr = 0; // failed to find framing + } else if (ptr == 1) { // looking for START2 + if (c != START2) + rxPtr = 0; // failed to find framing + } else if (ptr >= HEADER_LEN) { // we have at least read our 4 byte framing + uint32_t len = (rxBuf[2] << 8) + rxBuf[3]; // big endian 16 bit length follows framing - if (ptr == HEADER_LEN) { - // we _just_ finished our 4 byte header, validate length now (note: a length of zero is a valid - // protobuf also) - if (len > MAX_TO_FROM_RADIO_SIZE) - rxPtr = 0; // length is bogus, restart search for framing - } + if (ptr == HEADER_LEN) { + // we _just_ finished our 4 byte header, validate length now (note: a length of zero is a valid + // protobuf also) + if (len > MAX_TO_FROM_RADIO_SIZE) + rxPtr = 0; // length is bogus, restart search for framing + } - if (rxPtr != 0 && ptr + 1 == len + HEADER_LEN) { - // If we didn't just fail the packet and we now have the right # of bytes, parse it - handleToRadio(rxBuf + HEADER_LEN, len); - rxPtr = 0; // start over again + if (rxPtr != 0 && ptr + 1 == len + HEADER_LEN) { + rxPtr = 0; // start over again on the next packet + + // If we didn't just fail the packet and we now have the right # of bytes, parse it + if (handleToRadio(rxBuf + HEADER_LEN, len)) + return 0; // we want to be called again ASAP because we still have more work to do + } } } + + // we had packets available this time, so assume we might have them next time also + lastRxMsec = now; + return 0; } } @@ -71,7 +85,7 @@ void StreamAPI::writeStream() void StreamAPI::emitTxBuffer(size_t len) { if (len != 0) { - DEBUG_MSG("emit tx %d\n", len); + // DEBUG_MSG("emit tx %d\n", len); txBuf[0] = START1; txBuf[1] = START2; txBuf[2] = (len >> 8) & 0xff; @@ -93,6 +107,6 @@ void StreamAPI::emitRebooted() fromRadioScratch.which_payloadVariant = FromRadio_rebooted_tag; fromRadioScratch.rebooted = true; - DEBUG_MSG("Emitting reboot packet for serial shell\n"); + // DEBUG_MSG("Emitting reboot packet for serial shell\n"); emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, FromRadio_size, FromRadio_fields, &fromRadioScratch)); } \ No newline at end of file diff --git a/src/mesh/StreamAPI.h b/src/mesh/StreamAPI.h index ed0a5fbd4..6180a95d8 100644 --- a/src/mesh/StreamAPI.h +++ b/src/mesh/StreamAPI.h @@ -2,6 +2,7 @@ #include "PhoneAPI.h" #include "Stream.h" +#include "concurrency/OSThread.h" // A To/FromRadio packet + our 32 bit header #define MAX_STREAM_BUF_SIZE (MAX_TO_FROM_RADIO_SIZE + sizeof(uint32_t)) @@ -27,7 +28,7 @@ valid utf8 encoding. This makes it a bit easier to start a device outputting reg after it has received a valid packet from the PC, turn off unencoded debug printing and switch to this packet encoding. */ -class StreamAPI : public PhoneAPI +class StreamAPI : public PhoneAPI, protected concurrency::OSThread { /** * The stream we read/write from @@ -37,21 +38,23 @@ class StreamAPI : public PhoneAPI uint8_t rxBuf[MAX_STREAM_BUF_SIZE]; size_t rxPtr = 0; + /// time of last rx, used, to slow down our polling if we haven't heard from anyone + uint32_t lastRxMsec = 0; + public: - StreamAPI(Stream *_stream) : stream(_stream) {} + StreamAPI(Stream *_stream) : concurrency::OSThread("StreamAPI"), stream(_stream) {} /** * Currently we require frequent invocation from loop() to check for arrived serial packets and to send new packets to the * phone. - * FIXME, to support better power behavior instead move to a thread and block on serial reads. */ - void loop(); + virtual int32_t runOnce(); private: /** * Read any rx chars from the link and call handleToRadio */ - void readStream(); + int32_t readStream(); /** * call getFromRadio() and deliver encapsulated packets to the Stream @@ -63,7 +66,7 @@ class StreamAPI : public PhoneAPI * Send a FromRadio.rebooted = true packet to the phone */ void emitRebooted(); - + /** * Send the current txBuffer over our stream */ diff --git a/src/mesh/wifi/WiFiServerAPI.cpp b/src/mesh/wifi/WiFiServerAPI.cpp index 37183975e..c0be8bc4a 100644 --- a/src/mesh/wifi/WiFiServerAPI.cpp +++ b/src/mesh/wifi/WiFiServerAPI.cpp @@ -40,18 +40,20 @@ void WiFiServerAPI::onConnectionChanged(bool connected) } /// override close to also shutdown the TCP link -void WiFiServerAPI::close() { +void WiFiServerAPI::close() +{ client.stop(); // drop tcp connection StreamAPI::close(); } -bool WiFiServerAPI::loop() +int32_t WiFiServerAPI::runOnce() { if (client.connected()) { - StreamAPI::loop(); - return true; + return StreamAPI::runOnce(); } else { - return false; + DEBUG_MSG("Client dropped connection, suspending API service\n"); + enabled = false; // we no longer need to run + return 0; } } @@ -78,18 +80,5 @@ int32_t WiFiServerPort::runOnce() openAPI = new WiFiServerAPI(client); } - if (openAPI) { - // Allow idle processing so the API can read from its incoming stream - if(!openAPI->loop()) { - // If our API link was up, shut it down - - DEBUG_MSG("Client dropped connection, closing API client\n"); - // Note: we can't call delete here because this object includes other state - // besides the stream API. Instead kill it later when we start a new instance - delete openAPI; - openAPI = NULL; - } - return 0; // run fast while our API server is running - } else - return 100; // only check occasionally for incoming connections + return 100; // only check occasionally for incoming connections } \ No newline at end of file diff --git a/src/mesh/wifi/WiFiServerAPI.h b/src/mesh/wifi/WiFiServerAPI.h index 46e2fad93..96cfb2bb0 100644 --- a/src/mesh/wifi/WiFiServerAPI.h +++ b/src/mesh/wifi/WiFiServerAPI.h @@ -1,7 +1,6 @@ #pragma once #include "StreamAPI.h" -#include "concurrency/OSThread.h" #include /** @@ -18,15 +17,14 @@ class WiFiServerAPI : public StreamAPI virtual ~WiFiServerAPI(); - /// @return true if we want to keep running, or false if we are ready to be destroyed - virtual bool loop(); // Check for dropped client connections - /// override close to also shutdown the TCP link virtual void close(); protected: /// Hookable to find out when connection changes virtual void onConnectionChanged(bool connected); + + virtual int32_t runOnce(); // Check for dropped client connections }; /** From 0261c243e0a8cc4d75ca872223625bfae7ccbb87 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 25 Mar 2021 07:51:54 +0800 Subject: [PATCH 02/15] PhoneAPIs shouldn't register for messages until they have clients --- docs/software/TODO.md | 2 + proto | 2 +- src/SerialConsole.cpp | 1 - src/mesh/PhoneAPI.cpp | 72 ++++++++++++++++++++++-------------- src/mesh/PhoneAPI.h | 19 +++++++--- src/mesh/generated/mesh.pb.h | 5 ++- src/nimble/BluetoothUtil.cpp | 1 - 7 files changed, 64 insertions(+), 38 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 2633d3bcf..39dfa69c7 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,6 +4,8 @@ You probably don't care about this section - skip to the next one. ## before next release +* turn off bluetooth interface ENTIRELY while using serial API (was python client times out on connect sometimes) +* gps assistance from phone not working? * DONE test latest firmware update with is_router * DONE firmware OTA updates of is_router true nodes fails? * DONE add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263 diff --git a/proto b/proto index 820fa497d..e8d2a96a0 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 820fa497dfde07e129cad6955bf2f4b2b9cecebc +Subproject commit e8d2a96a00713608ba4a6a36c9bea4ce06886619 diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index 11534b1ee..137fbcf0d 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -29,7 +29,6 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port) // setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks Port.begin(SERIAL_BAUD); - StreamAPI::init(); emitRebooted(); } diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 3eaa60674..43259009a 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -17,34 +17,45 @@ PhoneAPI::PhoneAPI() {} -void PhoneAPI::init() -{ - observe(&service.fromNumChanged); -} - PhoneAPI::~PhoneAPI() { close(); } +void PhoneAPI::handleStartConfig() +{ + if (!isConnected()) { + onConnectionChanged(true); + observe(&service.fromNumChanged); + } + + // even if we were already connected - restart our state machine + state = STATE_SEND_MY_INFO; + + DEBUG_MSG("Reset nodeinfo read pointer\n"); + nodeInfoForPhone = NULL; // Don't keep returning old nodeinfos + nodeDB.resetReadPointer(); // FIXME, this read pointer should be moved out of nodeDB and into this class - because + // this will break once we have multiple instances of PhoneAPI running independently +} + void PhoneAPI::close() { - unobserve(); - state = STATE_SEND_NOTHING; - bool oldConnected = isConnected; - isConnected = false; - if (oldConnected != isConnected) - onConnectionChanged(isConnected); + if (state != STATE_SEND_NOTHING) { + state = STATE_SEND_NOTHING; + + unobserve(); + releasePhonePacket(); // Don't leak phone packets on shutdown + + onConnectionChanged(false); + } } void PhoneAPI::checkConnectionTimeout() { - if (isConnected) { + if (isConnected()) { bool newConnected = (millis() - lastContactMsec < getPref_phone_timeout_secs() * 1000L); - if (!newConnected) { - isConnected = false; - onConnectionChanged(isConnected); - } + if (!newConnected) + close(); } } @@ -55,10 +66,7 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) { powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep lastContactMsec = millis(); - if (!isConnected) { - isConnected = true; - onConnectionChanged(isConnected); - } + // return (lastContactMsec != 0) && memset(&toRadioScratch, 0, sizeof(toRadioScratch)); @@ -70,12 +78,12 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) case ToRadio_want_config_id_tag: config_nonce = toRadioScratch.want_config_id; DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce); - state = STATE_SEND_MY_INFO; - DEBUG_MSG("Reset nodeinfo read pointer\n"); - nodeInfoForPhone = NULL; // Don't keep returning old nodeinfos - nodeDB.resetReadPointer(); // FIXME, this read pointer should be moved out of nodeDB and into this class - because - // this will break once we have multiple instances of PhoneAPI running independently + handleStartConfig(); + break; + + case ToRadio_disconnect_tag: + close(); break; default: @@ -166,10 +174,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) // Encapsulate as a FromRadio packet fromRadioScratch.which_payloadVariant = FromRadio_packet_tag; fromRadioScratch.packet = *packetForPhone; - - service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore - packetForPhone = NULL; } + releasePhonePacket(); break; default: @@ -188,6 +194,16 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) return 0; } +void PhoneAPI::handleDisconnect() {} + +void PhoneAPI::releasePhonePacket() +{ + if (packetForPhone) { + service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore + packetForPhone = NULL; + } +} + /** * Return true if we have data available to send to the phone */ diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index dc7de3e18..02165aae3 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -22,6 +22,7 @@ class PhoneAPI enum State { STATE_UNUSED, // (no longer used) old default state - until Android apps are all updated, uses the old BLE API STATE_SEND_NOTHING, // (Eventual) Initial state, don't send anything until the client starts asking for config + // (disconnected) STATE_SEND_MY_INFO, // send our my info record // STATE_SEND_RADIO, // in 1.2 we now send this as a regular mesh packet // STATE_SEND_OWNER, no need to send Owner specially, it is just part of the nodedb @@ -58,9 +59,6 @@ class PhoneAPI /// Destructor - calls close() virtual ~PhoneAPI(); - /// Do late init that can't happen at constructor time - virtual void init(); - // Call this when the client drops the connection, resets the state to STATE_SEND_NOTHING // Unregisters our observer. A closed connection **can** be reopened by calling init again. virtual void close(); @@ -84,10 +82,9 @@ class PhoneAPI */ bool available(); - protected: - /// Are we currently connected to a client? - bool isConnected = false; + bool isConnected() { return state != STATE_SEND_NOTHING; } + protected: /// Our fromradio packet while it is being assembled FromRadio fromRadioScratch; @@ -102,7 +99,17 @@ class PhoneAPI */ virtual void onNowHasData(uint32_t fromRadioNum) {} + /** + * Subclasses can use this to find out when a client drops the link + */ + virtual void handleDisconnect(); + private: + void releasePhonePacket(); + + /// begin a new connection + void handleStartConfig(); + /** * Handle a packet that the phone wants us to send. We can write to it but can not keep a reference to it * @return true true if a packet was queued for sending diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index 8ef7f16f7..566efc1f0 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -191,6 +191,7 @@ typedef struct _ToRadio { union { MeshPacket packet; uint32_t want_config_id; + bool disconnect; }; } ToRadio; @@ -311,6 +312,7 @@ extern "C" { #define FromRadio_packet_tag 11 #define ToRadio_packet_tag 2 #define ToRadio_want_config_id_tag 100 +#define ToRadio_disconnect_tag 104 /* Struct field encoding specification for nanopb */ #define Position_FIELDLIST(X, a) \ @@ -423,7 +425,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 11) #define ToRadio_FIELDLIST(X, a) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 2) \ -X(a, STATIC, ONEOF, UINT32, (payloadVariant,want_config_id,want_config_id), 100) +X(a, STATIC, ONEOF, UINT32, (payloadVariant,want_config_id,want_config_id), 100) \ +X(a, STATIC, ONEOF, BOOL, (payloadVariant,disconnect,disconnect), 104) #define ToRadio_CALLBACK NULL #define ToRadio_DEFAULT NULL #define ToRadio_payloadVariant_packet_MSGTYPE MeshPacket diff --git a/src/nimble/BluetoothUtil.cpp b/src/nimble/BluetoothUtil.cpp index 1416453b1..0618c73e6 100644 --- a/src/nimble/BluetoothUtil.cpp +++ b/src/nimble/BluetoothUtil.cpp @@ -487,7 +487,6 @@ void reinitBluetooth() DEBUG_MSG("Starting bluetooth\n"); if (isFirstTime) { bluetoothPhoneAPI = new BluetoothPhoneAPI(); - bluetoothPhoneAPI->init(); } // FIXME - if waking from light sleep, only esp_nimble_hci_init? From 13cfce48fac619b98853466f68c93572c55e51c4 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 25 Mar 2021 08:54:43 +0800 Subject: [PATCH 03/15] cleanly disable bluetooth while serial API is in use (and only then) --- src/PowerFSM.cpp | 90 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 26 deletions(-) diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 86bc91639..338412c3d 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -9,6 +9,23 @@ #include "sleep.h" #include "target_specific.h" +/// Should we behave as if we have AC power now? +static bool isPowered() +{ + bool isRouter = radioConfig.preferences.is_router; + + // If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON + // We assume routers might be powered all the time, but from a low current (solar) source + bool isLowPower = radioConfig.preferences.is_low_power || isRouter; + + /* To determine if we're externally powered, assumptions + 1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead otherwise) + + 2) If we detect USB power from the power management chip, we must be getting power externally. + */ + return !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB()); +} + static void sdsEnter() { // FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw @@ -119,14 +136,34 @@ static void serialEnter() { setBluetoothEnable(false); screen->setOn(true); - screen->print("Using API...\n"); + screen->print("Serial connected\n"); +} + +static void serialExit() +{ + screen->print("Serial disconnected\n"); } static void powerEnter() { - screen->setOn(true); - setBluetoothEnable(true); - screen->print("Powered...\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"); + powerFSM.trigger(EVENT_POWER_DISCONNECTED); + } else { + screen->setOn(true); + setBluetoothEnable(true); + screen->print("Powered...\n"); + } +} + +static void powerIdle() +{ + if (!isPowered()) { + // If we got here, we are in the wrong state + DEBUG_MSG("Loss of power in Powered\n"); + powerFSM.trigger(EVENT_POWER_DISCONNECTED); + } } static void powerExit() @@ -153,6 +190,14 @@ static void onEnter() } } +static void onIdle() +{ + if (isPowered()) { + // If we got here, we are in the wrong state - we should be in powered, let that state ahndle things + powerFSM.trigger(EVENT_POWER_CONNECTED); + } +} + static void screenPress() { screen->onPress(); @@ -164,26 +209,16 @@ State stateSDS(sdsEnter, NULL, NULL, "SDS"); State stateLS(lsEnter, lsIdle, lsExit, "LS"); State stateNB(nbEnter, NULL, NULL, "NB"); State stateDARK(darkEnter, NULL, NULL, "DARK"); -State stateSERIAL(serialEnter, NULL, NULL, "SERIAL"); +State stateSERIAL(serialEnter, NULL, serialExit, "SERIAL"); State stateBOOT(bootEnter, NULL, NULL, "BOOT"); -State stateON(onEnter, NULL, NULL, "ON"); -State statePOWER(powerEnter, NULL, powerExit, "POWER"); +State stateON(onEnter, onIdle, NULL, "ON"); +State statePOWER(powerEnter, powerIdle, powerExit, "POWER"); Fsm powerFSM(&stateBOOT); void PowerFSM_setup() { bool isRouter = radioConfig.preferences.is_router; - - // If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON - // We assume routers might be powered all the time, but from a low current (solar) source - bool isLowPower = radioConfig.preferences.is_low_power || isRouter; - - /* To determine if we're externally powered, assumptions - 1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead otherwise) - - 2) If we detect USB power from the power management chip, we must be getting power externally. - */ - bool hasPower = !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB()); + bool hasPower = isPowered(); DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower); powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout"); @@ -231,22 +266,25 @@ void PowerFSM_setup() powerFSM.add_transition(&stateON, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text"); // restarts the sleep timer } + // If we are not in statePOWER but get a serial connection, suppress sleep (and keep the screen on) while connected powerFSM.add_transition(&stateLS, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); powerFSM.add_transition(&stateNB, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); powerFSM.add_transition(&stateDARK, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); powerFSM.add_transition(&stateON, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); + powerFSM.add_transition(&statePOWER, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); - if (!isLowPower) { - powerFSM.add_transition(&stateLS, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); - powerFSM.add_transition(&stateNB, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); - powerFSM.add_transition(&stateDARK, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); - powerFSM.add_transition(&stateON, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); - } + // If we get power connected, go to the power connect state + powerFSM.add_transition(&stateLS, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); + powerFSM.add_transition(&stateNB, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); + powerFSM.add_transition(&stateDARK, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); + powerFSM.add_transition(&stateON, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); powerFSM.add_transition(&statePOWER, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected"); - powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected"); + // powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected"); - powerFSM.add_transition(&stateSERIAL, &stateNB, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect"); + // the only way to leave state serial is for the client to disconnect (or we timeout and force disconnect them) + // when we leave, go to ON (which might not be the correct state if we have power connected, we will fix that in onEnter) + powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect"); powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone"); From 04225f7bc29c7d8834f245863cd0eff9237b3dde Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Fri, 26 Mar 2021 09:30:15 +0800 Subject: [PATCH 04/15] change! time of last packet rx in node->last_heard instead of node->position.time --- docs/software/TODO.md | 9 ++++++--- src/mesh/MeshService.cpp | 2 +- src/mesh/NodeDB.cpp | 11 ++++------- src/mesh/generated/deviceonly.pb.h | 2 +- src/mesh/generated/mesh.pb.h | 9 ++++++--- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 39dfa69c7..1571caeff 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,13 +4,16 @@ You probably don't care about this section - skip to the next one. ## before next release -* turn off bluetooth interface ENTIRELY while using serial API (was python client times out on connect sometimes) -* gps assistance from phone not working? +* split position.time and last_heard +* update android app to use last_heard +* have android fill in if local GPS has poor signal +* DONE turn off bluetooth interface ENTIRELY while using serial API (was python client times out on connect sometimes) +* DONE gps assistance from phone not working? * DONE test latest firmware update with is_router * DONE firmware OTA updates of is_router true nodes fails? * DONE add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263 * DONE TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752 -* DIBE bug report with remote info request timing out +* DONE bug report with remote info request timing out * DONE retest channel changing in android (using sim?) * DONE move remote admin doc from forum into git * DONE check crashlytics diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index e270b5a2c..bc14d057a 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -198,7 +198,7 @@ NodeInfo *MeshService::refreshMyNodeInfo() Position &position = node->position; // Update our local node info with our position (even if we don't decide to update anyone else) - position.time = + node->last_heard = getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid position.battery_level = powerStatus->getBatteryChargePercent(); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index bc71dab3d..aa346aca4 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -408,8 +408,7 @@ uint32_t sinceLastSeen(const NodeInfo *n) { uint32_t now = getTime(); - uint32_t last_seen = n->position.time; - int delta = (int)(now - last_seen); + int delta = (int)(now - n->last_heard); if (delta < 0) // our clock must be slightly off still - not set from GPS yet delta = 0; @@ -443,7 +442,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p) // Be careful to only update fields that have been set by the sender // A lot of position reports don't have time populated. In that case, be careful to not blow away the time we // recorded based on the packet rxTime - if (!info->position.time && p.time) + if (p.time) info->position.time = p.time; if (p.battery_level) info->position.battery_level = p.battery_level; @@ -493,10 +492,8 @@ void NodeDB::updateFrom(const MeshPacket &mp) NodeInfo *info = getOrCreateNode(getFrom(&mp)); - if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen - info->has_position = true; // at least the time is valid - info->position.time = mp.rx_time; - } + if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard + info->last_heard = mp.rx_time; if (mp.rx_snr) info->snr = mp.rx_snr; // keep the most recent SNR we received for this node. diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h index ababb0e0c..2a9082f69 100644 --- a/src/mesh/generated/deviceonly.pb.h +++ b/src/mesh/generated/deviceonly.pb.h @@ -125,7 +125,7 @@ extern const pb_msgdesc_t ChannelFile_msg; /* Maximum encoded size of messages (where known) */ #define LegacyRadioConfig_size 4 #define LegacyRadioConfig_LegacyPreferences_size 2 -#define DeviceState_size 4920 +#define DeviceState_size 5112 #define ChannelFile_size 832 #ifdef __cplusplus diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index 566efc1f0..bd59b8b9b 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -161,6 +161,7 @@ typedef struct _NodeInfo { User user; bool has_position; Position position; + uint32_t last_heard; float snr; } NodeInfo; @@ -233,7 +234,7 @@ extern "C" { #define Routing_init_default {0, {RouteDiscovery_init_default}} #define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0} #define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0} -#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0} +#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0} #define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0} #define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN} #define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}} @@ -244,7 +245,7 @@ extern "C" { #define Routing_init_zero {0, {RouteDiscovery_init_zero}} #define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0} #define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0} -#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0} +#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0} #define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0} #define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN} #define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}} @@ -299,6 +300,7 @@ extern "C" { #define NodeInfo_num_tag 1 #define NodeInfo_user_tag 2 #define NodeInfo_position_tag 3 +#define NodeInfo_last_heard_tag 4 #define NodeInfo_snr_tag 7 #define Routing_route_request_tag 1 #define Routing_route_reply_tag 2 @@ -378,6 +380,7 @@ X(a, STATIC, SINGULAR, INT32, rx_rssi, 13) X(a, STATIC, SINGULAR, UINT32, num, 1) \ X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \ X(a, STATIC, OPTIONAL, MESSAGE, position, 3) \ +X(a, STATIC, SINGULAR, FIXED32, last_heard, 4) \ X(a, STATIC, SINGULAR, FLOAT, snr, 7) #define NodeInfo_CALLBACK NULL #define NodeInfo_DEFAULT NULL @@ -463,7 +466,7 @@ extern const pb_msgdesc_t ToRadio_msg; #define Routing_size 42 #define Data_size 260 #define MeshPacket_size 309 -#define NodeInfo_size 126 +#define NodeInfo_size 131 #define MyNodeInfo_size 89 #define LogRecord_size 81 #define FromRadio_size 318 From 7e9e33d462f7d706aab5070c11723ecd84bbc7ac Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Fri, 26 Mar 2021 09:30:33 +0800 Subject: [PATCH 05/15] fix has_gps reporting to phones --- src/mesh/PhoneAPI.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 43259009a..db039afb2 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -129,9 +129,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) case STATE_SEND_MY_INFO: // If the user has specified they don't want our node to share its location, make sure to tell the phone // app not to send locations on our behalf. - myNodeInfo.has_gps = (radioConfig.preferences.location_share == LocationSharing_LocDisabled) - ? true - : (gps && gps->isConnected()); // Update with latest GPS connect info + myNodeInfo.has_gps = gps && gps->isConnected(); // Update with latest GPS connect info fromRadioScratch.which_payloadVariant = FromRadio_my_info_tag; fromRadioScratch.my_info = myNodeInfo; state = STATE_SEND_NODEINFO; @@ -144,7 +142,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) nodeInfoForPhone = NULL; // We just consumed a nodeinfo, will need a new one next time if (info) { - DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id, + DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->last_heard, info->user.id, info->user.long_name); fromRadioScratch.which_payloadVariant = FromRadio_node_info_tag; fromRadioScratch.node_info = *info; @@ -168,7 +166,6 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) case STATE_SEND_PACKETS: // Do we have a message from the mesh? if (packetForPhone) { - printPacket("phone downloaded packet", packetForPhone); // Encapsulate as a FromRadio packet From 8ffd5a1d4f78728f2eaaa7ed7b3b935b7d2daa45 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 27 Mar 2021 10:19:59 +0800 Subject: [PATCH 06/15] add reboot message --- proto | 2 +- src/esp32/BluetoothSoftwareUpdate.cpp | 9 --------- src/esp32/BluetoothSoftwareUpdate.h | 2 -- src/esp32/main-esp32.cpp | 3 +-- src/main.cpp | 15 +++++++++++++++ src/main.h | 2 ++ src/mesh/generated/admin.pb.h | 5 ++++- src/plugins/AdminPlugin.cpp | 7 +++++++ 8 files changed, 30 insertions(+), 15 deletions(-) diff --git a/proto b/proto index e8d2a96a0..f9c4f8758 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit e8d2a96a00713608ba4a6a36c9bea4ce06886619 +Subproject commit f9c4f875818c9aa6995e6e25803d52557a1779c7 diff --git a/src/esp32/BluetoothSoftwareUpdate.cpp b/src/esp32/BluetoothSoftwareUpdate.cpp index 843b14cf6..870d20ffd 100644 --- a/src/esp32/BluetoothSoftwareUpdate.cpp +++ b/src/esp32/BluetoothSoftwareUpdate.cpp @@ -16,7 +16,6 @@ int16_t updateResultHandle = -1; static CRC32 crc; -static uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes) static uint32_t updateExpectedSize, updateActualSize; static uint8_t update_result; @@ -139,14 +138,6 @@ int update_region_callback(uint16_t conn_handle, uint16_t attr_handle, struct bl return chr_readwrite8(&update_region, sizeof(update_region), ctxt); } -void bluetoothRebootCheck() -{ - if (rebootAtMsec && millis() > rebootAtMsec) { - DEBUG_MSG("Rebooting for update\n"); - ESP.restart(); - } -} - /* See bluetooth-api.md diff --git a/src/esp32/BluetoothSoftwareUpdate.h b/src/esp32/BluetoothSoftwareUpdate.h index f5e1a1ded..478d478d5 100644 --- a/src/esp32/BluetoothSoftwareUpdate.h +++ b/src/esp32/BluetoothSoftwareUpdate.h @@ -4,8 +4,6 @@ void reinitUpdateService(); -void bluetoothRebootCheck(); - #ifdef __cplusplus extern "C" { #endif diff --git a/src/esp32/main-esp32.cpp b/src/esp32/main-esp32.cpp index 0e60fba83..856f89cc6 100644 --- a/src/esp32/main-esp32.cpp +++ b/src/esp32/main-esp32.cpp @@ -7,9 +7,9 @@ #include "sleep.h" #include "target_specific.h" #include "utils.h" +#include #include #include -#include void getMacAddr(uint8_t *dmac) { @@ -84,7 +84,6 @@ void esp32Loop() { esp_task_wdt_reset(); // service our app level watchdog loopBLE(); - bluetoothRebootCheck(); // for debug printing // radio.radioIf.canSleep(); diff --git a/src/main.cpp b/src/main.cpp index 1621d081f..7ce86ffc0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -576,6 +576,20 @@ Periodic axpDebugOutput(axpDebugRead); axpDebugOutput.setup(); #endif +uint32_t rebootAtMsec; // If not zero we will reboot at this time (used to reboot shortly after the update completes) + +void rebootCheck() +{ + if (rebootAtMsec && millis() > rebootAtMsec) { +#ifndef NO_ESP32 + DEBUG_MSG("Rebooting for update\n"); + ESP.restart(); +#else + DEBUG_MSG("FIXME implement reboot for this platform"); +#endif + } +} + void loop() { // axpDebugOutput.loop(); @@ -588,6 +602,7 @@ void loop() #ifdef NRF52_SERIES nrf52Loop(); #endif + rebootCheck(); // For debugging // if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving(); diff --git a/src/main.h b/src/main.h index edb50d0d7..1a6dfe439 100644 --- a/src/main.h +++ b/src/main.h @@ -20,4 +20,6 @@ extern graphics::Screen *screen; // Return a human readable string of the form "Meshtastic_ab13" const char *getDeviceName(); +extern uint32_t rebootAtMsec; + void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(); diff --git a/src/mesh/generated/admin.pb.h b/src/mesh/generated/admin.pb.h index cf71a9cb2..a2e753546 100644 --- a/src/mesh/generated/admin.pb.h +++ b/src/mesh/generated/admin.pb.h @@ -26,6 +26,7 @@ typedef struct _AdminMessage { bool confirm_set_channel; bool confirm_set_radio; bool exit_simulator; + int32_t reboot_seconds; }; } AdminMessage; @@ -49,6 +50,7 @@ extern "C" { #define AdminMessage_confirm_set_channel_tag 32 #define AdminMessage_confirm_set_radio_tag 33 #define AdminMessage_exit_simulator_tag 34 +#define AdminMessage_reboot_seconds_tag 35 /* Struct field encoding specification for nanopb */ #define AdminMessage_FIELDLIST(X, a) \ @@ -61,7 +63,8 @@ X(a, STATIC, ONEOF, UINT32, (variant,get_channel_request,get_channel_requ X(a, STATIC, ONEOF, MESSAGE, (variant,get_channel_response,get_channel_response), 7) \ X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_channel,confirm_set_channel), 32) \ X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_radio,confirm_set_radio), 33) \ -X(a, STATIC, ONEOF, BOOL, (variant,exit_simulator,exit_simulator), 34) +X(a, STATIC, ONEOF, BOOL, (variant,exit_simulator,exit_simulator), 34) \ +X(a, STATIC, ONEOF, INT32, (variant,reboot_seconds,reboot_seconds), 35) #define AdminMessage_CALLBACK NULL #define AdminMessage_DEFAULT NULL #define AdminMessage_variant_set_radio_MSGTYPE RadioConfig diff --git a/src/plugins/AdminPlugin.cpp b/src/plugins/AdminPlugin.cpp index 69e96875c..30016484e 100644 --- a/src/plugins/AdminPlugin.cpp +++ b/src/plugins/AdminPlugin.cpp @@ -77,6 +77,13 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag handleGetRadio(mp); break; + case AdminMessage_reboot_seconds_tag: { + int32_t s = r->reboot_seconds; + DEBUG_MSG("Rebooting in %d seconds\n", s); + rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); + break; + } + #ifdef PORTDUINO case AdminMessage_exit_simulator_tag: DEBUG_MSG("Exiting simulator\n"); From 78f2c656d0bda5e7ac454dac3bf6d49cc6d91ee1 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 27 Mar 2021 11:21:43 +0800 Subject: [PATCH 07/15] fix nrf52 builds --- src/nrf52/NRF52Bluetooth.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/nrf52/NRF52Bluetooth.cpp b/src/nrf52/NRF52Bluetooth.cpp index bcd74e10c..e67607c94 100644 --- a/src/nrf52/NRF52Bluetooth.cpp +++ b/src/nrf52/NRF52Bluetooth.cpp @@ -2,9 +2,9 @@ #include "BluetoothCommon.h" #include "configuration.h" #include "main.h" -#include -#include "mesh/mesh-pb-constants.h" #include "mesh/PhoneAPI.h" +#include "mesh/mesh-pb-constants.h" +#include static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16)); static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16)); @@ -155,7 +155,6 @@ void fromNumAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt void setupMeshService(void) { bluetoothPhoneAPI = new BluetoothPhoneAPI(); - bluetoothPhoneAPI->init(); meshBleService.begin(); From 1e3b037feaf299e7650464781cc88e1564319193 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 27 Mar 2021 16:17:01 +0800 Subject: [PATCH 08/15] populate position.time for broadcast positions --- docs/software/TODO.md | 10 +++++++--- src/mesh/MeshService.cpp | 5 ++++- src/plugins/PositionPlugin.cpp | 6 ++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 1571caeff..33d1b97b6 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,9 +4,13 @@ You probably don't care about this section - skip to the next one. ## before next release -* split position.time and last_heard -* update android app to use last_heard -* have android fill in if local GPS has poor signal +* DONE have android fill in if local GPS has poor signal +* release +* fix heltec battery scaling +* add reference counting to mesh packets +* allow multiple simultanteous phoneapi connections +* DONE split position.time and last_heard +* DONE update android app to use last_heard * DONE turn off bluetooth interface ENTIRELY while using serial API (was python client times out on connect sometimes) * DONE gps assistance from phone not working? * DONE test latest firmware update with is_router diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index bc14d057a..4a42f7716 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -197,10 +197,13 @@ NodeInfo *MeshService::refreshMyNodeInfo() Position &position = node->position; - // Update our local node info with our position (even if we don't decide to update anyone else) + // Update our local node info with our time (even if we don't decide to update anyone else) node->last_heard = getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid + // For the time in the position field, only set that if we have a real GPS clock + position.time = getValidTime(RTCQualityGPS); + position.battery_level = powerStatus->getBatteryChargePercent(); updateBatteryLevel(position.battery_level); diff --git a/src/plugins/PositionPlugin.cpp b/src/plugins/PositionPlugin.cpp index b6f76ad72..2aaa31a5e 100644 --- a/src/plugins/PositionPlugin.cpp +++ b/src/plugins/PositionPlugin.cpp @@ -10,10 +10,8 @@ PositionPlugin *positionPlugin; PositionPlugin::PositionPlugin() : ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields), concurrency::OSThread("PositionPlugin") { - isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others - setIntervalFromNow(60 * - 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup) - + isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others + 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) From 890ec7bdb25cff6cd7895c24e50a41b0c9dac313 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 27 Mar 2021 17:19:15 +0800 Subject: [PATCH 09/15] doc update --- docs/software/TODO.md | 1 - proto | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 33d1b97b6..5c09e9b54 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -5,7 +5,6 @@ You probably don't care about this section - skip to the next one. ## before next release * DONE have android fill in if local GPS has poor signal -* release * fix heltec battery scaling * add reference counting to mesh packets * allow multiple simultanteous phoneapi connections diff --git a/proto b/proto index 1f56681b2..0ea232802 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 1f56681b2b629a92a099784f78cd11dff31a5a8f +Subproject commit 0ea232802651fd6aaa53c93c09f4c2eb36470dd0 From 9eb9c473db7f83bf97bcc381794beee8c513744e Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 28 Mar 2021 11:43:28 +0800 Subject: [PATCH 10/15] add note about credit! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c33d0f45..7bbed4283 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ We'd love to have you join us on this merry little project. Please see our [deve # Credits -This project is run by volunteers. Past contributors include: +This project is run by volunteers. We are a friendly group and welcome any contribution (code fixes, documentation, features, bug reports etc...). We try to be good about listing contributor names in release notes, but it has become unwieldy for the main-devs to keep updating the list below and we've neglected it too long. If you'd like your name included in this list please send a pull request to edit this README and simply add your line yourself. Thank you very much for your help! - @astro-arphid: Added support for 433MHz radios in europe. - @claesg: Various documentation fixes and 3D print enclosures From 4e87c4411c836fc26c68f64cbc23409471faa9e9 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 28 Mar 2021 11:44:19 +0800 Subject: [PATCH 11/15] fix serious nak bug reported by @havealoha and @luxoon --- src/mesh/MeshPlugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 2081def6e..e97836c09 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -36,6 +36,7 @@ MeshPacket *MeshPlugin::allocAckNak(Routing_Error err, NodeNum to, PacketId idFr Routing c = Routing_init_default; c.error_reason = err; + c.which_variant = Routing_error_reason_tag; // Now that we have moded sendAckNak up one level into the class heirarchy we can no longer assume we are a RoutingPlugin // So we manually call pb_encode_to_bytes and specify routing port number From fc965003296788501c98826cabbc90bdd6b2f9c0 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 28 Mar 2021 12:06:16 +0800 Subject: [PATCH 12/15] fix unused prefs field --- src/mesh/http/ContentHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 6bd7b8dd9..9f55e9c36 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -840,7 +840,7 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) Preferences preferences; preferences.begin("meshtastic", false); - uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0); + // uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0); if (!params->getQueryParameter("content", content)) { content = "json"; From c7f411fc7c5cbd2e5e90d384c87308b5571d53ce Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 28 Mar 2021 12:07:43 +0800 Subject: [PATCH 13/15] remove unused Preferences code (cc @mc-hamster for review) (noticed because of a compiler warning) --- src/mesh/http/ContentHandler.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 9f55e9c36..851f06abe 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #ifndef NO_ESP32 @@ -837,11 +836,6 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) ResourceParameters *params = req->getParams(); std::string content; - Preferences preferences; - preferences.begin("meshtastic", false); - - // uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0); - if (!params->getQueryParameter("content", content)) { content = "json"; } From 525fe9b96cd527f109c256d26e438c234775ec59 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 28 Mar 2021 12:16:37 +0800 Subject: [PATCH 14/15] dramatically speed up message RX in some cases (we were sleeping much too long) --- docs/software/TODO.md | 1 + src/concurrency/NotifiedWorkerThread.cpp | 2 ++ src/graphics/Screen.cpp | 2 ++ src/main.cpp | 9 ++++++++- src/main.h | 4 ++++ src/mesh/ReliableRouter.h | 6 +++--- src/mesh/Router.cpp | 5 +++++ 7 files changed, 25 insertions(+), 4 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 5c09e9b54..828f4f933 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,6 +4,7 @@ You probably don't care about this section - skip to the next one. ## before next release +* DONE naks are being dropped (though enqueuedLocal) sometimes before phone/PC gets them * DONE have android fill in if local GPS has poor signal * fix heltec battery scaling * add reference counting to mesh packets diff --git a/src/concurrency/NotifiedWorkerThread.cpp b/src/concurrency/NotifiedWorkerThread.cpp index aaac07f59..78c15a6d0 100644 --- a/src/concurrency/NotifiedWorkerThread.cpp +++ b/src/concurrency/NotifiedWorkerThread.cpp @@ -1,5 +1,6 @@ #include "NotifiedWorkerThread.h" #include "configuration.h" +#include "main.h" #include namespace concurrency @@ -28,6 +29,7 @@ IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite) if (overwrite || notification == 0) { enabled = true; setInterval(0); // Run ASAP + runASAP = true; notification = v; if (debugNotification) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 9698f41b1..c7580f02d 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -717,6 +717,7 @@ void Screen::handleSetOn(bool on) dispdev.displayOn(); enabled = true; setInterval(0); // Draw ASAP + runASAP = true; } else { DEBUG_MSG("Turning off screen\n"); dispdev.displayOff(); @@ -1053,6 +1054,7 @@ void Screen::setFastFramerate() targetFramerate = SCREEN_TRANSITION_FRAMERATE; ui.setTargetFPS(targetFramerate); setInterval(0); // redraw ASAP + runASAP = true; } void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) diff --git a/src/main.cpp b/src/main.cpp index 7ce86ffc0..ba99c88aa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -590,8 +590,14 @@ void rebootCheck() } } +// If a thread does something that might need for it to be rescheduled ASAP it can set this flag +// This will supress the current delay and instead try to run ASAP. +bool runASAP; + void loop() { + runASAP = false; + // axpDebugOutput.loop(); // heap_caps_check_integrity_all(true); // FIXME - disable this expensive check @@ -627,6 +633,7 @@ void loop() mainController.nextThread->tillRun(millis())); */ // We want to sleep as long as possible here - because it saves power - mainDelay.delay(delayMsec); + if (!runASAP) + mainDelay.delay(delayMsec); // if (didWake) DEBUG_MSG("wake!\n"); } diff --git a/src/main.h b/src/main.h index 1a6dfe439..0c5cb349c 100644 --- a/src/main.h +++ b/src/main.h @@ -22,4 +22,8 @@ const char *getDeviceName(); extern uint32_t rebootAtMsec; +// If a thread does something that might need for it to be rescheduled ASAP it can set this flag +// This will supress the current delay and instead try to run ASAP. +extern bool runASAP; + void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(); diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h index db161b984..7299d1c18 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -79,9 +79,10 @@ class ReliableRouter : public FloodingRouter /** Do our retransmission handling */ virtual int32_t runOnce() { - auto d = FloodingRouter::runOnce(); + // Note: We must doRetransmissions FIRST, because it might queue up work for the base class runOnce implementation + auto d = doRetransmissions(); - int32_t r = doRetransmissions(); + int32_t r = FloodingRouter::runOnce(); return min(d, r); } @@ -109,7 +110,6 @@ class ReliableRouter : public FloodingRouter PendingPacket *startRetransmission(MeshPacket *p); private: - /** * Stop any retransmissions we are doing of the specified node/packet ID pair * diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index fe207de1e..5582b77d3 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -4,6 +4,7 @@ #include "NodeDB.h" #include "RTC.h" #include "configuration.h" +#include "main.h" #include "mesh-pb-constants.h" #include "plugins/RoutingPlugin.h" @@ -55,9 +56,11 @@ int32_t Router::runOnce() { MeshPacket *mp; while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) { + // printPacket("handle fromRadioQ", mp); perhapsHandleReceived(mp); } + // DEBUG_MSG("sleeping forever!\n"); return INT32_MAX; // Wait a long time - until we get woken for the message queue } @@ -117,7 +120,9 @@ void Router::abortSendAndNak(Routing_Error err, MeshPacket *p) void Router::setReceivedMessage() { + // DEBUG_MSG("set interval to ASAP\n"); setInterval(0); // Run ASAP, so we can figure out our correct sleep time + runASAP = true; } ErrorCode Router::sendLocal(MeshPacket *p) From 7e600787912a714d4b258da92e509b3189866613 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 28 Mar 2021 12:19:49 +0800 Subject: [PATCH 15/15] 1.2.17 --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 71cb4d0eb..6a086e6a3 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 1 minor = 2 -build = 16 +build = 17