From 356902d552f1501200a3e416b6b42b984c967bd7 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sun, 23 Feb 2020 10:49:37 -0800 Subject: [PATCH] wip - make ble come back after sleep --- TODO.md | 1 + .../src/BluetoothSoftwareUpdate.cpp | 2 +- lib/BluetoothOTA/src/BluetoothUtil.cpp | 20 ++- lib/BluetoothOTA/src/BluetoothUtil.h | 7 +- lib/BluetoothOTA/src/SimpleAllocator.cpp | 20 +++ lib/BluetoothOTA/src/SimpleAllocator.h | 33 ++++ src/MeshBluetoothService.cpp | 161 ++++++++++-------- src/main.ino | 59 +++++-- src/sleep.cpp | 26 +-- 9 files changed, 208 insertions(+), 121 deletions(-) create mode 100644 lib/BluetoothOTA/src/SimpleAllocator.cpp create mode 100644 lib/BluetoothOTA/src/SimpleAllocator.h diff --git a/TODO.md b/TODO.md index e64665f01..59fbd8611 100644 --- a/TODO.md +++ b/TODO.md @@ -48,6 +48,7 @@ Items to complete before the first beta release. During the beta timeframe the following improvements 'would be nice' (and yeah - I guess some of these items count as features, but it is a hobby project ;-) ) +* use BLEDevice::setPower to lower our BLE transmit power - extra range doesn't help us, it costs amps and it increases snoopability * make an install script to let novices install software on their boards * fix the frequency error reading in the RF95 RX code (can't do floating point math in an ISR ;-) * See CustomRF95::send and fix the problem of dropping partially received packets if we want to start sending diff --git a/lib/BluetoothOTA/src/BluetoothSoftwareUpdate.cpp b/lib/BluetoothOTA/src/BluetoothSoftwareUpdate.cpp index daf2cb984..e5401c326 100644 --- a/lib/BluetoothOTA/src/BluetoothSoftwareUpdate.cpp +++ b/lib/BluetoothOTA/src/BluetoothSoftwareUpdate.cpp @@ -109,7 +109,7 @@ BLEService *createUpdateService(BLEServer* server) { swUpdateDataCharacteristic.setCallbacks(&updateCb); swUpdateCRC32Characteristic.setCallbacks(&updateCb); - swUpdateResultCharacteristic.addDescriptor(new BLE2902()); // Needed so clients can request notification + swUpdateResultCharacteristic.addDescriptor(new(btPool) BLE2902()); // Needed so clients can request notification return service; } diff --git a/lib/BluetoothOTA/src/BluetoothUtil.cpp b/lib/BluetoothOTA/src/BluetoothUtil.cpp index 8a756a6ca..9d22d8795 100644 --- a/lib/BluetoothOTA/src/BluetoothUtil.cpp +++ b/lib/BluetoothOTA/src/BluetoothUtil.cpp @@ -7,6 +7,8 @@ #include "configuration.h" #include "screen.h" +SimpleAllocator btPool; + static BLECharacteristic SWVersionCharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ); static BLECharacteristic ManufacturerCharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_MANU_NAME), BLECharacteristic::PROPERTY_READ); static BLECharacteristic HardwareVersionCharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ); @@ -66,13 +68,17 @@ class MyServerCallbacks : public BLEServerCallbacks } }; + + + + // Help routine to add a description to any BLECharacteristic and add it to the service // We default to require an encrypted BOND for all these these characterstics void addWithDesc(BLEService *service, BLECharacteristic *c, const char *description) { c->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); - BLEDescriptor *desc = new BLEDescriptor(BLEUUID((uint16_t)ESP_GATT_UUID_CHAR_DESCRIPTION), strlen(description) + 1); + BLEDescriptor *desc = new(btPool) BLEDescriptor(BLEUUID((uint16_t)ESP_GATT_UUID_CHAR_DESCRIPTION), strlen(description) + 1); assert(desc); desc->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); desc->setValue(description); @@ -82,6 +88,7 @@ void addWithDesc(BLEService *service, BLECharacteristic *c, const char *descript static BLECharacteristic BatteryLevelCharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_BATTERY_LEVEL), BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); + /** * Create a battery level service */ @@ -91,7 +98,7 @@ BLEService *createBatteryService(BLEServer *server) BLEService *pBattery = server->createService(BLEUUID((uint16_t)0x180F)); addWithDesc(pBattery, &BatteryLevelCharacteristic, "Percentage 0 - 100"); - BatteryLevelCharacteristic.addDescriptor(new BLE2902()); // Needed so clients can request notification + BatteryLevelCharacteristic.addDescriptor(new(btPool) BLE2902()); // Needed so clients can request notification // I don't think we need to advertise this // server->getAdvertising()->addServiceUUID(pBattery->getUUID()); @@ -190,11 +197,13 @@ BLEServer *initBLE(std::string deviceName, std::string hwVendor, std::string swV /* * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation */ - BLEDevice::setSecurityCallbacks(new MySecurity()); + static MySecurity mySecurity; + BLEDevice::setSecurityCallbacks(&mySecurity); // Create the BLE Server BLEServer *pServer = BLEDevice::createServer(); - pServer->setCallbacks(new MyServerCallbacks()); + static MyServerCallbacks myCallbacks; + pServer->setCallbacks(&myCallbacks); BLEService *pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion); @@ -213,7 +222,8 @@ BLEServer *initBLE(std::string deviceName, std::string hwVendor, std::string swV // FIXME turn on this restriction only after the device is paired with a phone // advert->setScanFilter(false, true); // We let anyone scan for us (FIXME, perhaps only allow that until we are paired with a phone and configured) but only let whitelist phones connect - BLESecurity *pSecurity = new BLESecurity(); + static BLESecurity security; // static to avoid allocs + BLESecurity *pSecurity = &security; pSecurity->setCapability(ESP_IO_CAP_OUT); pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); diff --git a/lib/BluetoothOTA/src/BluetoothUtil.h b/lib/BluetoothOTA/src/BluetoothUtil.h index 1f1cd9cb8..2bb13c165 100644 --- a/lib/BluetoothOTA/src/BluetoothUtil.h +++ b/lib/BluetoothOTA/src/BluetoothUtil.h @@ -4,7 +4,7 @@ #include #include #include - +#include "SimpleAllocator.h" // Now handled by BluetoothUtil.cpp // BLEService *createDeviceInfomationService(BLEServer* server, uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version); @@ -18,4 +18,7 @@ void dumpCharacteristic(BLECharacteristic *c); uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue); void loopBLE(); -BLEServer *initBLE(std::string devName, std::string hwVendor, std::string swVersion, std::string hwVersion = ""); \ No newline at end of file +BLEServer *initBLE(std::string devName, std::string hwVendor, std::string swVersion, std::string hwVersion = ""); + +/// Any bluetooth objects you allocate _must_ come from this pool if you want to be able to call destroyBLE() +extern SimpleAllocator btPool; diff --git a/lib/BluetoothOTA/src/SimpleAllocator.cpp b/lib/BluetoothOTA/src/SimpleAllocator.cpp new file mode 100644 index 000000000..1fa3c203a --- /dev/null +++ b/lib/BluetoothOTA/src/SimpleAllocator.cpp @@ -0,0 +1,20 @@ +#include "SimpleAllocator.h" +#include "assert.h" + +SimpleAllocator::SimpleAllocator() { reset(); } + +void *SimpleAllocator::alloc(size_t size) +{ + assert(nextFree + size <= sizeof(bytes)); + void *res = &bytes[nextFree]; + nextFree += size; + + return res; +} + +void SimpleAllocator::reset() { nextFree = 0; } + +void *operator new(size_t size, SimpleAllocator &p) +{ + return p.alloc(size); +} diff --git a/lib/BluetoothOTA/src/SimpleAllocator.h b/lib/BluetoothOTA/src/SimpleAllocator.h new file mode 100644 index 000000000..82c2dbce0 --- /dev/null +++ b/lib/BluetoothOTA/src/SimpleAllocator.h @@ -0,0 +1,33 @@ +#pragma once +#include + +#define POOL_SIZE 32768 + +/** + * An allocator (and placement new operator) that allocates storage from a fixed sized buffer. + * It will panic if that buffer fills up. + * If you are _sure_ no outstanding references to blocks in this buffer still exist, you can call + * reset() to start from scratch. + * + * Currently the only usecase for this class is the ESP32 bluetooth stack, where once we've called deinit(false) + * we are sure all those bluetooth objects no longer exist, and we'll need to recreate them when we restart bluetooth + */ +class SimpleAllocator +{ + uint8_t bytes[POOL_SIZE]; + + uint32_t nextFree; + +public: + SimpleAllocator(); + + void *alloc(size_t size); + + /** If you are _sure_ no outstanding references to blocks in this buffer still exist, you can call + * reset() to start from scratch. + * */ + void reset(); +}; + +void *operator new(size_t size, SimpleAllocator &p); + diff --git a/src/MeshBluetoothService.cpp b/src/MeshBluetoothService.cpp index a97692f1a..bb735f599 100644 --- a/src/MeshBluetoothService.cpp +++ b/src/MeshBluetoothService.cpp @@ -32,14 +32,27 @@ public: } }; -class ProtobufCharacteristic : public BLECharacteristic, public BLEKeepAliveCallbacks +/** + * A characterstic with a set of overridable callbacks + */ +class CallbackCharacteristic : public BLECharacteristic, public BLEKeepAliveCallbacks +{ +public: + CallbackCharacteristic(const char *uuid, uint32_t btprops) + : BLECharacteristic(uuid, btprops) + { + setCallbacks(this); + } +}; + +class ProtobufCharacteristic : public CallbackCharacteristic { const pb_msgdesc_t *fields; void *my_struct; public: ProtobufCharacteristic(const char *uuid, uint32_t btprops, const pb_msgdesc_t *_fields, void *_my_struct) - : BLECharacteristic(uuid, btprops), + : CallbackCharacteristic(uuid, btprops), fields(_fields), my_struct(_my_struct) { @@ -165,11 +178,82 @@ public: } }; -static BLECharacteristic - meshFromRadioCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ), - meshToRadioCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE), - meshFromNumCharacteristic("ed9da18c-a800-4f66-a670-aa7547e34453", BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); +class ToRadioCharacteristic : public CallbackCharacteristic +{ +public: + ToRadioCharacteristic() + : CallbackCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE) + { + } + void onWrite(BLECharacteristic *c) + { + BLEKeepAliveCallbacks::onWrite(c); + DEBUG_MSG("Got on write\n"); + + service.handleToRadio(c->getValue()); + } +}; + +class FromRadioCharacteristic : public CallbackCharacteristic +{ +public: + FromRadioCharacteristic() + : CallbackCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ) + { + } + + void onRead(BLECharacteristic *c) + { + BLEKeepAliveCallbacks::onRead(c); + MeshPacket *mp = service.getForPhone(); + + // Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue + // or make empty if the queue is empty + if (!mp) + { + DEBUG_MSG("toPhone queue is empty\n"); + c->setValue((uint8_t *)"", 0); + } + else + { + DEBUG_MSG("delivering toPhone packet to phone\n"); + + static FromRadio fradio; + + // Encapsulate as a ToRadio packet + memset(&fradio, 0, sizeof(fradio)); + fradio.which_variant = FromRadio_packet_tag; + fradio.variant.packet = *mp; + + service.releaseToPool(mp); // we just copied the bytes, so don't need this buffer anymore + + size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), FromRadio_fields, &fradio); + c->setValue(trBytes, numbytes); + } + } +}; + +class FromNumCharacteristic : public CallbackCharacteristic +{ +public: + FromNumCharacteristic() + : CallbackCharacteristic("ed9da18c-a800-4f66-a670-aa7547e34453", + BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY) + { + } + + void onRead(BLECharacteristic *c) + { + BLEKeepAliveCallbacks::onRead(c); + DEBUG_MSG("FIXME implement fromnum read\n"); + } +}; + +static FromNumCharacteristic meshFromNumCharacteristic; + +static FromRadioCharacteristic meshFromRadioCharacteristic; +static ToRadioCharacteristic meshToRadioCharacteristic; static NodeInfoCharacteristic meshNodeInfoCharacteristic; static ProtobufCharacteristic @@ -187,65 +271,6 @@ void bluetoothNotifyFromNum(uint32_t newValue) meshFromNumCharacteristic.notify(); } -class BluetoothMeshCallbacks : public BLEKeepAliveCallbacks -{ - void onRead(BLECharacteristic *c) - { - BLEKeepAliveCallbacks::onRead(c); - DEBUG_MSG("Got on read\n"); - - if (c == &meshFromRadioCharacteristic) - { - MeshPacket *mp = service.getForPhone(); - - // Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue - // or make empty if the queue is empty - if (!mp) - { - DEBUG_MSG("toPhone queue is empty\n"); - c->setValue((uint8_t *)"", 0); - } - else - { - DEBUG_MSG("delivering toPhone packet to phone\n"); - - static FromRadio fradio; - - // Encapsulate as a ToRadio packet - memset(&fradio, 0, sizeof(fradio)); - fradio.which_variant = FromRadio_packet_tag; - fradio.variant.packet = *mp; - - service.releaseToPool(mp); // we just copied the bytes, so don't need this buffer anymore - - size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), FromRadio_fields, &fradio); - c->setValue(trBytes, numbytes); - } - } - else - { - // we are uninterested in the other reads - } - } - - void onWrite(BLECharacteristic *c) - { - BLEKeepAliveCallbacks::onWrite(c); - DEBUG_MSG("Got on write\n"); - - if (c == &meshToRadioCharacteristic) - { - service.handleToRadio(c->getValue()); - } - else - { - // we are uninterested in the other writes - } - } -}; - -static BluetoothMeshCallbacks btMeshCb; - /* MeshBluetoothService UUID 6ba1b218-15a8-461f-9fa8-5dcae273eafd @@ -311,16 +336,12 @@ BLEService *createMeshBluetoothService(BLEServer *server) addWithDesc(service, &meshToRadioCharacteristic, "toRadio"); addWithDesc(service, &meshFromNumCharacteristic, "fromNum"); - meshFromRadioCharacteristic.setCallbacks(&btMeshCb); - meshToRadioCharacteristic.setCallbacks(&btMeshCb); - meshFromNumCharacteristic.setCallbacks(&btMeshCb); - addWithDesc(service, &meshMyNodeCharacteristic, "myNode"); addWithDesc(service, &meshRadioCharacteristic, "radio"); addWithDesc(service, &meshOwnerCharacteristic, "owner"); addWithDesc(service, &meshNodeInfoCharacteristic, "nodeinfo"); - meshFromNumCharacteristic.addDescriptor(new BLE2902()); // Needed so clients can request notification + meshFromNumCharacteristic.addDescriptor(new (btPool) BLE2902()); // Needed so clients can request notification service->start(); server->getAdvertising()->addServiceUUID(service->getUUID()); diff --git a/src/main.ino b/src/main.ino index 47e5d538f..55622d0a5 100644 --- a/src/main.ino +++ b/src/main.ino @@ -44,11 +44,15 @@ AXP20X_Class axp; bool pmu_irq = false; #endif -bool isCharging = false; -bool isUSBPowered = false; -bool ssd1306_found = false; -bool axp192_found = false; +// these flags are all in bss so they default false +bool isCharging; +bool isUSBPowered; + +bool ssd1306_found; +bool axp192_found; + +bool bluetoothOn; #define xstr(s) str(s) #define str(s) #s @@ -250,24 +254,43 @@ void setup() service.init(); - bool useBluetooth = true; - if (useBluetooth) - { - DEBUG_MSG("Starting bluetooth\n"); - BLEServer *serve = initBLE(getDeviceName(), HW_VENDOR, str(APP_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(); - } - - setBluetoothEnable(false); + // setBluetoothEnable(false); we now don't start bluetooth until we enter the proper state setCPUFast(false); // 80MHz is fine for our slow peripherals PowerFSM_setup(); powerFSM.trigger(EVENT_BOOT); // transition to ON, FIXME, only do this for cold boots, not waking from SDS } +void initBluetooth() +{ + DEBUG_MSG("Starting bluetooth\n"); + BLEServer *serve = initBLE(getDeviceName(), HW_VENDOR, str(APP_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) + { + initBluetooth(); + } + else + { + // FIXME - we are leaking like crazy + BLEDevice::deinit(false); + btPool.reset(); + } + } +} + uint32_t ledBlinker() { static bool ledOn; @@ -337,7 +360,7 @@ void loop() static uint32_t minPressMs; // what tick should we call this press long enough static uint32_t lastPingMs; - if (!digitalRead(BUTTON_PIN)) + if (!digitalRead(BUTTON_PIN)) { if (!wasPressed) { // just started a new press @@ -371,7 +394,7 @@ void loop() // ESP.restart(); } } - #endif +#endif // No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in) // i.e. don't just keep spinning in loop as fast as we can. diff --git a/src/sleep.cpp b/src/sleep.cpp index 31d634940..91b3785a7 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -14,6 +14,7 @@ #include "esp_pm.h" #include "MeshRadio.h" #include "main.h" +#include "sleep.h" #ifdef T_BEAM_V10 #include "axp20x.h" @@ -27,9 +28,6 @@ esp_sleep_source_t wakeCause; // the reason we booted this time #define xstr(s) str(s) #define str(s) #s -#include "esp_bt_main.h" - -bool bluetoothOn = true; // we turn it on during setup() so default on // ----------------------------------------------------------------------------- @@ -178,29 +176,7 @@ void doDeepSleep(uint64_t msecToWake) } -void setBluetoothEnable(bool on) -{ - if (on != bluetoothOn) - { - DEBUG_MSG("Setting bluetooth enable=%d\n", on); - bluetoothOn = on; - if (on) - { - if (esp_bt_controller_enable(ESP_BT_MODE_BTDM) != ESP_OK) - DEBUG_MSG("error reenabling bt controller\n"); - if (esp_bluedroid_enable() != ESP_OK) - DEBUG_MSG("error reenabling bluedroid\n"); - } - else - { - if (esp_bluedroid_disable() != ESP_OK) - DEBUG_MSG("error disabling bluedroid\n"); - if (esp_bt_controller_disable() != ESP_OK) - DEBUG_MSG("error disabling bt controller\n"); - } - } -} /** * enter light sleep (preserves ram but stops everything about CPU).