From 5b0451f25c32365d695f2646d7ad0762f9419a32 Mon Sep 17 00:00:00 2001 From: geeksville Date: Thu, 23 Apr 2020 18:02:28 -0700 Subject: [PATCH] add NRF52 BLE example code --- docs/software/nrf52-TODO.md | 29 +++-- src/bare/NRF52Bluetooth.cpp | 206 ++++++++++++++++++++++++++++++++++++ src/bare/NRF52Bluetooth.h | 8 ++ src/bare/main-bare.cpp | 5 - src/bare/main-nrf52.cpp | 22 +++- 5 files changed, 257 insertions(+), 13 deletions(-) create mode 100644 src/bare/NRF52Bluetooth.cpp create mode 100644 src/bare/NRF52Bluetooth.h diff --git a/docs/software/nrf52-TODO.md b/docs/software/nrf52-TODO.md index 88720e0f..c9912bec 100644 --- a/docs/software/nrf52-TODO.md +++ b/docs/software/nrf52-TODO.md @@ -1,12 +1,26 @@ +# Initial work items -* make a new boarddef with a variant.h file. Fix pins in that file. In particular: -#define PIN_SPI_MISO (46) -#define PIN_SPI_MOSI (45) -#define PIN_SPI_SCK (47) -#define PIN_WIRE_SDA (26) -#define PIN_WIRE_SCL (27) +- get old radio driver working on NRF52 +- get BLE working +- add PMU driver +- add new radio driver +- make a file system implementation (preferably one that can see the files the bootloader also sees) +- add LCD driver +- make a new boarddef with a variant.h file. Fix pins in that file. In particular: + #define PIN_SPI_MISO (46) + #define PIN_SPI_MOSI (45) + #define PIN_SPI_SCK (47) + #define PIN_WIRE_SDA (26) + #define PIN_WIRE_SCL (27) +# Secondary work items +- turn on security for BLE +- make power management/sleep work properly +- make a settimeofday implementation +- make ble endpoints not require "start config", jsut have them start in config mode + +``` /* per https://docs.platformio.org/en/latest/tutorials/nordicnrf52/arduino_debugging_unit_testing.html @@ -56,4 +70,5 @@ https://learn.adafruit.com/bluefruit-nrf52-feather-learning-guide/custom-hrm /* good led ble demo: https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/libraries/Bluefruit52Lib/examples/Peripheral/nrf_blinky/nrf_blinky.ino -*/ \ No newline at end of file +*/ +``` diff --git a/src/bare/NRF52Bluetooth.cpp b/src/bare/NRF52Bluetooth.cpp new file mode 100644 index 00000000..843a6117 --- /dev/null +++ b/src/bare/NRF52Bluetooth.cpp @@ -0,0 +1,206 @@ +#include "NRF52Bluetooth.h" +#include "configuration.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); + +BLEDis bledis; // DIS (Device Information Service) helper class instance +BLEBas blebas; // BAS (Battery Service) helper class instance + +uint8_t bps = 0; + +void connect_callback(uint16_t conn_handle) +{ + // Get the reference to current connection + BLEConnection *connection = Bluefruit.Connection(conn_handle); + + char central_name[32] = {0}; + connection->getPeerName(central_name, sizeof(central_name)); + + DEBUG_MSG("Connected to %s\n", central_name); +} + +/** + * Callback invoked when a connection is dropped + * @param conn_handle connection where this event happens + * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h + */ +void disconnect_callback(uint16_t conn_handle, uint8_t reason) +{ + (void)conn_handle; + (void)reason; + + DEBUG_MSG("Disconnected, reason = 0x%x\n", reason); +} + +void cccd_callback(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value) +{ + // Display the raw request packet + DEBUG_MSG("CCCD Updated: %u\n", cccd_value); + + // 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->notifyEnabled(conn_hdl)) { + DEBUG_MSG("Heart Rate Measurement 'Notify' enabled\n"); + } else { + DEBUG_MSG("Heart Rate Measurement 'Notify' disabled\n"); + } + } +} + +void startAdv(void) +{ + // Advertising packet + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + + // Include HRM Service UUID + Bluefruit.Advertising.addService(hrms); + + // Include Name + Bluefruit.Advertising.addName(); + + /* Start Advertising + * - Enable auto advertising if disconnected + * - Interval: fast mode = 20 ms, slow mode = 152.5 ms + * - Timeout for fast mode is 30 seconds + * - Start(timeout) with timeout = 0 will advertise forever (until connected) + * + * For recommended advertising interval + * https://developer.apple.com/library/content/qa/qa1931/_index.html + */ + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms + Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode + Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds +} + +void setupHRM(void) +{ + // 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(); + + // 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); + + // 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) +} + +void NRF52Bluetooth::setup() +{ + // Initialise the Bluefruit module + DEBUG_MSG("Initialise the Bluefruit nRF52 module\n"); + Bluefruit.begin(); + + // Set the advertised device name (keep it short!) + Bluefruit.setName("Meshtastic52"); + + // Set the connect/disconnect callback handlers + Bluefruit.Periph.setConnectCallback(connect_callback); + Bluefruit.Periph.setDisconnectCallback(disconnect_callback); + + // Configure and Start the Device Information Service + DEBUG_MSG("Configuring the Device Information Service"); + bledis.setManufacturer("meshtastic.org"); + bledis.setModel("NRF52-meshtastic"); + bledis.begin(); + + // Start the BLE Battery Service and set it to 100% + DEBUG_MSG("Configuring the Battery Service"); + blebas.begin(); + blebas.write(42); // FIXME, report real power levels + + // Setup the Heart Rate Monitor service using + // BLEService and BLECharacteristic classes + DEBUG_MSG("Configuring the Heart Rate Monitor Service"); + setupHRM(); + + // Setup the advertising packet(s) + DEBUG_MSG("Setting up the advertising payload(s)\n"); + startAdv(); + + DEBUG_MSG("Advertising\n"); +} + +/* +void loop() +{ + digitalToggle(LED_RED); + + if ( Bluefruit.connected() ) { + uint8_t hrmdata[2] = { 0b00000110, bps++ }; // Sensor connected, increment BPS value + + // Note: We use .notify instead of .write! + // If it is connected but CCCD is not enabled + // The characteristic's value is still updated although notification is not sent + if ( hrmc.notify(hrmdata, sizeof(hrmdata)) ){ + Serial.print("Heart Rate Measurement updated to: "); Serial.println(bps); + }else{ + Serial.println("ERROR: Notify not set in the CCCD or not connected!"); + } + } + + // Only send update once per second + delay(1000); +} +*/ \ No newline at end of file diff --git a/src/bare/NRF52Bluetooth.h b/src/bare/NRF52Bluetooth.h new file mode 100644 index 00000000..40f13d8b --- /dev/null +++ b/src/bare/NRF52Bluetooth.h @@ -0,0 +1,8 @@ +#pragma once + +class NRF52Bluetooth +{ + public: + void setup(); +}; + diff --git a/src/bare/main-bare.cpp b/src/bare/main-bare.cpp index d327c238..fb1b4a27 100644 --- a/src/bare/main-bare.cpp +++ b/src/bare/main-bare.cpp @@ -1,7 +1,2 @@ #include "target_specific.h" -void setBluetoothEnable(bool on) -{ - // Do nothing -} - diff --git a/src/bare/main-nrf52.cpp b/src/bare/main-nrf52.cpp index 2c4bcbc1..89180950 100644 --- a/src/bare/main-nrf52.cpp +++ b/src/bare/main-nrf52.cpp @@ -1,3 +1,5 @@ +#include "NRF52Bluetooth.h" +#include "configuration.h" #include #include #include @@ -31,4 +33,22 @@ void getMacAddr(uint8_t *dmac) // FIXME - byte order might be wrong and high bits might be wrong memcpy(dmac, (const void *)NRF_FICR->DEVICEADDR, 6); #endif -} \ No newline at end of file +} + +NRF52Bluetooth *nrf52Bluetooth; + +static bool bleOn = false; +void setBluetoothEnable(bool on) +{ + if (on != bleOn) { + if (on) { + if (!nrf52Bluetooth) { + nrf52Bluetooth = new NRF52Bluetooth(); + nrf52Bluetooth->setup(); + } + } else { + DEBUG_MSG("FIXME: implement BLE disable\n"); + } + bleOn = on; + } +}