From 4147786b12e13baaee04ab8936942f16d3aadc3b Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 15 Jul 2020 13:10:56 -0700 Subject: [PATCH] WIP of adding NRF52 bluetooth API, we take a hardfault in Bluefruit init --- src/BluetoothCommon.h | 14 +++ src/esp32/MeshBluetoothService.cpp | 22 ++-- src/nrf52/NRF52Bluetooth.cpp | 176 ++++++++++++++++------------- src/nrf52/main-nrf52.cpp | 6 +- 4 files changed, 123 insertions(+), 95 deletions(-) diff --git a/src/BluetoothCommon.h b/src/BluetoothCommon.h index 5d551d079..14be8a916 100644 --- a/src/BluetoothCommon.h +++ b/src/BluetoothCommon.h @@ -1,8 +1,22 @@ #pragma once +#include + /** * Common lib functions for all platforms that have bluetooth */ +#define MESH_SERVICE_UUID "6ba1b218-15a8-461f-9fa8-5dcae273eafd" + +#define TORADIO_UUID "f75c76d2-129e-4dad-a1dd-7866124401e7" +#define FROMRADIO_UUID "8ba2bcc2-ee02-4a55-a531-c525c5e454d5" +#define FROMNUM_UUID "ed9da18c-a800-4f66-a670-aa7547e34453" + +// NRF52 wants these constants without the hypen and I'm lazy +#define MESH_SERVICE_UUID_16 ((const uint8_t *)"6ba1b21815a8461f9fa85dcae273eafd") +#define TORADIO_UUID_16 ((const uint8_t *)"f75c76d2129e4dada1dd7866124401e7") +#define FROMRADIO_UUID_16 ((const uint8_t *)"8ba2bcc2ee024a55a531c525c5e454d5") +#define FROMNUM_UUID_16 ((const uint8_t *)"ed9da18ca8004f66a670aa7547e34453") + /// Given a level between 0-100, update the BLE attribute void updateBatteryLevel(uint8_t level); \ No newline at end of file diff --git a/src/esp32/MeshBluetoothService.cpp b/src/esp32/MeshBluetoothService.cpp index 9bc41459a..044bbc4e4 100644 --- a/src/esp32/MeshBluetoothService.cpp +++ b/src/esp32/MeshBluetoothService.cpp @@ -5,6 +5,7 @@ #include #include +#include "BluetoothCommon.h" #include "CallbackCharacteristic.h" #include "GPS.h" #include "MeshService.h" @@ -39,26 +40,20 @@ class BluetoothPhoneAPI : public PhoneAPI } }; -BluetoothPhoneAPI *bluetoothPhoneAPI; - +static BluetoothPhoneAPI *bluetoothPhoneAPI; class ToRadioCharacteristic : public CallbackCharacteristic { public: - ToRadioCharacteristic() : CallbackCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE) {} + ToRadioCharacteristic() : CallbackCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE) {} - void onWrite(BLECharacteristic *c) - { - bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length()); - } + void onWrite(BLECharacteristic *c) { bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length()); } }; class FromRadioCharacteristic : public CallbackCharacteristic { public: - FromRadioCharacteristic() : CallbackCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ) - { - } + FromRadioCharacteristic() : CallbackCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ) {} void onRead(BLECharacteristic *c) { @@ -78,9 +73,8 @@ class FromNumCharacteristic : public CallbackCharacteristic { public: FromNumCharacteristic() - : CallbackCharacteristic("ed9da18c-a800-4f66-a670-aa7547e34453", BLECharacteristic::PROPERTY_WRITE | - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_NOTIFY) + : CallbackCharacteristic(FROMNUM_UUID, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY) { // observe(&service.fromNumChanged); } @@ -100,7 +94,7 @@ BLEService *createMeshBluetoothService(BLEServer *server) } // Create the BLE Service, we need more than the default of 15 handles - BLEService *service = server->createService(BLEUUID("6ba1b218-15a8-461f-9fa8-5dcae273eafd"), 30, 0); + BLEService *service = server->createService(BLEUUID(MESH_SERVICE_UUID), 30, 0); assert(!meshFromNumCharacteristic); meshFromNumCharacteristic = new FromNumCharacteristic; diff --git a/src/nrf52/NRF52Bluetooth.cpp b/src/nrf52/NRF52Bluetooth.cpp index 03e5b8909..ed1f39d2e 100644 --- a/src/nrf52/NRF52Bluetooth.cpp +++ b/src/nrf52/NRF52Bluetooth.cpp @@ -1,23 +1,38 @@ #include "NRF52Bluetooth.h" +#include "BluetoothCommon.h" #include "configuration.h" #include "main.h" -#include "BluetoothCommon.h" #include -/* HRM Service Definitions - * Heart Rate Monitor Service: 0x180D - * Heart Rate Measurement Char: 0x2A37 - * Body Sensor Location Char: 0x2A38 - */ -BLEService hrms = BLEService(UUID16_SVC_HEART_RATE); -BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT); -BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION); +static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16)); +static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16)); +static BLECharacteristic fromRadio = BLECharacteristic(BLEUuid(FROMRADIO_UUID_16)); +static BLECharacteristic toRadio = BLECharacteristic(BLEUuid(TORADIO_UUID_16)); -BLEDis bledis; // DIS (Device Information Service) helper class instance -BLEBas blebas; // BAS (Battery Service) helper class instance -BLEDfu bledfu; // DFU software update helper service +static BLEDis bledis; // DIS (Device Information Service) helper class instance +static BLEBas blebas; // BAS (Battery Service) helper class instance +static BLEDfu bledfu; // DFU software update helper service -uint8_t bps = 0; +// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in +// proccess at once +// static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)]; +static uint8_t fromRadioBytes[FromRadio_size]; + +class BluetoothPhoneAPI : public PhoneAPI +{ + /** + * Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies) + */ + virtual void onNowHasData(uint32_t fromRadioNum) + { + PhoneAPI::onNowHasData(fromRadioNum); + + DEBUG_MSG("BLE notify fromNum\n"); + fromNum.notify32(fromRadioNum); + } +}; + +static BluetoothPhoneAPI *bluetoothPhoneAPI; void connect_callback(uint16_t conn_handle) { @@ -27,7 +42,7 @@ void connect_callback(uint16_t conn_handle) char central_name[32] = {0}; connection->getPeerName(central_name, sizeof(central_name)); - DEBUG_MSG("Connected to %s\n", central_name); + DEBUG_MSG("BLE Connected to %s\n", central_name); } /** @@ -38,9 +53,8 @@ void connect_callback(uint16_t conn_handle) void disconnect_callback(uint16_t conn_handle, uint8_t reason) { (void)conn_handle; - (void)reason; - DEBUG_MSG("Disconnected, reason = 0x%x\n", reason); + DEBUG_MSG("BLE Disconnected, reason = 0x%x\n", reason); } void cccd_callback(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value) @@ -50,11 +64,11 @@ void cccd_callback(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_valu // Check the characteristic this CCCD update is associated with in case // this handler is used for multiple CCCD records. - if (chr->uuid == hrmc.uuid) { + if (chr->uuid == fromNum.uuid) { if (chr->notifyEnabled(conn_hdl)) { - DEBUG_MSG("Heart Rate Measurement 'Notify' enabled\n"); + DEBUG_MSG("fromNum 'Notify' enabled\n"); } else { - DEBUG_MSG("Heart Rate Measurement 'Notify' disabled\n"); + DEBUG_MSG("fromNum 'Notify' disabled\n"); } } } @@ -66,7 +80,7 @@ void startAdv(void) Bluefruit.Advertising.addTxPower(); // Include HRM Service UUID - Bluefruit.Advertising.addService(hrms); + Bluefruit.Advertising.addService(meshBleService); // Include Name Bluefruit.Advertising.addName(); @@ -86,67 +100,72 @@ void startAdv(void) Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds } -void setupHRM(void) +/** + * client is starting read, pull the bytes from our API class + */ +void fromRadioAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request) { - // Configure the Heart Rate Monitor service - // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml - // Supported Characteristics: - // Name UUID Requirement Properties - // ---------------------------- ------ ----------- ---------- - // Heart Rate Measurement 0x2A37 Mandatory Notify - // Body Sensor Location 0x2A38 Optional Read - // Heart Rate Control Point 0x2A39 Conditional Write <-- Not used here - hrms.begin(); + size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes); + + DEBUG_MSG("fromRadioAuthorizeCb numBytes=%u\n", numBytes); + + // 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 + chr->write(fromRadioBytes, numBytes); +} + +void toRadioWriteCb(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len) +{ + DEBUG_MSG("toRadioWriteCb data %p, len %u\n", data, len); + + bluetoothPhoneAPI->handleToRadio(data, len); +} + +/** + * client is starting read, pull the bytes from our API class + */ +void fromNumAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request) +{ + DEBUG_MSG("fromNumAuthorizeCb FIXME - implement\n"); +} + +void setupMeshService(void) +{ + bluetoothPhoneAPI = new BluetoothPhoneAPI(); + bluetoothPhoneAPI->init(); + + meshBleService.begin(); // Note: You must call .begin() on the BLEService before calling .begin() on // any characteristic(s) within that service definition.. Calling .begin() on // a BLECharacteristic will cause it to be added to the last BLEService that // was 'begin()'ed! - // Configure the Heart Rate Measurement characteristic - // See: - // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml - // Properties = Notify - // Min Len = 1 - // Max Len = 8 - // B0 = UINT8 - Flag (MANDATORY) - // b5:7 = Reserved - // b4 = RR-Internal (0 = Not present, 1 = Present) - // b3 = Energy expended status (0 = Not present, 1 = Present) - // b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and - // detected) b0 = Value format (0 = UINT8, 1 = UINT16) - // B1 = UINT8 - 8-bit heart rate measurement value in BPM - // B2:3 = UINT16 - 16-bit heart rate measurement value in BPM - // B4:5 = UINT16 - Energy expended in joules - // B6:7 = UINT16 - RR Internal (1/1024 second resolution) - hrmc.setProperties(CHR_PROPS_NOTIFY); - hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); - hrmc.setFixedLen(2); - hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates - hrmc.begin(); - uint8_t hrmdata[2] = {0b00000110, 0x40}; // Set the characteristic to use 8-bit values, with the sensor connected and detected - hrmc.write(hrmdata, 2); + fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ); + fromNum.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME, secure this!!! + fromNum.setFixedLen( + 0); // Variable len (either 0 or 4) FIXME consider changing protocol so it is fixed 4 byte len, where 0 means empty + fromNum.setMaxLen(4); + fromNum.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates + fromNum.setReadAuthorizeCallback(fromNumAuthorizeCb); + fromNum.begin(); + // uint8_t hrmdata[2] = {0b00000110, 0x40}; // Set the characteristic to use 8-bit values, with the sensor connected and + // detected + // hrmc.write(hrmdata, 2); - // Configure the Body Sensor Location characteristic - // See: - // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml - // Properties = Read - // Min Len = 1 - // Max Len = 1 - // B0 = UINT8 - Body Sensor Location - // 0 = Other - // 1 = Chest - // 2 = Wrist - // 3 = Finger - // 4 = Hand - // 5 = Ear Lobe - // 6 = Foot - // 7:255 = Reserved - bslc.setProperties(CHR_PROPS_READ); - bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); - bslc.setFixedLen(1); - bslc.begin(); - bslc.write8(2); // Set the characteristic to 'Wrist' (2) + fromRadio.setProperties(CHR_PROPS_READ); + fromRadio.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME secure this! + fromRadio.setMaxLen(512); + fromRadio.setReadAuthorizeCallback(fromRadioAuthorizeCb); + fromRadio.setBuffer(fromRadioBytes, + sizeof(fromRadioBytes)); // we preallocate our fromradio buffer so we won't waste space for two copies + fromRadio.begin(); + + toRadio.setProperties(CHR_PROPS_WRITE); + toRadio.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME secure this! + toRadio.setMaxLen(512); + toRadio.setWriteCallback(toRadioWriteCb); + toRadio.begin(); } // FIXME, turn off soft device access for debugging @@ -174,14 +193,14 @@ void NRF52Bluetooth::setup() // Start the BLE Battery Service and set it to 100% DEBUG_MSG("Configuring the Battery Service\n"); blebas.begin(); - blebas.write(42); // FIXME, report real power levels + blebas.write(0); // Unknown battery level for now bledfu.begin(); // Install the DFU helper // Setup the Heart Rate Monitor service using // BLEService and BLECharacteristic classes - DEBUG_MSG("Configuring the Heart Rate Monitor Service\n"); - setupHRM(); + DEBUG_MSG("Configuring the Mesh bluetooth service\n"); + setupMeshService(); // Supposedly debugging works with soft device if you disable advertising if (isSoftDeviceAllowed) { @@ -194,8 +213,9 @@ void NRF52Bluetooth::setup() } /// Given a level between 0-100, update the BLE attribute -void updateBatteryLevel(uint8_t level) { - // FIXME - implement +void updateBatteryLevel(uint8_t level) +{ + blebas.write(level); } /* diff --git a/src/nrf52/main-nrf52.cpp b/src/nrf52/main-nrf52.cpp index 9f81f074c..d2fd1e060 100644 --- a/src/nrf52/main-nrf52.cpp +++ b/src/nrf52/main-nrf52.cpp @@ -52,9 +52,9 @@ void setBluetoothEnable(bool on) if (on != bleOn) { if (on) { if (!nrf52Bluetooth) { - DEBUG_MSG("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n"); - //nrf52Bluetooth = new NRF52Bluetooth(); - //nrf52Bluetooth->setup(); + // DEBUG_MSG("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n"); + nrf52Bluetooth = new NRF52Bluetooth(); + nrf52Bluetooth->setup(); } } else { DEBUG_MSG("FIXME: implement BLE disable\n");