Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
