Bill Siever / Mbed OS nRF5-DK-HeartRateDemo

Fork of nRF5-DK-HeartRateDemo by Bill Siever

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HeartRateService.h Source File

HeartRateService.h

00001 /* Preface:
00002    This is the HeartRateService from the mbed Microcontroller library. 
00003    Copied from https://github.com/ARMmbed/mbed-os/blob/master/features/FEATURE_BLE/ble/services/HeartRateService.h
00004    (2259e0d)
00005    
00006    The source has not been changed, but comments have been added to help clarify:
00007       Behavior, 
00008       C++ Syntax, and
00009       Design decisions
00010 
00011 Added comments are all in-line comments that start with:
00012   // COMMENT:       
00013       
00014 
00015 */
00016 
00017 /* mbed Microcontroller Library
00018  * Copyright (c) 2006-2013 ARM Limited
00019  *
00020  * Licensed under the Apache License, Version 2.0 (the "License");
00021  * you may not use this file except in compliance with the License.
00022  * You may obtain a copy of the License at
00023  *
00024  *     http://www.apache.org/licenses/LICENSE-2.0
00025  *
00026  * Unless required by applicable law or agreed to in writing, software
00027  * distributed under the License is distributed on an "AS IS" BASIS,
00028  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00029  * See the License for the specific language governing permissions and
00030  * limitations under the License.
00031  */
00032 
00033 #ifndef __BLE_HEART_RATE_SERVICE_H__
00034 #define __BLE_HEART_RATE_SERVICE_H__
00035 
00036 #include "ble/BLE.h"
00037 
00038 /**
00039 * @class HeartRateService
00040 * @brief BLE Service for HeartRate. This BLE Service contains the location of the sensor and the heart rate in beats per minute.
00041 * Service:  https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.heart_rate.xml
00042 * HRM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
00043 * Location: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.body_sensor_location.xml
00044 */
00045 
00046 // COMMENT: It would be wise to review the definition of the HeartRateService
00047 // COMMENT:    https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.heart_rate.xml
00048 // COMMENT: Note that it contains three characteristics:  The Heart Rate Measurement, the Body Sensor Location, and the Heart Rate Cotrol Point.
00049 class HeartRateService {
00050 
00051 // COMMENT: C++ uses the labels public: , private: , and protected:  to control scope. 
00052 // COMMENT: The scope applies to everything until the next scope label.    
00053 public:
00054     /**
00055     * @enum SensorLocation
00056     * @brief Location of the heart rate sensor on body.
00057     */
00058     enum {
00059         LOCATION_OTHER  = 0, /*!< Other location. */
00060         LOCATION_CHEST ,     /*!< Chest. */
00061         LOCATION_WRIST ,     /*!< Wrist. */
00062         LOCATION_FINGER ,    /*!< Finger. */
00063         LOCATION_HAND ,      /*!< Hand. */
00064         LOCATION_EAR_LOBE ,  /*!< Earlobe. */
00065         LOCATION_FOOT ,      /*!< Foot. */
00066     };
00067 
00068 public:
00069     /**
00070      * @brief Constructor with 8-bit HRM Counter value.
00071      *
00072      * @param[ref] _ble
00073      *               Reference to the underlying BLE.
00074      * @param[in] hrmCounter (8-bit)
00075      *               Initial value for the HRM counter.
00076      * @param[in] location
00077      *               Sensor's location.
00078      */
00079     HeartRateService(BLE &_ble, uint8_t hrmCounter, uint8_t location) :
00080 // COMMENT: This declares a constructor. The "BLE &_ble" parameter is an reference variable.
00081 // COMMENT: Reference variables are a way to efficiently pass and use an object while limiting the way 
00082 // COMMENT: it can be changed.  See: https://en.wikipedia.org/wiki/Reference_(C%2B%2B) for more.
00083 
00084 // COMMENT: The lines below are a way to efficiently initialize the fields (aka instance variables) of the
00085 // COMMENT: object.  In Java this is often done with several assignments in the body of the constructor.
00086 // COMMENT:    Ex:  "valueBytes = hrmCounter;" within the function rather than "valueBytes(hrmCounter)" before
00087 // COMMENT: the function begins.  (The behavior of the approach used here can be subtly different with C++ 
00088 // COMMENT: objects and references)
00089 // COMMENT: See: http://www.cprogramming.com/tutorial/initialization-lists-c++.html for more on initialization lists
00090         ble(_ble),
00091         valueBytes(hrmCounter),
00092 // COMMENT: The hrmRate is declared as a "GattCharacteristic", which doesn't have any properties by default.
00093 // COMMENT: Notice that READ and NOTIFY properties are being passed into the constructor to allow read/notify.
00094         hrmRate(GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR, valueBytes.getPointer(),
00095                 valueBytes.getNumValueBytes(), HeartRateValueBytes::MAX_VALUE_BYTES,
00096                 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
00097         hrmLocation(GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR, &location),
00098         controlPoint(GattCharacteristic::UUID_HEART_RATE_CONTROL_POINT_CHAR, &controlPointValue) {
00099 // COMMENT: This is the body of the method. It just calls a private method to complete the setup.
00100 // COMMENT: This was done so all constructors could use this same method for common setup steps
00101 // COMMENT: (rather than replicate the code in each constructor) 
00102         setupService();
00103     }
00104 
00105     /**
00106      * @brief Constructor with a 16-bit HRM Counter value.
00107      *
00108      * @param[in] _ble
00109      *               Reference to the underlying BLE.
00110      * @param[in] hrmCounter (8-bit)
00111      *               Initial value for the HRM counter.
00112      * @param[in] location
00113      *               Sensor's location.
00114      */
00115 // COMMENT: Another constructor.
00116     HeartRateService(BLE &_ble, uint16_t hrmCounter, uint8_t location) :
00117         ble(_ble),
00118         valueBytes(hrmCounter),
00119         hrmRate(GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR, valueBytes.getPointer(),
00120                 valueBytes.getNumValueBytes(), HeartRateValueBytes::MAX_VALUE_BYTES,
00121                 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
00122         hrmLocation(GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR, &location),
00123         controlPoint(GattCharacteristic::UUID_HEART_RATE_CONTROL_POINT_CHAR, &controlPointValue) {
00124         setupService();
00125     }
00126 
00127     /**
00128      * @brief Set a new 8-bit value for the heart rate.
00129      *
00130      * @param[in] hrmCounter
00131      *                  Heart rate in BPM.
00132      */
00133     void updateHeartRate(uint8_t hrmCounter) {
00134 // COMMENT: This is method that will be used to update the value of the heart rate characteristic.
00135 // COMMENT: valueBytes is the actual characteristic.  The following line of code will update its value.
00136         valueBytes.updateHeartRate(hrmCounter);
00137 // COMMENT: But valueBytes is just the member variable in this object for the heart rate. The BLE subsystem
00138 // COMMENT: needs to be notified that it has changed (so a notification can be sent)
00139 // COMMENT: This is changing an "Attribute's value".  The BLE Server has it's own internal copy of all the
00140 // COMMENT: data.  The following line of code will update this internal copy:
00141         ble.gattServer().write(hrmRate.getValueHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes());
00142 // COMMENT: I.e., the server write to the heart rate characteristic's "value" by using the designated number of bytes.
00143 // COMMENT: "pointers" are used to copy the data. For more on pointers see: https://en.wikibooks.org/wiki/C_Programming/Pointers_and_arrays
00144     }
00145 
00146     /**
00147      * Set a new 16-bit value for the heart rate.
00148      *
00149      * @param[in] hrmCounter
00150      *                  Heart rate in BPM.
00151      */
00152     void updateHeartRate(uint16_t hrmCounter) {
00153 // COMMENT: Like the above, but using a 16-bit value instead.
00154         valueBytes.updateHeartRate(hrmCounter);
00155         ble.gattServer().write(hrmRate.getValueHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes());
00156     }
00157 
00158     /**
00159      * This callback allows the heart rate service to receive updates to the
00160      * controlPoint characteristic.
00161      *
00162      * @param[in] params
00163      *     Information about the characterisitc being updated.
00164      */
00165     virtual void onDataWritten(const GattWriteCallbackParams *params) {
00166         if (params->handle == controlPoint.getValueAttribute().getHandle()) {
00167 // COMMENT: This is a virtual function --- It could be "overridden" by a subclass.
00168 // COMMENT: The if-statement would allow this code to determine if a change had been 
00169 // COMMENT: to the control point (the code to react to that change could be placed here) 
00170 // COMMENT: This callback must be "connected" to the BLE stack (this is done in the setupService())
00171             /* Do something here if the new value is 1; else you can override this method by
00172              * extending this class.
00173              * @NOTE: If you are extending this class, be sure to also call
00174              * ble.onDataWritten(this, &ExtendedHRService::onDataWritten); in
00175              * your constructor.
00176              */
00177         }
00178     }
00179 
00180 protected:
00181     void setupService(void) {
00182 // COMMENT: This builds the service table (an array of the individual characteristics)
00183 // COMMENT: Note the use of the "address-of" operation (&) to build the table. See: http://www.cplusplus.com/doc/tutorial/pointers/
00184         GattCharacteristic *charTable[] = {&hrmRate, &hrmLocation, &controlPoint};
00185         GattService         hrmService(GattService::UUID_HEART_RATE_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
00186 
00187         ble.addService(hrmService);
00188 // COMMENT: This setups up the "onDataWritten" callback above. I.e., it adds in a way to 
00189 // COMMENT: respond when data is written to the BLE stack from the client.
00190         ble.onDataWritten(this, &HeartRateService::onDataWritten);
00191     }
00192 
00193 protected:
00194     /* Private internal representation for the bytes used to work with the value of the heart rate characteristic. */
00195 // COMMENT: This "struct" is like having another class nested (with all public member fields/methods)
00196 // COMMENT: The Heart Rate Service can use two different formats for the heart rate data. This hides
00197 // COMMENT: the details of which is being used. 
00198     struct HeartRateValueBytes {
00199         static const unsigned MAX_VALUE_BYTES  = 3; /* Flags, and up to two bytes for heart rate. */
00200         static const unsigned FLAGS_BYTE_INDEX = 0;
00201 
00202         static const unsigned VALUE_FORMAT_BITNUM = 0;
00203         static const uint8_t  VALUE_FORMAT_FLAG   = (1 << VALUE_FORMAT_BITNUM);
00204 
00205 // The following two constructors allow objects to be constructed from either 8-bit or 16-bit values.
00206         HeartRateValueBytes(uint8_t hrmCounter) : valueBytes() {
00207             updateHeartRate(hrmCounter);
00208         }
00209 
00210         HeartRateValueBytes(uint16_t hrmCounter) : valueBytes() {
00211             updateHeartRate(hrmCounter);
00212         }
00213 
00214 // COMMENT: This will allow an update of the data from an 8-bit value (for the 8-bit format)
00215         void updateHeartRate(uint8_t hrmCounter) {
00216             valueBytes[FLAGS_BYTE_INDEX]    &= ~VALUE_FORMAT_FLAG;
00217             valueBytes[FLAGS_BYTE_INDEX + 1] = hrmCounter;
00218         }
00219 
00220 // COMMENT: This will allow an update of the data from an 16-bit value (for the 16-bit format)
00221         void updateHeartRate(uint16_t hrmCounter) {
00222             valueBytes[FLAGS_BYTE_INDEX]    |= VALUE_FORMAT_FLAG;
00223             valueBytes[FLAGS_BYTE_INDEX + 1] = (uint8_t)(hrmCounter & 0xFF);
00224             valueBytes[FLAGS_BYTE_INDEX + 2] = (uint8_t)(hrmCounter >> 8);
00225         }
00226 // COMMENT: The two "getPointer()" functions allow access to the stored heart rate data via pointers.
00227 // COMMENT: See: https://en.wikibooks.org/wiki/C_Programming/Pointers_and_arrays for details of pointers
00228         uint8_t       *getPointer(void) {
00229             return valueBytes;
00230         }
00231 
00232         const uint8_t *getPointer(void) const {
00233             return valueBytes;
00234         }
00235 
00236 // COMMENT: The following getter will get the actual bytes promoted to a 32-bit unsigned. 
00237         unsigned       getNumValueBytes(void) const {
00238             return 1 + ((valueBytes[FLAGS_BYTE_INDEX] & VALUE_FORMAT_FLAG) ? sizeof(uint16_t) : sizeof(uint8_t));
00239         }
00240 
00241     private:
00242         /* First byte: 8-bit values, no extra info. Second byte: uint8_t HRM value */
00243         /* See https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml */
00244 // COMMENT: The actual bytes are protected/hidden from direct access. 
00245         uint8_t valueBytes[MAX_VALUE_BYTES];
00246     };
00247 
00248 protected:
00249 // COMMENT: These are the fields (aka properties aka instance variables) of the object.
00250 // COMMENT: The ble variable is used to access the entire BLE subsystem (GAP and GATT layers of the stack)
00251     BLE                 &ble;
00252 
00253 // COMMENT: Use a "HeartRateValueBytes" object to keep track of the heart rate
00254 // COMMENT: (This hide the details of how it is actually stored)
00255     HeartRateValueBytes  valueBytes;
00256 // COMMENT: the controlPointValue represents the value of the control point.
00257     uint8_t              controlPointValue;
00258 
00259 // COMMENT: The following three variables declare the actual characteristics
00260     GattCharacteristic                   hrmRate;
00261     ReadOnlyGattCharacteristic<uint8_t>  hrmLocation;
00262 // COMMENT: The ReadOnlyGattCharacteristic uses templates (like Java's Generics) to specify the data type that will be used.
00263 // COMMENT: In these cases a single byte will be used for both the hrmLocation and controlPoint characteristics.
00264     WriteOnlyGattCharacteristic<uint8_t> controlPoint;
00265     
00266 // COMMENT: Notice that characteristics were declared with three different types:
00267 // COMMENT:   Just "GattCharacteristic", which needs to have the data & properties specified in the constructor.
00268 // COMMENT:   As "ReadOnlyGattCharacteristic", which will be readable (only)
00269 // COMMENT:   And as "WriteOnlyGattCharacteristic", which will be writeabel (only) 
00270 };
00271 
00272 #endif /* #ifndef __BLE_HEART_RATE_SERVICE_H__*/