Bill Siever
/
nRF5-DK-HeartRateDemo_C
Fork of nRF5-DK-HeartRateDemo by
Diff: source/main.cpp
- Revision:
- 3:f593ad98fe21
- Parent:
- 2:b850666f3c8f
- Child:
- 4:6b6018da25f6
--- a/source/main.cpp Fri Nov 11 19:25:56 2016 +0000 +++ b/source/main.cpp Fri Nov 11 20:33:07 2016 +0000 @@ -3,10 +3,10 @@ #include "ble/BLE.h" #include "ble/Gap.h" -// Uses the provided Service Classes: -// (Local copies with comments are used rather than "ble/services/NAME" versions -#include "DeviceInformationService.h" -#include "HeartRateService.h" +// See the official heart rate service definitions: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml +// This version shows a working heart rate service created with a "C style" (non object-oriented) approach. +// It uses 8-bit heart rate values and includes "energy expended" values. + // If debug is "true", include code to print debugging messages to the "console" #define DEBUG 1 @@ -19,27 +19,48 @@ /*********************************************************************** I. Global Variables: This section declares "global variable" (variables that are used - in multiple functions in this file) + in multiple functions in this file and need to exist the entire time + code is running (i.e., the concept of global is about both their scope + and their lifetime) ************************************************************************/ +// A. UUIDs for the service and its characteristics (they won't change, so declared const) +const UUID HRMS_SERV(0x180D); +const UUID HRMS_HRM_CHAR(0x2A37); +const UUID HRMS_BODYSENSELOC_CHAR(0x2A38); +const UUID HRMS_CONTROLPOINT_CHAR(0x2A39); + +// B. Create variables for any actual data +uint8_t hrmHeartRateData[4] = {0x08,0,0,0}; // 1st byte: flags, 2nd byte heart date, 3-4th bytes: energy expended + // Flags 0x8 indicates energy expended is included and heart rate is an 8-bit value. +uint8_t hrmBodySensorLocation = 0; // 0 is code for "Other". See https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml +uint8_t hrmControlPoint = 0; + -// A. A way to keep track of the heart rate service object -// Declare a pointer to a heart rate service object. (This variable isn't -// an actual object. It's used like Java Reference Variables --- it can refer -// to a heart rate service object, but doesn't until it is initialized) -// Pointers are used because the heart rate service constructor requires a BLE -// object in its constructors, which isn't available when the program first starts -// The Object won't exist until created with "new" ( - HeartRateService *pHrs; +// C. Create "pointers" (references) to the characteristics +// (The actual objects are constructed at run-time) +GattCharacteristic hrmRateChar(HRMS_HRM_CHAR, // UUID to use + hrmHeartRateData, // Pointer to data to use (arrays ARE pointers) + 4, // Number of bytes (current data) + 4, // Number of bytes (max) + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); // Permissions -// B. A way to create new "events" +// The versions below use templated classes (a congenience for a few "common" characteristic permissions) +ReadOnlyGattCharacteristic<uint8_t> hrmLocationChar(HRMS_BODYSENSELOC_CHAR, // UUID to use + &hrmBodySensorLocation); // Data to use + +WriteOnlyGattCharacteristic<uint8_t> hrmControlPointChar(HRMS_CONTROLPOINT_CHAR, // UUID + &hrmControlPoint); // Data to use + + + + +// D. A way to create new "events" /* Total size of space for eventQueue = event count * event size */ EventQueue eventQueue( 16 * 32); -// C. Setup the interrupt pins for any buttons +// D. Setup the interrupt pins for any buttons InterruptIn buttons[4] = { InterruptIn(P0_13), InterruptIn(P0_14), InterruptIn(P0_15), InterruptIn(P0_16) }; -// D. Variable to contain the heart rate value -uint8_t heartRate = 0; /*********************************************************************** @@ -57,6 +78,40 @@ } + +void onDataWritten(const GattWriteCallbackParams *params) { + LOG_PRINTF(__func__); // Print the function's name + // See: https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/docs/65474dc93927/GattCallbackParamTypes_8h_source.html for structure + // A write of 1 to the "control point" should reset the energy expended + if (params->handle == hrmControlPointChar.getValueAttribute().getHandle()) { + LOG_PRINTF("Writing to HRM Control Point"); + if(params->len == 1 && params->data[0]==1) { + LOG_PRINTF("Clearing HRM Control Point"); + // If it has the correct length and data, reset + hrmHeartRateData[2] = 0; + hrmHeartRateData[3] = 0; + // Get the BLE object + BLE& ble = BLE::Instance(); + ble.gattServer().write(hrmRateChar.getValueHandle(), hrmHeartRateData, 4); + } + } +} + +void createHeartRateService() { + LOG_PRINTF(__func__); // Print the function's name + + // Use the default BLE object: + BLE &ble = BLE::Instance(); + + GattCharacteristic *charTable[] = {&hrmRateChar, &hrmLocationChar, &hrmControlPointChar}; + GattService hrmService(HRMS_SERV, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); + + ble.addService(hrmService); + // Add an call back when data is written. + ble.onDataWritten(onDataWritten); +} + + // B. Callback for things to do when the BLE object is ready void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) { LOG_PRINTF(__func__); // Print the function's name @@ -64,19 +119,13 @@ // Get the BLE object BLE& ble = BLE::Instance(); - // Create the HeartRateService Object with initial value of 0 and "OTHER" location - // (The object's constructor automatically adds the service) - pHrs = new HeartRateService(ble, heartRate, HeartRateService::LOCATION_OTHER); - - // Add in a Device Information Service object too. - // Argument order: "Manufacturer", "Model No", "Serial No", "Hardware Rev", "Firmware Rev", "Software Rev" - new DeviceInformationService(ble, "Acme", "1A", "123", "v1.1", "r2a", "r3"); + // Create the HeartRateService + createHeartRateService(); // Setup the "onDisconnection()" callback // Connection info is handeled by GAP (Generic ACCESS Protocol), hence the ble.gap() part ble.gap().onDisconnection(bleDisconnectionCallback); - // Setup advertising: Build the "payload" // Add in the mode (discoverable / low energy only) ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); @@ -102,6 +151,10 @@ void buttonsPress() { LOG_PRINTF(__func__); // Print the function's name + + // Get access to the BLE object + BLE &ble = BLE::Instance(); + // Display the buttons that are pressed // Do NOT use printf() in callbacks. Put all printing on tasks handled by the event queue. @@ -111,13 +164,28 @@ LOG_PRINTF("\t%d down\r\n",i+1); } if(buttons[0].read()==0) { - heartRate++; - pHrs->updateHeartRate(heartRate); + // Increase the heart rate and update + hrmHeartRateData[1]++; + ble.gattServer().write(hrmRateChar.getValueHandle(), hrmHeartRateData, 4); } else if(buttons[1].read()==0) { - heartRate--; - pHrs->updateHeartRate(heartRate); + hrmHeartRateData[1]--; + ble.gattServer().write(hrmRateChar.getValueHandle(), hrmHeartRateData, 4); } + if(buttons[2].read()==0) { + unsigned energy = hrmHeartRateData[2] | hrmHeartRateData[3]<<8; + energy++; + hrmHeartRateData[2] = energy & 0xFF; + hrmHeartRateData[3] = energy >> 8; + ble.gattServer().write(hrmRateChar.getValueHandle(), hrmHeartRateData, 4); + } + if(buttons[3].read()==0) { + unsigned energy = hrmHeartRateData[2] | hrmHeartRateData[3]<<8; + energy--; + hrmHeartRateData[2] = energy & 0xFF; + hrmHeartRateData[3] = energy >> 8; + ble.gattServer().write(hrmRateChar.getValueHandle(), hrmHeartRateData, 4); + } } // C. Callback to respond to BLE events. This will add a function call to the