diff --git a/bin/program-release-heltec.sh b/bin/program-release-heltec.sh index d16f6c360..90506473b 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 2c5f030c6..d83b35622 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 diff --git a/platformio.ini b/platformio.ini index 093a8d477..cd1755adf 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/GPS.cpp b/src/GPS.cpp index 8ca8dd36b..ea04b09f9 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 5d0d4b876..caf3fc249 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/MeshRadio.cpp b/src/MeshRadio.cpp index 1f7d20d43..7950760bf 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 efb9e4faa..4b4d3c7b6 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 85f4856d2..c8e947acf 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" @@ -51,9 +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), fromNum(0), - 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 } @@ -62,10 +60,7 @@ void MeshService::init() { nodeDB.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 +186,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() @@ -206,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 @@ -219,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(); } @@ -244,7 +237,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; } @@ -277,8 +270,11 @@ 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...\n"); + releaseToPool(p); + } } } @@ -323,8 +319,10 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies) sendToMesh(p); } -void MeshService::onGPSChanged() +int 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; @@ -353,10 +351,6 @@ void MeshService::onGPSChanged() releaseToPool(p); } -} -void MeshService::onNotify(Observable *o) -{ - DEBUG_MSG("got gps notify\n"); - onGPSChanged(); + return 0; } diff --git a/src/MeshService.h b/src/MeshService.h index 884529237..e9d4fe488 100644 --- a/src/MeshService.h +++ b/src/MeshService.h @@ -13,9 +13,9 @@ * Top level app for this service. keeps the mesh, the radio config and the queue of received packets. * */ -class MeshService : private Observer +class MeshService { - MemoryPool packetPool; + CallbackObserver gpsObserver = CallbackObserver(this, &MeshService::onGPSChanged); /// 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 @@ -23,15 +23,25 @@ class MeshService : private Observer /// FIXME - save this to flash on deep sleep PointerQueue toPhoneQueue; + /// The current nonce for the newest packet which has been queued for the phone + uint32_t fromNum = 0; + + public: + 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; - /// The current nonce for the newest packet which has been queued for the phone - uint32_t fromNum; + /// Called when some new packets have arrived from one of the radios + Observable fromNumChanged; - public: - MeshRadio radio; + /// 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(); @@ -76,14 +86,14 @@ 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); + /// returns 0 to allow futher processing + int 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 468f5ade9..1025f8bc0 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 b4de9d83b..eab1a4a30 100644 --- a/src/Observer.h +++ b/src/Observer.h @@ -4,38 +4,93 @@ #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: + /** + * 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; }; -class Observable +/** + * An observer that calls an arbitrary method + */ +template class CallbackObserver : public Observer { - std::list observers; + typedef int (Callback::*ObserverCallback)(T arg); + + Callback *objPtr; + ObserverCallback method; public: - void notifyObservers() + CallbackObserver(Callback *_objPtr, ObserverCallback _method) : objPtr(_objPtr), method(_method) {} + + protected: + virtual int onNotify(T arg) { return (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 + * + * returns !0 if an observer chose to abort processing by returning this code + */ + int 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) { + int result = (*iterator)->onNotify(arg); + if (result != 0) + return result; } + + return 0; } - 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 ce7005ffa..716e4e2c3 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() { @@ -30,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/bare/main-bare.cpp b/src/bare/main-bare.cpp new file mode 100644 index 000000000..29532a639 --- /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 86bdd231b..9d6ca47d3 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/error.h b/src/error.h index cc3328c3b..3fa3f4709 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/MeshBluetoothService.cpp b/src/esp32/MeshBluetoothService.cpp similarity index 96% rename from src/MeshBluetoothService.cpp rename to src/esp32/MeshBluetoothService.cpp index 16af14921..086f0afe6 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,14 @@ 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 int onNotify(uint32_t newValue) + { + setValue(newValue); + notify(); + return 0; + } }; class MyNodeInfoCharacteristic : public ProtobufCharacteristic @@ -244,18 +253,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/esp32/main-esp32.cpp b/src/esp32/main-esp32.cpp new file mode 100644 index 000000000..baf0d85b7 --- /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/main.cpp b/src/main.cpp index 910d45cfe..a10de160b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,15 +21,14 @@ */ -#include "BluetoothUtil.h" #include "GPS.h" -#include "MeshBluetoothService.h" #include "MeshRadio.h" #include "MeshService.h" #include "NodeDB.h" #include "Periodic.h" #include "PowerFSM.h" #include "configuration.h" +#include "error.h" #include "esp32/pm.h" #include "esp_pm.h" #include "power.h" @@ -39,6 +38,10 @@ #include #include +#ifndef NO_ESP32 +#include "BluetoothUtil.h" +#endif + #ifdef TBEAM_V10 #include "axp20x.h" AXP20X_Class axp; @@ -59,8 +62,6 @@ static meshtastic::PowerStatus powerStatus; bool ssd1306_found; bool axp192_found; -bool bluetoothOn; - // ----------------------------------------------------------------------------- // Application // ----------------------------------------------------------------------------- @@ -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 @@ -266,49 +277,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; @@ -350,12 +318,18 @@ void loop() gps.loop(); service.loop(); + if (radio) + radio->loop(); + ledPeriodic.loop(); // axpDebugOutput.loop(); + +#ifndef NO_ESP32 loopBLE(); +#endif // for debug printing - // service.radio.radioIf.canSleep(); + // radio.radioIf.canSleep(); #ifdef PMU_IRQ if (pmu_irq) { diff --git a/src/main.h b/src/main.h index 9842ea96f..c8c379715 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 e204ea5f5..0ae911b04 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -1,24 +1,34 @@ #include "sleep.h" -#include "BluetoothUtil.h" #include "GPS.h" -#include "MeshBluetoothService.h" #include "MeshRadio.h" #include "MeshService.h" #include "NodeDB.h" #include "Periodic.h" #include "configuration.h" +#include "error.h" #include "esp32/pm.h" #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; #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 @@ -98,20 +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(); - - BLEDevice::deinit(false); // We are required to shutdown bluetooth before deep or light sleep + 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 @@ -199,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 49976516b..e63fa70a6 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -1,11 +1,11 @@ #pragma once #include "Arduino.h" +#include "Observer.h" #include "esp_sleep.h" 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 @@ -18,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 diff --git a/src/target_specific.h b/src/target_specific.h new file mode 100644 index 000000000..6acca364a --- /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