From 93a06906cb8cb15d99434bb09f1e73fe8416c29e Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 10 Apr 2020 12:15:00 -0700 Subject: [PATCH 1/6] begin splitting up source files, so we can have a tree of sources... unique to each architecture. For now, we have "esp32" and "bare" esp32 is the old esp stuff bare is an target suitable for emulation that doesn't require any particular hardware to run (no bluetooth, no i2c devices, no spi devices) --- platformio.ini | 20 ++++++++++++--- src/bare/main-bare.cpp | 6 +++++ src/configuration.h | 2 ++ src/esp32/main-esp32.cpp | 53 ++++++++++++++++++++++++++++++++++++++++ src/target_specific.h | 6 +++++ 5 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 src/bare/main-bare.cpp create mode 100644 src/esp32/main-esp32.cpp create mode 100644 src/target_specific.h diff --git a/platformio.ini b/platformio.ini index 093a8d47..cd1755ad 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,6 +15,11 @@ default_envs = tbeam ; default to a US frequency range, change it as needed for your region and hardware (CN, JP, EU433, EU865) hw_version = US +; Common settings for ESP targes, mixin with extends = esp32_base +[esp32_base] +src_filter = + ${env.src_filter} - + [env] platform = espressif32 framework = arduino @@ -70,7 +75,8 @@ lib_deps = https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git ; The 1.0 release of the TBEAM board -[env:tbeam] +[env:tbeam] +extends = esp32_base board = ttgo-t-beam lib_deps = ${env.lib_deps} @@ -79,22 +85,26 @@ build_flags = ${env.build_flags} -D TBEAM_V10 ; The original TBEAM board without the AXP power chip and a few other changes -[env:tbeam0.7] +[env:tbeam0.7] +extends = esp32_base board = ttgo-t-beam build_flags = ${env.build_flags} -D TBEAM_V07 [env:heltec] ;build_type = debug ; to make it possible to step through our jtag debugger +extends = esp32_base board = heltec_wifi_lora_32_V2 [env:ttgo-lora32-v1] +extends = esp32_base board = ttgo-lora32-v1 build_flags = ${env.build_flags} -D TTGO_LORA_V1 ; note: the platformio definition for lora32-v2 seems stale, it is missing a pins_arduino.h file, therefore I don't think it works [env:ttgo-lora32-v2] +extends = esp32_base board = ttgo-lora32-v1 build_flags = ${env.build_flags} -D TTGO_LORA_V2 @@ -104,4 +114,8 @@ build_flags = [env:bare] board = ttgo-lora32-v1 build_flags = - ${env.build_flags} -D BARE_BOARD \ No newline at end of file + ${env.build_flags} -D BARE_BOARD +src_filter = + ${env.src_filter} - +lib_ignore = + BluetoothOTA \ No newline at end of file diff --git a/src/bare/main-bare.cpp b/src/bare/main-bare.cpp new file mode 100644 index 00000000..29532a63 --- /dev/null +++ b/src/bare/main-bare.cpp @@ -0,0 +1,6 @@ +#include "target_specific.h" + +void setBluetoothEnable(bool on) +{ + // Do nothing +} \ No newline at end of file diff --git a/src/configuration.h b/src/configuration.h index a8128882..862aa0f1 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -202,6 +202,8 @@ along with this program. If not, see . // This string must exactly match the case used in release file names or the android updater won't work #define HW_VENDOR "bare" +#define NO_ESP32 // Don't use ESP32 libs (mainly bluetooth) + #endif // ----------------------------------------------------------------------------- diff --git a/src/esp32/main-esp32.cpp b/src/esp32/main-esp32.cpp new file mode 100644 index 00000000..baf0d85b --- /dev/null +++ b/src/esp32/main-esp32.cpp @@ -0,0 +1,53 @@ +#include "BluetoothUtil.h" +#include "MeshBluetoothService.h" +#include "PowerFSM.h" +#include "configuration.h" +#include "main.h" +#include "target_specific.h" + +bool bluetoothOn; + +// This routine is called multiple times, once each time we come back from sleep +void reinitBluetooth() +{ + DEBUG_MSG("Starting bluetooth\n"); + + // FIXME - we are leaking like crazy + // AllocatorScope scope(btPool); + + // Note: these callbacks might be coming in from a different thread. + BLEServer *serve = initBLE( + [](uint32_t pin) { + powerFSM.trigger(EVENT_BLUETOOTH_PAIR); + screen.startBluetoothPinScreen(pin); + }, + []() { screen.stopBluetoothPinScreen(); }, getDeviceName(), HW_VENDOR, xstr(APP_VERSION), + xstr(HW_VERSION)); // FIXME, use a real name based on the macaddr + createMeshBluetoothService(serve); + + // Start advertising - this must be done _after_ creating all services + serve->getAdvertising()->start(); +} + +// Enable/disable bluetooth. +void setBluetoothEnable(bool on) +{ + if (on != bluetoothOn) { + DEBUG_MSG("Setting bluetooth enable=%d\n", on); + + bluetoothOn = on; + if (on) { + Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap()); + // ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) ); + reinitBluetooth(); + } else { + // We have to totally teardown our bluetooth objects to prevent leaks + stopMeshBluetoothService(); // Must do before shutting down bluetooth + deinitBLE(); + destroyMeshBluetoothService(); // must do after deinit, because it frees our service + Serial.printf("Shutdown BT: %u heap size\n", ESP.getFreeHeap()); + // ESP_ERROR_CHECK( heap_trace_stop() ); + // heap_trace_dump(); + } + } +} \ No newline at end of file diff --git a/src/target_specific.h b/src/target_specific.h new file mode 100644 index 00000000..6acca364 --- /dev/null +++ b/src/target_specific.h @@ -0,0 +1,6 @@ +#pragma once + +// Functions that are unique to particular target types (esp32, bare, nrf52 etc...) + +// Enable/disable bluetooth. +void setBluetoothEnable(bool on); \ No newline at end of file From 6ad451eb5f9387c77740691601f04a8a4bf2e9a7 Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 10 Apr 2020 12:18:48 -0700 Subject: [PATCH 2/6] move bluetooth code into something that is architecture specific... because the ESP32 implementation will be different from NRF52 to make this possible I needed to decouple knowlege about bluetooth from our mesh service. Instead mesh service now uses the Obserable pattern to let any interested consumer get notified of important mesh changes (currently that is only bluetooth, but really we should do the same thing for decoupling the GUI 'app' from the mesh service) @girtsf would you mind reviewing my Observer changes? I haven't written C++ code in a long time ;-) --- src/GPS.cpp | 2 +- src/GPS.h | 2 +- src/MeshService.cpp | 21 +++---- src/MeshService.h | 15 +++-- src/Observer.cpp | 11 ---- src/Observer.h | 71 +++++++++++++++++++----- src/PowerFSM.cpp | 1 + src/{ => esp32}/MeshBluetoothService.cpp | 22 +++----- src/{ => esp32}/MeshBluetoothService.h | 0 src/main.cpp | 54 +++--------------- src/main.h | 3 + src/sleep.cpp | 9 ++- src/sleep.h | 1 - 13 files changed, 104 insertions(+), 108 deletions(-) rename src/{ => esp32}/MeshBluetoothService.cpp (96%) rename src/{ => esp32}/MeshBluetoothService.h (100%) diff --git a/src/GPS.cpp b/src/GPS.cpp index 8ca8dd36..ea04b09f 100644 --- a/src/GPS.cpp +++ b/src/GPS.cpp @@ -192,7 +192,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0 if (hasValidLocation) { wantNewLocation = false; - notifyObservers(); + notifyObservers(NULL); // ublox.powerOff(); } } else // we didn't get a location update, go back to sleep and hope the characters show up diff --git a/src/GPS.h b/src/GPS.h index 5d0d4b87..caf3fc24 100644 --- a/src/GPS.h +++ b/src/GPS.h @@ -10,7 +10,7 @@ * * When new data is available it will notify observers. */ -class GPS : public PeriodicTask, public Observable +class GPS : public PeriodicTask, public Observable { SFE_UBLOX_GPS ublox; diff --git a/src/MeshService.cpp b/src/MeshService.cpp index 85f4856d..6b9dabdc 100644 --- a/src/MeshService.cpp +++ b/src/MeshService.cpp @@ -3,7 +3,7 @@ #include #include "GPS.h" -#include "MeshBluetoothService.h" +//#include "MeshBluetoothService.h" #include "MeshService.h" #include "NodeDB.h" #include "Periodic.h" @@ -52,8 +52,7 @@ MeshService service; 4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big MeshService::MeshService() - : packetPool(MAX_PACKETS), toPhoneQueue(MAX_RX_TOPHONE), fromRadioQueue(MAX_RX_FROMRADIO), fromNum(0), - radio(packetPool, fromRadioQueue) + : packetPool(MAX_PACKETS), toPhoneQueue(MAX_RX_TOPHONE), fromRadioQueue(MAX_RX_FROMRADIO), radio(packetPool, fromRadioQueue) { // assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro } @@ -65,7 +64,7 @@ void MeshService::init() if (!radio.init()) DEBUG_MSG("radio init failed\n"); - gps.addObserver(this); + gpsObserver.observe(&gps); // No need to call this here, our periodic task will fire quite soon // sendOwnerPeriod(); @@ -191,7 +190,7 @@ void MeshService::handleFromRadio() handleFromRadio(mp); } if (oldFromNum != fromNum) // We don't want to generate extra notifies for multiple new packets - bluetoothNotifyFromNum(fromNum); + fromNumChanged.notifyObservers(fromNum); } uint32_t sendOwnerCb() @@ -244,7 +243,7 @@ void MeshService::handleToRadio(std::string s) if (loopback) { MeshPacket *mp = packetPool.allocCopy(r.variant.packet); handleFromRadio(mp); - bluetoothNotifyFromNum(fromNum); // tell the phone a new packet arrived + // handleFromRadio will tell the phone a new packet arrived } break; } @@ -323,8 +322,10 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies) sendToMesh(p); } -void MeshService::onGPSChanged() +void MeshService::onGPSChanged(void *unused) { + DEBUG_MSG("got gps notify\n"); + // Update our local node info with our position (even if we don't decide to update anyone else) MeshPacket *p = allocForSending(); p->payload.which_variant = SubPacket_position_tag; @@ -354,9 +355,3 @@ void MeshService::onGPSChanged() releaseToPool(p); } } - -void MeshService::onNotify(Observable *o) -{ - DEBUG_MSG("got gps notify\n"); - onGPSChanged(); -} diff --git a/src/MeshService.h b/src/MeshService.h index 88452923..30e03017 100644 --- a/src/MeshService.h +++ b/src/MeshService.h @@ -13,8 +13,10 @@ * Top level app for this service. keeps the mesh, the radio config and the queue of received packets. * */ -class MeshService : private Observer +class MeshService { + CallbackObserver gpsObserver = CallbackObserver(this, &MeshService::onGPSChanged); + MemoryPool packetPool; /// received packets waiting for the phone to process them @@ -28,11 +30,13 @@ class MeshService : private Observer PointerQueue fromRadioQueue; /// The current nonce for the newest packet which has been queued for the phone - uint32_t fromNum; + uint32_t fromNum = 0; public: MeshRadio radio; + Observable fromNumChanged; + MeshService(); void init(); @@ -76,14 +80,13 @@ class MeshService : private Observer void sendToMesh(MeshPacket *p); /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh - void onGPSChanged(); - - virtual void onNotify(Observable *o); + void onGPSChanged(void *arg); /// handle all the packets that just arrived from the mesh radio void handleFromRadio(); - /// Handle a packet that just arrived from the radio. We will either eventually enqueue the message to the phone or return it to the free pool + /// Handle a packet that just arrived from the radio. We will either eventually enqueue the message to the phone or return it + /// to the free pool void handleFromRadio(MeshPacket *p); /// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all diff --git a/src/Observer.cpp b/src/Observer.cpp index 468f5ade..1025f8bc 100644 --- a/src/Observer.cpp +++ b/src/Observer.cpp @@ -1,13 +1,2 @@ #include "Observer.h" -Observer::~Observer() -{ - if (observed) - observed->removeObserver(this); - observed = NULL; -} - -void Observer::observe(Observable *o) -{ - o->addObserver(this); -} \ No newline at end of file diff --git a/src/Observer.h b/src/Observer.h index b4de9d83..b20fe818 100644 --- a/src/Observer.h +++ b/src/Observer.h @@ -4,38 +4,83 @@ #include -class Observable; +template class Observable; -class Observer +/** + * An observer which can be mixed in as a baseclass. Implement onNotify as a method in your class. + */ +template class Observer { - Observable *observed; + Observable *observed; public: Observer() : observed(NULL) {} virtual ~Observer(); - void observe(Observable *o); + void observe(Observable *o); private: - friend class Observable; + friend class Observable; - virtual void onNotify(Observable *o) = 0; + protected: + virtual void onNotify(T arg) = 0; }; -class Observable +/** + * An observer that calls an arbitrary method + */ +template class CallbackObserver : public Observer { - std::list observers; + typedef void (Callback::*ObserverCallback)(T arg); + + Callback *objPtr; + ObserverCallback method; public: - void notifyObservers() + CallbackObserver(Callback *_objPtr, ObserverCallback _method) : objPtr(_objPtr), method(_method) {} + + protected: + virtual void onNotify(T arg) { (objPtr->*method)(arg); } +}; + +/** + * An observable class that will notify observers anytime notifyObservers is called. Argument type T can be any type, but for + * performance reasons a pointer or word sized object is recommended. + */ +template class Observable +{ + std::list *> observers; + + public: + /** + * Tell all observers about a change, observers can process arg as they wish + */ + void notifyObservers(T arg) { - for (std::list::const_iterator iterator = observers.begin(); iterator != observers.end(); ++iterator) { - (*iterator)->onNotify(this); + for (typename std::list *>::const_iterator iterator = observers.begin(); iterator != observers.end(); + ++iterator) { + (*iterator)->onNotify(arg); } } - void addObserver(Observer *o) { observers.push_back(o); } + private: + friend class Observer; - void removeObserver(Observer *o) { observers.remove(o); } + // Not called directly, instead call observer.observe + void addObserver(Observer *o) { observers.push_back(o); } + + void removeObserver(Observer *o) { observers.remove(o); } }; + +template Observer::~Observer() +{ + if (observed) + observed->removeObserver(this); + observed = NULL; +} + +template void Observer::observe(Observable *o) +{ + o->addObserver(this); +} \ No newline at end of file diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index ce7005ff..d484efe6 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -7,6 +7,7 @@ #include "main.h" #include "screen.h" #include "sleep.h" +#include "target_specific.h" static void sdsEnter() { diff --git a/src/MeshBluetoothService.cpp b/src/esp32/MeshBluetoothService.cpp similarity index 96% rename from src/MeshBluetoothService.cpp rename to src/esp32/MeshBluetoothService.cpp index 16af1492..d1bd273b 100644 --- a/src/MeshBluetoothService.cpp +++ b/src/esp32/MeshBluetoothService.cpp @@ -204,7 +204,7 @@ class FromRadioCharacteristic : public CallbackCharacteristic } }; -class FromNumCharacteristic : public CallbackCharacteristic +class FromNumCharacteristic : public CallbackCharacteristic, public Observer { public: FromNumCharacteristic() @@ -212,6 +212,7 @@ class FromNumCharacteristic : public CallbackCharacteristic BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY) { + observe(&service.fromNumChanged); } void onRead(BLECharacteristic *c) @@ -219,6 +220,13 @@ class FromNumCharacteristic : public CallbackCharacteristic BLEKeepAliveCallbacks::onRead(c); DEBUG_MSG("FIXME implement fromnum read\n"); } + + /// If the mesh service tells us fromNum has changed, tell the phone + virtual void onNotify(uint32_t newValue) + { + setValue(newValue); + notify(); + } }; class MyNodeInfoCharacteristic : public ProtobufCharacteristic @@ -244,18 +252,6 @@ class MyNodeInfoCharacteristic : public ProtobufCharacteristic FromNumCharacteristic *meshFromNumCharacteristic; -/** - * Tell any bluetooth clients that the number of rx packets has changed - */ -void bluetoothNotifyFromNum(uint32_t newValue) -{ - if (meshFromNumCharacteristic) { - // if bt not running ignore - meshFromNumCharacteristic->setValue(newValue); - meshFromNumCharacteristic->notify(); - } -} - BLEService *meshService; /* diff --git a/src/MeshBluetoothService.h b/src/esp32/MeshBluetoothService.h similarity index 100% rename from src/MeshBluetoothService.h rename to src/esp32/MeshBluetoothService.h diff --git a/src/main.cpp b/src/main.cpp index 910d45cf..0c0958a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,9 +21,7 @@ */ -#include "BluetoothUtil.h" #include "GPS.h" -#include "MeshBluetoothService.h" #include "MeshRadio.h" #include "MeshService.h" #include "NodeDB.h" @@ -39,6 +37,10 @@ #include #include +#ifndef NO_ESP32 +#include "BluetoothUtil.h" +#endif + #ifdef TBEAM_V10 #include "axp20x.h" AXP20X_Class axp; @@ -59,8 +61,6 @@ static meshtastic::PowerStatus powerStatus; bool ssd1306_found; bool axp192_found; -bool bluetoothOn; - // ----------------------------------------------------------------------------- // Application // ----------------------------------------------------------------------------- @@ -266,49 +266,6 @@ void setup() setCPUFast(false); // 80MHz is fine for our slow peripherals } -void initBluetooth() -{ - DEBUG_MSG("Starting bluetooth\n"); - - // FIXME - we are leaking like crazy - // AllocatorScope scope(btPool); - - // Note: these callbacks might be coming in from a different thread. - BLEServer *serve = initBLE( - [](uint32_t pin) { - powerFSM.trigger(EVENT_BLUETOOTH_PAIR); - screen.startBluetoothPinScreen(pin); - }, - []() { screen.stopBluetoothPinScreen(); }, getDeviceName(), HW_VENDOR, xstr(APP_VERSION), - xstr(HW_VERSION)); // FIXME, use a real name based on the macaddr - createMeshBluetoothService(serve); - - // Start advertising - this must be done _after_ creating all services - serve->getAdvertising()->start(); -} - -void setBluetoothEnable(bool on) -{ - if (on != bluetoothOn) { - DEBUG_MSG("Setting bluetooth enable=%d\n", on); - - bluetoothOn = on; - if (on) { - Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap()); - // ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) ); - initBluetooth(); - } else { - // We have to totally teardown our bluetooth objects to prevent leaks - stopMeshBluetoothService(); // Must do before shutting down bluetooth - deinitBLE(); - destroyMeshBluetoothService(); // must do after deinit, because it frees our service - Serial.printf("Shutdown BT: %u heap size\n", ESP.getFreeHeap()); - // ESP_ERROR_CHECK( heap_trace_stop() ); - // heap_trace_dump(); - } - } -} - uint32_t ledBlinker() { static bool ledOn; @@ -352,7 +309,10 @@ void loop() ledPeriodic.loop(); // axpDebugOutput.loop(); + +#ifndef NO_ESP32 loopBLE(); +#endif // for debug printing // service.radio.radioIf.canSleep(); diff --git a/src/main.h b/src/main.h index 9842ea96..c8c37971 100644 --- a/src/main.h +++ b/src/main.h @@ -9,3 +9,6 @@ extern bool isUSBPowered; // Global Screen singleton. extern meshtastic::Screen screen; + +// Return a human readable string of the form "Meshtastic_ab13" +const char *getDeviceName(); diff --git a/src/sleep.cpp b/src/sleep.cpp index e204ea5f..2a3dcda2 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -1,7 +1,5 @@ #include "sleep.h" -#include "BluetoothUtil.h" #include "GPS.h" -#include "MeshBluetoothService.h" #include "MeshRadio.h" #include "MeshService.h" #include "NodeDB.h" @@ -11,9 +9,14 @@ #include "esp_pm.h" #include "main.h" #include "rom/rtc.h" +#include "target_specific.h" #include #include +#ifndef NO_ESP32 +#include "BluetoothUtil.h" +#endif + #ifdef TBEAM_V10 #include "axp20x.h" extern AXP20X_Class axp; @@ -105,7 +108,9 @@ void doDeepSleep(uint64_t msecToWake) // not using wifi yet, but once we are this is needed to shutoff the radio hw // esp_wifi_stop(); +#ifndef NO_ESP32 BLEDevice::deinit(false); // We are required to shutdown bluetooth before deep or light sleep +#endif screen.setOn(false); // datasheet says this will draw only 10ua diff --git a/src/sleep.h b/src/sleep.h index 49976516..129728f5 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -5,7 +5,6 @@ void doDeepSleep(uint64_t msecToWake); esp_sleep_wakeup_cause_t doLightSleep(uint64_t msecToWake); -void setBluetoothEnable(bool on); void setGPSPower(bool on); // Perform power on init that we do on each wake from deep sleep From 640cb3bf7f551bf40caee034d022c78187d6cf5c Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 10 Apr 2020 12:40:44 -0700 Subject: [PATCH 3/6] allow observers to return an error code to abort further processing Will allow me to use observers to generalize the various hooks that need to run to preflight sleep entry. --- src/MeshService.cpp | 4 +++- src/MeshService.h | 3 ++- src/Observer.h | 20 +++++++++++++++----- src/esp32/MeshBluetoothService.cpp | 3 ++- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/MeshService.cpp b/src/MeshService.cpp index 6b9dabdc..38fdc347 100644 --- a/src/MeshService.cpp +++ b/src/MeshService.cpp @@ -322,7 +322,7 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies) sendToMesh(p); } -void MeshService::onGPSChanged(void *unused) +int MeshService::onGPSChanged(void *unused) { DEBUG_MSG("got gps notify\n"); @@ -354,4 +354,6 @@ void MeshService::onGPSChanged(void *unused) releaseToPool(p); } + + return 0; } diff --git a/src/MeshService.h b/src/MeshService.h index 30e03017..e8f8721e 100644 --- a/src/MeshService.h +++ b/src/MeshService.h @@ -80,7 +80,8 @@ class MeshService void sendToMesh(MeshPacket *p); /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh - void onGPSChanged(void *arg); + /// returns 0 to allow futher processing + int onGPSChanged(void *arg); /// handle all the packets that just arrived from the mesh radio void handleFromRadio(); diff --git a/src/Observer.h b/src/Observer.h index b20fe818..eab1a4a3 100644 --- a/src/Observer.h +++ b/src/Observer.h @@ -24,7 +24,11 @@ template class Observer friend class Observable; protected: - virtual void onNotify(T arg) = 0; + /** + * returns 0 if other observers should continue to be called + * returns !0 if the observe calls should be aborted and this result code returned for notifyObservers + **/ + virtual int onNotify(T arg) = 0; }; /** @@ -32,7 +36,7 @@ template class Observer */ template class CallbackObserver : public Observer { - typedef void (Callback::*ObserverCallback)(T arg); + typedef int (Callback::*ObserverCallback)(T arg); Callback *objPtr; ObserverCallback method; @@ -41,7 +45,7 @@ template class CallbackObserver : public Observer CallbackObserver(Callback *_objPtr, ObserverCallback _method) : objPtr(_objPtr), method(_method) {} protected: - virtual void onNotify(T arg) { (objPtr->*method)(arg); } + virtual int onNotify(T arg) { return (objPtr->*method)(arg); } }; /** @@ -55,13 +59,19 @@ template class Observable public: /** * Tell all observers about a change, observers can process arg as they wish + * + * returns !0 if an observer chose to abort processing by returning this code */ - void notifyObservers(T arg) + int notifyObservers(T arg) { for (typename std::list *>::const_iterator iterator = observers.begin(); iterator != observers.end(); ++iterator) { - (*iterator)->onNotify(arg); + int result = (*iterator)->onNotify(arg); + if (result != 0) + return result; } + + return 0; } private: diff --git a/src/esp32/MeshBluetoothService.cpp b/src/esp32/MeshBluetoothService.cpp index d1bd273b..086f0afe 100644 --- a/src/esp32/MeshBluetoothService.cpp +++ b/src/esp32/MeshBluetoothService.cpp @@ -222,10 +222,11 @@ class FromNumCharacteristic : public CallbackCharacteristic, public Observer Date: Sun, 12 Apr 2020 10:54:27 -0700 Subject: [PATCH 4/6] fix bin paths --- bin/program-release-heltec.sh | 2 +- bin/program-release-tbeam.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/program-release-heltec.sh b/bin/program-release-heltec.sh index d16f6c36..90506473 100755 --- a/bin/program-release-heltec.sh +++ b/bin/program-release-heltec.sh @@ -3,4 +3,4 @@ set -e source bin/version.sh -esptool.py --baud 921600 write_flash 0x10000 release/latest/firmware-HELTEC-US-$VERSION.bin +esptool.py --baud 921600 write_flash 0x10000 release/latest/bins/firmware-heltec-US-$VERSION.bin diff --git a/bin/program-release-tbeam.sh b/bin/program-release-tbeam.sh index 2c5f030c..d83b3562 100755 --- a/bin/program-release-tbeam.sh +++ b/bin/program-release-tbeam.sh @@ -3,4 +3,4 @@ set -e source bin/version.sh -esptool.py --baud 921600 write_flash 0x10000 release/latest/firmware-TBEAM-US-$VERSION.bin +esptool.py --baud 921600 write_flash 0x10000 release/latest/bins/firmware-tbeam-US-$VERSION.bin From 4757b6807ec1c1dcd95bce0d50e7f50ec3bb7e98 Mon Sep 17 00:00:00 2001 From: geeksville Date: Tue, 14 Apr 2020 11:40:49 -0700 Subject: [PATCH 5/6] lots of changes: * preflightSleep, notifySleep, notifyDeepSleep now allow arbitrary drivers/devices/software to register for sleep notification. * Use the proceeding to clean up MeshRadio - now the mesh radio is more like an independent driver that doesn't care so much about other systems * clean up MeshService so that it can work with zero MeshRadios added. This is a prelude to supporting boards with multiple interfaces (wifi, extra LORA radios etc) and allows development/testing in sim with a bare ESP32 board * Remove remaining ESP32 dependencies from the bare simulation target this allows running on anything that implements the arduino API --- src/MeshRadio.cpp | 27 ++++++++++++++------- src/MeshRadio.h | 43 ++++++++++++++++++++++++--------- src/MeshService.cpp | 18 +++++++------- src/MeshService.h | 20 ++++++++++------ src/PowerFSM.cpp | 16 ------------- src/error.h | 2 +- src/main.cpp | 16 ++++++++++++- src/sleep.cpp | 58 +++++++++++++++++++++++++++++++++++++-------- src/sleep.h | 12 +++++++++- 9 files changed, 147 insertions(+), 65 deletions(-) diff --git a/src/MeshRadio.cpp b/src/MeshRadio.cpp index 1f7d20d4..7950760b 100644 --- a/src/MeshRadio.cpp +++ b/src/MeshRadio.cpp @@ -5,8 +5,10 @@ #include #include "MeshRadio.h" +#include "MeshService.h" #include "NodeDB.h" #include "configuration.h" +#include "sleep.h" #include #include @@ -25,7 +27,7 @@ separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts bool useHardware = true; MeshRadio::MeshRadio(MemoryPool &_pool, PointerQueue &_rxDest) - : radioIf(_pool, _rxDest) // , manager(radioIf) + : radioIf(_pool, _rxDest), sendPacketObserver(this, &MeshRadio::send) // , manager(radioIf) { myNodeInfo.num_channels = NUM_CHANNELS; @@ -40,6 +42,11 @@ bool MeshRadio::init() DEBUG_MSG("Starting meshradio init...\n"); + configChangedObserver.observe(&service.configChanged); + sendPacketObserver.observe(&service.sendViaRadio); + preflightSleepObserver.observe(&preflightSleep); + notifyDeepSleepObserver.observe(¬ifyDeepSleep); + #ifdef RESET_GPIO pinMode(RESET_GPIO, OUTPUT); // Deassert reset digitalWrite(RESET_GPIO, HIGH); @@ -84,7 +91,7 @@ unsigned long hash(char *str) return hash; } -void MeshRadio::reloadConfig() +int MeshRadio::reloadConfig(void *unused) { radioIf.setModeIdle(); // Need to be idle before doing init @@ -116,17 +123,21 @@ void MeshRadio::reloadConfig() // Done with init tell radio to start receiving radioIf.setModeRx(); + + return 0; } -ErrorCode MeshRadio::send(MeshPacket *p) +int MeshRadio::send(MeshPacket *p) { lastTxStart = millis(); - if (useHardware) - return radioIf.send(p); - else { - radioIf.pool.release(p); - return ERRNO_OK; + if (useHardware) { + radioIf.send(p); + // Note: we ignore the error code, because no matter what the interface has already freed the packet. + return 1; // Indicate success - stop offering this packet to radios + } else { + // fail + return 0; } } diff --git a/src/MeshRadio.h b/src/MeshRadio.h index efb9e4fa..4b4d3c7b 100644 --- a/src/MeshRadio.h +++ b/src/MeshRadio.h @@ -3,6 +3,7 @@ #include "CustomRF95.h" #include "MemoryPool.h" #include "MeshTypes.h" +#include "Observer.h" #include "PointerQueue.h" #include "configuration.h" #include "mesh.pb.h" @@ -80,22 +81,42 @@ class MeshRadio bool init(); - /// Send a packet (possibly by enquing in a private fifo). This routine will - /// later free() the packet to pool. This routine is not allowed to stall because it is called from - /// bluetooth comms code. If the txmit queue is empty it might return an error - ErrorCode send(MeshPacket *p); - /// Do loop callback operations (we currently FIXME poll the receive mailbox here) /// for received packets it will call the rx handler void loop(); - /// The radioConfig object just changed, call this to force the hw to change to the new settings - void reloadConfig(); - private: - // RHReliableDatagram manager; // don't use mesh yet - // RHMesh manager; - /// Used for the tx timer watchdog, to check for bugs in our transmit code, msec of last time we did a send uint32_t lastTxStart = 0; + + CallbackObserver configChangedObserver = + CallbackObserver(this, &MeshRadio::reloadConfig); + + CallbackObserver preflightSleepObserver = + CallbackObserver(this, &MeshRadio::preflightSleepCb); + + CallbackObserver notifyDeepSleepObserver = + CallbackObserver(this, &MeshRadio::notifyDeepSleepDb); + + CallbackObserver sendPacketObserver; /* = + CallbackObserver(this, &MeshRadio::send); */ + + /// Send a packet (possibly by enquing in a private fifo). This routine will + /// later free() the packet to pool. This routine is not allowed to stall because it is called from + /// bluetooth comms code. If the txmit queue is empty it might return an error. + /// + /// Returns 1 for success or 0 for failure (and if we fail it is the _callers_ responsibility to free the packet) + int send(MeshPacket *p); + + /// The radioConfig object just changed, call this to force the hw to change to the new settings + int reloadConfig(void *unused = NULL); + + /// Return 0 if sleep is okay + int preflightSleepCb(void *unused = NULL) { return radioIf.canSleep() ? 0 : 1; } + + int notifyDeepSleepDb(void *unused = NULL) + { + radioIf.sleep(); + return 0; + } }; diff --git a/src/MeshService.cpp b/src/MeshService.cpp index 38fdc347..2af86b4c 100644 --- a/src/MeshService.cpp +++ b/src/MeshService.cpp @@ -51,8 +51,7 @@ MeshService service; #define MAX_RX_FROMRADIO \ 4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big -MeshService::MeshService() - : packetPool(MAX_PACKETS), toPhoneQueue(MAX_RX_TOPHONE), fromRadioQueue(MAX_RX_FROMRADIO), radio(packetPool, fromRadioQueue) +MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE), packetPool(MAX_PACKETS), fromRadioQueue(MAX_RX_FROMRADIO) { // assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro } @@ -61,9 +60,6 @@ void MeshService::init() { nodeDB.init(); - if (!radio.init()) - DEBUG_MSG("radio init failed\n"); - gpsObserver.observe(&gps); // No need to call this here, our periodic task will fire quite soon @@ -205,8 +201,6 @@ Periodic sendOwnerPeriod(sendOwnerCb); /// Do idle processing (mostly processing messages which have been queued from the radio) void MeshService::loop() { - radio.loop(); // FIXME, possibly move radio interaction to own thread - handleFromRadio(); // occasionally send our owner info @@ -218,7 +212,7 @@ void MeshService::reloadConfig() { // If we can successfully set this radio to these settings, save them to disk nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings - radio.reloadConfig(); + configChanged.notifyObservers(NULL); nodeDB.saveToDisk(); } @@ -276,8 +270,12 @@ void MeshService::sendToMesh(MeshPacket *p) DEBUG_MSG("Dropping locally processed message\n"); else { // Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it - if (radio.send(p) != ERRNO_OK) - DEBUG_MSG("Dropped packet because send queue was full!\n"); + int didSend = sendViaRadio.notifyObservers(p); + if (!didSend) { + DEBUG_MSG("No radio was able to send packet, discarding..."); + releaseToPool(p); + } + } } diff --git a/src/MeshService.h b/src/MeshService.h index e8f8721e..e9d4fe48 100644 --- a/src/MeshService.h +++ b/src/MeshService.h @@ -17,26 +17,32 @@ class MeshService { CallbackObserver gpsObserver = CallbackObserver(this, &MeshService::onGPSChanged); - MemoryPool packetPool; - /// received packets waiting for the phone to process them /// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure /// we never hang because android hasn't been there in a while /// FIXME - save this to flash on deep sleep PointerQueue toPhoneQueue; - /// Packets which have just arrived from the radio, ready to be processed by this service and possibly - /// forwarded to the phone. - PointerQueue fromRadioQueue; - /// The current nonce for the newest packet which has been queued for the phone uint32_t fromNum = 0; public: - MeshRadio radio; + MemoryPool packetPool; + /// Packets which have just arrived from the radio, ready to be processed by this service and possibly + /// forwarded to the phone. + PointerQueue fromRadioQueue; + + /// Called when some new packets have arrived from one of the radios Observable fromNumChanged; + /// Called when radio config has changed (radios should observe this and set their hardware as required) + Observable configChanged; + + /// Radios should observe this and return 0 if they were unable to process the packet or 1 if they were (and therefore it + /// should not be offered to other radios) + Observable sendViaRadio; + MeshService(); void init(); diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index d484efe6..716e4e2c 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -31,22 +31,6 @@ static void lsEnter() DEBUG_MSG("lsEnter begin, ls_secs=%u\n", radioConfig.preferences.ls_secs); screen.setOn(false); - uint32_t now = millis(); - while (!service.radio.radioIf.canSleep()) { - delay(10); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives) - - if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep - recordCriticalError(ErrSleepEnterWait); - break; - } - } - - gps.prepareSleep(); // abandon in-process parsing - - // if (!isUSBPowered) // FIXME - temp hack until we can put gps in sleep mode, if we have AC when we go to sleep then - // leave GPS on - // setGPSPower(false); // kill GPS power - DEBUG_MSG("lsEnter end\n"); } diff --git a/src/error.h b/src/error.h index cc3328c3..3fa3f470 100644 --- a/src/error.h +++ b/src/error.h @@ -1,7 +1,7 @@ #pragma once /// Error codes for critical error -enum CriticalErrorCode { NoError, ErrTxWatchdog, ErrSleepEnterWait }; +enum CriticalErrorCode { NoError, ErrTxWatchdog, ErrSleepEnterWait, ErrNoRadio }; /// Record an error that should be reported via analytics void recordCriticalError(CriticalErrorCode code, uint32_t address = 0); diff --git a/src/main.cpp b/src/main.cpp index 0c0958a6..a10de160 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,7 @@ #include "Periodic.h" #include "PowerFSM.h" #include "configuration.h" +#include "error.h" #include "esp32/pm.h" #include "esp_pm.h" #include "power.h" @@ -205,6 +206,8 @@ const char *getDeviceName() return name; } +static MeshRadio *radio = NULL; + void setup() { // Debug @@ -259,6 +262,14 @@ void setup() service.init(); +#ifndef NO_ESP32 + // MUST BE AFTER service.init, so we have our radio config settings (from nodedb init) + radio = new MeshRadio(service.packetPool, service.fromRadioQueue); +#endif + + if (radio && !radio->init()) + recordCriticalError(ErrNoRadio); + // This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS @@ -307,6 +318,9 @@ void loop() gps.loop(); service.loop(); + if (radio) + radio->loop(); + ledPeriodic.loop(); // axpDebugOutput.loop(); @@ -315,7 +329,7 @@ void loop() #endif // for debug printing - // service.radio.radioIf.canSleep(); + // radio.radioIf.canSleep(); #ifdef PMU_IRQ if (pmu_irq) { diff --git a/src/sleep.cpp b/src/sleep.cpp index 2a3dcda2..0ae911b0 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -5,6 +5,7 @@ #include "NodeDB.h" #include "Periodic.h" #include "configuration.h" +#include "error.h" #include "esp32/pm.h" #include "esp_pm.h" #include "main.h" @@ -22,6 +23,12 @@ extern AXP20X_Class axp; #endif +/// Called to ask any observers if they want to veto sleep. Return 1 to veto or 0 to allow sleep to happen +Observable preflightSleep; + +/// Called to tell observers we are now entering sleep and you should prepare. Must return 0 +Observable notifySleep, notifyDeepSleep; + // deep sleep support RTC_DATA_ATTR int bootCount = 0; esp_sleep_source_t wakeCause; // the reason we booted this time @@ -101,22 +108,53 @@ void initDeepSleep() DEBUG_MSG("booted, wake cause %d (boot count %d), reset_reason=%s\n", wakeCause, bootCount, reason); } +/// return true if sleep is allowed +static bool doPreflightSleep() +{ + if (preflightSleep.notifyObservers(NULL) != 0) + return false; // vetoed + else + return true; +} + +/// Tell devices we are going to sleep and wait for them to handle things +static void waitEnterSleep() +{ + /* + former hardwired code - now moved into notifySleep callbacks: + // Put radio in sleep mode (will still draw power but only 0.2uA) + service.radio.radioIf.sleep(); + */ + + uint32_t now = millis(); + while (!doPreflightSleep()) { + delay(10); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives) + + if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep + recordCriticalError(ErrSleepEnterWait); + break; + } + } + + // Code that still needs to be moved into notifyObservers + Serial.flush(); // send all our characters before we stop cpu clock + setBluetoothEnable(false); // has to be off before calling light sleep + gps.prepareSleep(); // abandon in-process parsing + + notifySleep.notifyObservers(NULL); +} + void doDeepSleep(uint64_t msecToWake) { DEBUG_MSG("Entering deep sleep for %llu seconds\n", msecToWake / 1000); // not using wifi yet, but once we are this is needed to shutoff the radio hw // esp_wifi_stop(); - -#ifndef NO_ESP32 - BLEDevice::deinit(false); // We are required to shutdown bluetooth before deep or light sleep -#endif + waitEnterSleep(); + notifyDeepSleep.notifyObservers(NULL); screen.setOn(false); // datasheet says this will draw only 10ua - // Put radio in sleep mode (will still draw power but only 0.2uA) - service.radio.radioIf.sleep(); - nodeDB.saveToDisk(); #ifdef RESET_OLED @@ -204,10 +242,10 @@ void doDeepSleep(uint64_t msecToWake) esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more reasonable default { // DEBUG_MSG("Enter light sleep\n"); - uint64_t sleepUsec = sleepMsec * 1000LL; - Serial.flush(); // send all our characters before we stop cpu clock - setBluetoothEnable(false); // has to be off before calling light sleep + waitEnterSleep(); + + uint64_t sleepUsec = sleepMsec * 1000LL; // NOTE! ESP docs say we must disable bluetooth and wifi before light sleep diff --git a/src/sleep.h b/src/sleep.h index 129728f5..e63fa70a 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -1,6 +1,7 @@ #pragma once #include "Arduino.h" +#include "Observer.h" #include "esp_sleep.h" void doDeepSleep(uint64_t msecToWake); @@ -17,4 +18,13 @@ extern int bootCount; extern esp_sleep_source_t wakeCause; // is bluetooth sw currently running? -extern bool bluetoothOn; \ No newline at end of file +extern bool bluetoothOn; + +/// Called to ask any observers if they want to veto sleep. Return 1 to veto or 0 to allow sleep to happen +extern Observable preflightSleep; + +/// Called to tell observers we are now entering (light or deep) sleep and you should prepare. Must return 0 +extern Observable notifySleep; + +/// Called to tell observers we are now entering (deep) sleep and you should prepare. Must return 0 +extern Observable notifyDeepSleep; \ No newline at end of file From 5c379c4a989296dde1d07fca6ab5511b6f5f194c Mon Sep 17 00:00:00 2001 From: geeksville Date: Tue, 14 Apr 2020 11:44:35 -0700 Subject: [PATCH 6/6] missing newline --- src/MeshService.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/MeshService.cpp b/src/MeshService.cpp index 2af86b4c..c8e947ac 100644 --- a/src/MeshService.cpp +++ b/src/MeshService.cpp @@ -272,10 +272,9 @@ void MeshService::sendToMesh(MeshPacket *p) // Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it int didSend = sendViaRadio.notifyObservers(p); if (!didSend) { - DEBUG_MSG("No radio was able to send packet, discarding..."); + DEBUG_MSG("No radio was able to send packet, discarding...\n"); releaseToPool(p); } - } }