Fork of nRF5-DK-HeartRateDemo by Bill Siever

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