diff --git a/bin/version.sh b/bin/version.sh index 4b3996936..befa884d6 100644 --- a/bin/version.sh +++ b/bin/version.sh @@ -1,3 +1,3 @@ -export VERSION=0.6.8 \ No newline at end of file +export VERSION=0.7.4 \ No newline at end of file diff --git a/docs/software/power.md b/docs/software/power.md index 84c397519..cd0d2c913 100644 --- a/docs/software/power.md +++ b/docs/software/power.md @@ -36,6 +36,10 @@ From lower to higher power consumption. onEntry: setBluetoothOn(true), screen.setOn(true) onExit: screen.setOn(false) +- serial API usage (SERIAL) - Screen is on, device doesn't sleep, bluetooth off + onEntry: setBluetooth off, screen on + onExit: + ## Behavior ### events that increase CPU activity @@ -51,9 +55,11 @@ From lower to higher power consumption. - While in DARK/ON: If we receive EVENT_BLUETOOTH_PAIR we transition to ON and start our screen_on_secs timeout - While in NB/DARK/ON: If we receive EVENT_NODEDB_UPDATED we transition to ON (so the new screen can be shown) - While in DARK: While the phone talks to us over BLE (EVENT_CONTACT_FROM_PHONE) reset any sleep timers and stay in DARK (needed for bluetooth sw update and nice user experience if the user is reading/replying to texts) +- while in LS/NB/DARK: if SERIAL_CONNECTED, go to serial ### events that decrease cpu activity +- While in SERIAL: if SERIAL_DISCONNECTED, go to NB - While in ON: If PRESS event occurs, reset screen_on_secs timer and tell the screen to handle the pess - While in ON: If it has been more than screen_on_secs since a press, lower to DARK - While in DARK: If time since last contact by our phone exceeds phone_timeout_secs (15 minutes), we transition down into NB mode diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 22b2ccd72..1e9adbec9 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -104,6 +104,12 @@ static void darkEnter() screen.setOn(false); } +static void serialEnter() +{ + setBluetoothEnable(false); + screen.setOn(true); +} + static void onEnter() { screen.setOn(true); @@ -133,6 +139,7 @@ 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 stateBOOT(bootEnter, NULL, NULL, "BOOT"); State stateON(onEnter, NULL, NULL, "ON"); Fsm powerFSM(&stateBOOT); @@ -148,7 +155,7 @@ void PowerFSM_setup() powerFSM.add_transition(&stateNB, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet, resetting win wake"); - // Handle press events + // Handle press events - note: we ignore button presses when in API mode powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL, "Press"); powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, NULL, "Press"); powerFSM.add_transition(&stateDARK, &stateON, EVENT_PRESS, NULL, "Press"); @@ -160,6 +167,7 @@ void PowerFSM_setup() powerFSM.add_transition(&stateNB, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); powerFSM.add_transition(&stateDARK, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); powerFSM.add_transition(&stateON, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); + powerFSM.add_transition(&stateSERIAL, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); powerFSM.add_transition(&stateDARK, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing"); powerFSM.add_transition(&stateON, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing"); @@ -173,6 +181,13 @@ void PowerFSM_setup() powerFSM.add_transition(&stateDARK, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text"); powerFSM.add_transition(&stateON, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text"); // restarts the sleep timer + 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(&stateSERIAL, &stateNB, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect"); + powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone"); powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone"); diff --git a/src/PowerFSM.h b/src/PowerFSM.h index ecaea70ac..c89ad9148 100644 --- a/src/PowerFSM.h +++ b/src/PowerFSM.h @@ -14,6 +14,8 @@ #define EVENT_NODEDB_UPDATED 8 // NodeDB has a big enough change that we think you should turn on the screen #define EVENT_CONTACT_FROM_PHONE 9 // the phone just talked to us over bluetooth #define EVENT_LOW_BATTERY 10 // Battery is critically low, go to sleep +#define EVENT_SERIAL_CONNECTED 11 +#define EVENT_SERIAL_DISCONNECTED 12 extern Fsm powerFSM; diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index 9e84a958f..683886ed5 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -1,4 +1,5 @@ #include "SerialConsole.h" +#include "PowerFSM.h" #include "configuration.h" #include @@ -26,12 +27,19 @@ void SerialConsole::init() */ void SerialConsole::handleToRadio(const uint8_t *buf, size_t len) { - // Note: for the time being we could _allow_ debug printing to keep going out the console - // I _think_ this is okay because we currently only print debug msgs from loop() and we are only - // dispatching serial protobuf msgs from loop() as well. When things are more threaded in the future this - // will need to change. - // setDestination(&noopPrint); + // Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets + setDestination(&noopPrint); canWrite = true; StreamAPI::handleToRadio(buf, len); +} + +/// Hookable to find out when connection changes +void SerialConsole::onConnectionChanged(bool connected) +{ + if (connected) { // To prevent user confusion, turn off bluetooth while using the serial port api + powerFSM.trigger(EVENT_SERIAL_CONNECTED); + } else { + powerFSM.trigger(EVENT_SERIAL_DISCONNECTED); + } } \ No newline at end of file diff --git a/src/SerialConsole.h b/src/SerialConsole.h index b39eda23b..17cb16943 100644 --- a/src/SerialConsole.h +++ b/src/SerialConsole.h @@ -26,6 +26,10 @@ class SerialConsole : public StreamAPI, public RedirectablePrint RedirectablePrint::write('\r'); return RedirectablePrint::write(c); } + + protected: + /// Hookable to find out when connection changes + virtual void onConnectionChanged(bool connected); }; extern SerialConsole console; diff --git a/src/esp32/BluetoothSoftwareUpdate.cpp b/src/esp32/BluetoothSoftwareUpdate.cpp index 0f56cecaa..f5f98a47f 100644 --- a/src/esp32/BluetoothSoftwareUpdate.cpp +++ b/src/esp32/BluetoothSoftwareUpdate.cpp @@ -30,8 +30,6 @@ class TotalSizeCharacteristic : public CallbackCharacteristic void onWrite(BLECharacteristic *c) { - BLEKeepAliveCallbacks::onWrite(c); - LockGuard g(updateLock); // Check if there is enough to OTA Update uint32_t len = getValue32(c, 0); @@ -67,8 +65,6 @@ class DataCharacteristic : public CallbackCharacteristic void onWrite(BLECharacteristic *c) { - BLEKeepAliveCallbacks::onWrite(c); - LockGuard g(updateLock); std::string value = c->getValue(); uint32_t len = value.length(); @@ -92,8 +88,6 @@ class CRC32Characteristic : public CallbackCharacteristic void onWrite(BLECharacteristic *c) { - BLEKeepAliveCallbacks::onWrite(c); - LockGuard g(updateLock); uint32_t expectedCRC = getValue32(c, 0); uint32_t actualCRC = crc.finalize(); diff --git a/src/esp32/CallbackCharacteristic.h b/src/esp32/CallbackCharacteristic.h index daec1d500..9c4f59a05 100644 --- a/src/esp32/CallbackCharacteristic.h +++ b/src/esp32/CallbackCharacteristic.h @@ -1,33 +1,12 @@ #pragma once -#include "PowerFSM.h" // FIXME - someday I want to make this OTA thing a separate lb at at that point it can't touch this #include "BLECharacteristic.h" - -/** - * This mixin just lets the power management state machine know the phone is still talking to us - */ -class BLEKeepAliveCallbacks : public BLECharacteristicCallbacks -{ -public: - void onRead(BLECharacteristic *c) - { - powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); - } - - void onWrite(BLECharacteristic *c) - { - powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); - } -}; +#include "PowerFSM.h" // FIXME - someday I want to make this OTA thing a separate lb at at that point it can't touch this /** * A characterstic with a set of overridable callbacks */ -class CallbackCharacteristic : public BLECharacteristic, public BLEKeepAliveCallbacks +class CallbackCharacteristic : public BLECharacteristic, public BLECharacteristicCallbacks { -public: - CallbackCharacteristic(const char *uuid, uint32_t btprops) - : BLECharacteristic(uuid, btprops) - { - setCallbacks(this); - } + public: + CallbackCharacteristic(const char *uuid, uint32_t btprops) : BLECharacteristic(uuid, btprops) { setCallbacks(this); } }; diff --git a/src/esp32/MeshBluetoothService.cpp b/src/esp32/MeshBluetoothService.cpp index ecda8bf40..3e803ad66 100644 --- a/src/esp32/MeshBluetoothService.cpp +++ b/src/esp32/MeshBluetoothService.cpp @@ -23,9 +23,6 @@ static CallbackCharacteristic *meshFromNumCharacteristic; BLEService *meshService; -// If defined we will also support the old API -#define SUPPORT_OLD_BLE_API - class BluetoothPhoneAPI : public PhoneAPI { /** @@ -58,17 +55,12 @@ class ProtobufCharacteristic : public CallbackCharacteristic void onRead(BLECharacteristic *c) { - BLEKeepAliveCallbacks::onRead(c); size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), fields, my_struct); DEBUG_MSG("pbread from %s returns %d bytes\n", c->getUUID().toString().c_str(), numbytes); c->setValue(trBytes, numbytes); } - void onWrite(BLECharacteristic *c) - { - BLEKeepAliveCallbacks::onWrite(c); - writeToDest(c, my_struct); - } + void onWrite(BLECharacteristic *c) { writeToDest(c, my_struct); } protected: /// like onWrite, but we provide an different destination to write to, for use by subclasses that @@ -83,112 +75,6 @@ class ProtobufCharacteristic : public CallbackCharacteristic } }; -#ifdef SUPPORT_OLD_BLE_API -class NodeInfoCharacteristic : public BLECharacteristic, public BLEKeepAliveCallbacks -{ - public: - NodeInfoCharacteristic() - : BLECharacteristic("d31e02e0-c8ab-4d3f-9cc9-0b8466bdabe8", - BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ) - { - setCallbacks(this); - } - - void onRead(BLECharacteristic *c) - { - BLEKeepAliveCallbacks::onRead(c); - - const NodeInfo *info = nodeDB.readNextInfo(); - - if (info) { - DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id, - info->user.long_name); - size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), NodeInfo_fields, info); - c->setValue(trBytes, numbytes); - } else { - c->setValue(trBytes, 0); // Send an empty response - DEBUG_MSG("Done sending nodeinfos\n"); - } - } - - void onWrite(BLECharacteristic *c) - { - BLEKeepAliveCallbacks::onWrite(c); - DEBUG_MSG("Reset nodeinfo read pointer\n"); - nodeDB.resetReadPointer(); - } -}; - -// wrap our protobuf version with something that forces the service to reload the config -class RadioCharacteristic : public ProtobufCharacteristic -{ - public: - RadioCharacteristic() - : ProtobufCharacteristic("b56786c8-839a-44a1-b98e-a1724c4a0262", - BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ, RadioConfig_fields, - &radioConfig) - { - } - - void onRead(BLECharacteristic *c) - { - DEBUG_MSG("Reading radio config, sdsecs %u\n", radioConfig.preferences.sds_secs); - ProtobufCharacteristic::onRead(c); - } - - void onWrite(BLECharacteristic *c) - { - DEBUG_MSG("Writing radio config\n"); - ProtobufCharacteristic::onWrite(c); - bluetoothPhoneAPI->handleSetRadio(radioConfig); - } -}; - -// wrap our protobuf version with something that forces the service to reload the owner -class OwnerCharacteristic : public ProtobufCharacteristic -{ - public: - OwnerCharacteristic() - : ProtobufCharacteristic("6ff1d8b6-e2de-41e3-8c0b-8fa384f64eb6", - BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ, User_fields, &owner) - { - } - - void onWrite(BLECharacteristic *c) - { - BLEKeepAliveCallbacks::onWrite( - c); // NOTE: We do not call the standard ProtobufCharacteristic superclass, because we want custom write behavior - - static User o; // if the phone doesn't set ID we are careful to keep ours, we also always keep our macaddr - if (writeToDest(c, &o)) { - bluetoothPhoneAPI->handleSetOwner(o); - } - } -}; - -class MyNodeInfoCharacteristic : public ProtobufCharacteristic -{ - public: - MyNodeInfoCharacteristic() - : ProtobufCharacteristic("ea9f3f82-8dc4-4733-9452-1f6da28892a2", BLECharacteristic::PROPERTY_READ, MyNodeInfo_fields, - &myNodeInfo) - { - } - - void onRead(BLECharacteristic *c) - { - // update gps connection state - myNodeInfo.has_gps = gps->isConnected; - - ProtobufCharacteristic::onRead(c); - - myNodeInfo.error_code = 0; // The phone just read us, so throw it away - myNodeInfo.error_address = 0; - } -}; - -#endif - class ToRadioCharacteristic : public CallbackCharacteristic { public: @@ -196,7 +82,6 @@ class ToRadioCharacteristic : public CallbackCharacteristic void onWrite(BLECharacteristic *c) { - BLEKeepAliveCallbacks::onWrite(c); DEBUG_MSG("Got on write\n"); bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length()); @@ -212,7 +97,6 @@ class FromRadioCharacteristic : public CallbackCharacteristic void onRead(BLECharacteristic *c) { - BLEKeepAliveCallbacks::onRead(c); size_t numBytes = bluetoothPhoneAPI->getFromRadio(trBytes); // Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue @@ -236,11 +120,7 @@ class FromNumCharacteristic : public CallbackCharacteristic // observe(&service.fromNumChanged); } - void onRead(BLECharacteristic *c) - { - BLEKeepAliveCallbacks::onRead(c); - DEBUG_MSG("FIXME implement fromnum read\n"); - } + void onRead(BLECharacteristic *c) { DEBUG_MSG("FIXME implement fromnum read\n"); } }; /* @@ -263,12 +143,6 @@ BLEService *createMeshBluetoothService(BLEServer *server) addWithDesc(service, meshFromNumCharacteristic, "fromRadio"); addWithDesc(service, new ToRadioCharacteristic, "toRadio"); addWithDesc(service, new FromRadioCharacteristic, "fromNum"); -#ifdef SUPPORT_OLD_BLE_API - addWithDesc(service, new MyNodeInfoCharacteristic, "myNode"); - addWithDesc(service, new RadioCharacteristic, "radio"); - addWithDesc(service, new OwnerCharacteristic, "owner"); - addWithDesc(service, new NodeInfoCharacteristic, "nodeinfo"); -#endif meshFromNumCharacteristic->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 194a15e68..b4c5538ca 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -1,6 +1,7 @@ #include "PhoneAPI.h" #include "MeshService.h" #include "NodeDB.h" +#include "PowerFSM.h" #include PhoneAPI::PhoneAPI() @@ -14,11 +15,30 @@ void PhoneAPI::init() observe(&service.fromNumChanged); } +void PhoneAPI::checkConnectionTimeout() +{ + if (isConnected) { + bool newConnected = (millis() - lastContactMsec < radioConfig.preferences.phone_timeout_secs * 1000L); + if (!newConnected) { + isConnected = false; + onConnectionChanged(isConnected); + } + } +} + /** * Handle a ToRadio protobuf */ void 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) && + if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) { switch (toRadioScratch.which_variant) { case ToRadio_packet_tag: { @@ -227,6 +247,9 @@ void PhoneAPI::handleToRadioPacket(MeshPacket *p) {} /// If the mesh service tells us fromNum has changed, tell the phone int PhoneAPI::onNotify(uint32_t newValue) { + checkConnectionTimeout(); // a handy place to check if we've heard from the phone (since the BLE version doesn't call this + // from idle) + if (state == STATE_SEND_PACKETS || state == STATE_LEGACY) { DEBUG_MSG("Telling client we have new packets %u\n", newValue); onNowHasData(newValue); diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index f08c9009f..129ecb5db 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -50,6 +50,11 @@ class PhoneAPI /// Use to ensure that clients don't get confused about old messages from the radio uint32_t config_nonce = 0; + /** the last msec we heard from the client on the other side of this link */ + uint32_t lastContactMsec = 0; + + bool isConnected = false; + public: PhoneAPI(); @@ -85,6 +90,12 @@ class PhoneAPI /// Our fromradio packet while it is being assembled FromRadio fromRadioScratch; + /// 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 + void checkConnectionTimeout(); + /** * Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies) */ diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp index 49ccb3d6b..06b80b2fa 100644 --- a/src/mesh/StreamAPI.cpp +++ b/src/mesh/StreamAPI.cpp @@ -9,6 +9,7 @@ void StreamAPI::loop() { writeStream(); readStream(); + checkConnectionTimeout(); } /** diff --git a/src/sleep.cpp b/src/sleep.cpp index 270ecc5cc..18462de79 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -129,6 +129,7 @@ static void waitEnterSleep() if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep recordCriticalError(ErrSleepEnterWait); + ESP.restart(); // FIXME - for now we just restart, need to fix bug #167 break; } }