Skip to content

Commit 93a4a2a

Browse files
committed
BLE: update heart rate service definition.
This patch includes: * Documentation update. * Code simplification and correctness; the value overloads based on the size of the hrm counter has been removed in favor of runtime check which is more correct. * The control point characteristic has been removed since HeartRate value byte does not support the accumulated energy expanded.
1 parent 47bae16 commit 93a4a2a

File tree

1 file changed

+157
-120
lines changed

1 file changed

+157
-120
lines changed

features/FEATURE_BLE/ble/services/HeartRateService.h

Lines changed: 157 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -14,181 +14,218 @@
1414
* limitations under the License.
1515
*/
1616

17-
#ifndef __BLE_HEART_RATE_SERVICE_H__
18-
#define __BLE_HEART_RATE_SERVICE_H__
17+
#ifndef MBED_BLE_HEART_RATE_SERVICE_H__
18+
#define MBED_BLE_HEART_RATE_SERVICE_H__
1919

2020
#include "ble/BLE.h"
2121

2222
/**
23-
* @class HeartRateService
24-
* @brief BLE Service for HeartRate. This BLE Service contains the location of the sensor and the heart rate in beats per minute.
25-
* Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.heart_rate.xml
26-
* HRM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
27-
* Location: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.body_sensor_location.xml
28-
*/
23+
* BLE Heart Rate Service.
24+
*
25+
* @purpose
26+
*
27+
* The heart rate service is used by fitness applications to exposes the heart
28+
* beat per minute measured by an heart rate sensor.
29+
*
30+
* Clients can read the intended location of the sensor and the last heart rate
31+
* value measured. Additionally clients can subscribe to server initiated
32+
* updates of the heart rate value measured by the sensor. The service delivers
33+
* these updates to the subscribed client in a notification packet.
34+
*
35+
* The subscription mechanism is useful to save power; it avoids unecessary data
36+
* traffic between the client and the server, which may be induced by polling the
37+
* value of the heart rate measurement characteristic.
38+
*
39+
* @par usage
40+
*
41+
* When this class is instantiated, it adds an heart rate service in the GattServer.
42+
* The service contains the location of the sensor and the initial value measured
43+
* by the sensor.
44+
*
45+
* Application code can invoke updateHeartRate() when a new heart rate measurement
46+
* is acquired; this function updates the value of the heart rate measurement
47+
* characteristic and notifies the new value to subscribed clients.
48+
*
49+
* @note You can find specification of the heart rate service here:
50+
* https://www.bluetooth.com/specifications/gatt
51+
*
52+
* @important The service does not expose information related to the sensor
53+
* contact, the accumulated energy expanded or the interbeat intervals.
54+
*
55+
* @important The heart rate profile limits the number of instantiations of the
56+
* heart rate services to one.
57+
*/
2958
class HeartRateService {
3059
public:
3160
/**
32-
* @enum SensorLocation
33-
* @brief Location of the heart rate sensor on body.
34-
*/
35-
enum {
36-
LOCATION_OTHER = 0, /*!< Other location. */
37-
LOCATION_CHEST, /*!< Chest. */
38-
LOCATION_WRIST, /*!< Wrist. */
39-
LOCATION_FINGER, /*!< Finger. */
40-
LOCATION_HAND, /*!< Hand. */
41-
LOCATION_EAR_LOBE, /*!< Earlobe. */
42-
LOCATION_FOOT, /*!< Foot. */
61+
* Intended location of the heart rate sensor.
62+
*/
63+
enum BodySensorLocation {
64+
/**
65+
* Other location.
66+
*/
67+
LOCATION_OTHER = 0,
68+
69+
/**
70+
* Chest.
71+
*/
72+
LOCATION_CHEST = 1,
73+
74+
/**
75+
* Wrist.
76+
*/
77+
LOCATION_WRIST = 2,
78+
79+
/**
80+
* Finger.
81+
*/
82+
LOCATION_FINGER,
83+
84+
/**
85+
* Hand.
86+
*/
87+
LOCATION_HAND,
88+
89+
/**
90+
* Earlobe.
91+
*/
92+
LOCATION_EAR_LOBE,
93+
94+
/**
95+
* Foot.
96+
*/
97+
LOCATION_FOOT,
4398
};
4499

45100
public:
46101
/**
47-
* @brief Constructor with 8-bit HRM Counter value.
102+
* Construct and initialize an heart rate service.
48103
*
49-
* @param[ref] _ble
50-
* Reference to the underlying BLE.
51-
* @param[in] hrmCounter (8-bit)
52-
* Initial value for the HRM counter.
53-
* @param[in] location
54-
* Sensor's location.
55-
*/
56-
HeartRateService(BLE &_ble, uint8_t hrmCounter, uint8_t location) :
57-
ble(_ble),
58-
valueBytes(hrmCounter),
59-
hrmRate(GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR, valueBytes.getPointer(),
60-
valueBytes.getNumValueBytes(), HeartRateValueBytes::MAX_VALUE_BYTES,
61-
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
62-
hrmLocation(GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR, &location),
63-
controlPoint(GattCharacteristic::UUID_HEART_RATE_CONTROL_POINT_CHAR, &controlPointValue) {
64-
setupService();
65-
}
66-
67-
/**
68-
* @brief Constructor with a 16-bit HRM Counter value.
104+
* The construction process adds a GATT heart rate service in @p _ble
105+
* GattServer, sets the value of the heart rate measurement characteristic
106+
* to @p hrmCounter and the value of the body sensor location characteristic
107+
* to @p location.
69108
*
70-
* @param[in] _ble
71-
* Reference to the underlying BLE.
72-
* @param[in] hrmCounter (8-bit)
73-
* Initial value for the HRM counter.
74-
* @param[in] location
75-
* Sensor's location.
109+
* @param[in] _ble BLE device which will host the heart rate service.
110+
* @param[in] hrmCounter Heart beats per minute measured by the heart rate
111+
* sensor.
112+
* @param[in] location Intended location of the heart rate sensor.
76113
*/
77-
HeartRateService(BLE &_ble, uint16_t hrmCounter, uint8_t location) :
114+
HeartRateService(BLE &_ble, uint16_t hrmCounter, BodySensorLocation location) :
78115
ble(_ble),
79116
valueBytes(hrmCounter),
80-
hrmRate(GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR, valueBytes.getPointer(),
81-
valueBytes.getNumValueBytes(), HeartRateValueBytes::MAX_VALUE_BYTES,
82-
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
83-
hrmLocation(GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR, &location),
84-
controlPoint(GattCharacteristic::UUID_HEART_RATE_CONTROL_POINT_CHAR, &controlPointValue) {
117+
hrmRate(
118+
GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR,
119+
valueBytes.getPointer(),
120+
valueBytes.getNumValueBytes(),
121+
HeartRateValueBytes::MAX_VALUE_BYTES,
122+
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
123+
),
124+
hrmLocation(
125+
GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR,
126+
reinterpret_cast<uint8_t*>(&location)
127+
)
128+
{
85129
setupService();
86130
}
87131

88132
/**
89-
* @brief Set a new 8-bit value for the heart rate.
133+
* Update the heart rate that the service exposes.
90134
*
91-
* @param[in] hrmCounter
92-
* Heart rate in BPM.
93-
*/
94-
void updateHeartRate(uint8_t hrmCounter) {
95-
valueBytes.updateHeartRate(hrmCounter);
96-
ble.gattServer().write(hrmRate.getValueHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes());
97-
}
98-
99-
/**
100-
* Set a new 16-bit value for the heart rate.
135+
* The server sends a notification of the new value to clients that have
136+
* subscribed to updates of the heart rate measurement characteristic; clients
137+
* reading the heart rate measurement characteristic after the update obtain
138+
* the updated value.
139+
*
140+
* @param[in] hrmCounter Heart rate measured in BPM.
101141
*
102-
* @param[in] hrmCounter
103-
* Heart rate in BPM.
142+
* @important This function must be called in the execution context of the
143+
* BLE stack.
104144
*/
105145
void updateHeartRate(uint16_t hrmCounter) {
106146
valueBytes.updateHeartRate(hrmCounter);
107-
ble.gattServer().write(hrmRate.getValueHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes());
147+
ble.gattServer().write(
148+
hrmRate.getValueHandle(),
149+
valueBytes.getPointer(),
150+
valueBytes.getNumValueBytes()
151+
);
108152
}
109153

154+
protected:
110155
/**
111-
* This callback allows the heart rate service to receive updates to the
112-
* controlPoint characteristic.
113-
*
114-
* @param[in] params
115-
* Information about the characteristic being updated.
156+
* Construct and add to the GattServer the heart rate service.
116157
*/
117-
virtual void onDataWritten(const GattWriteCallbackParams *params) {
118-
if (params->handle == controlPoint.getValueAttribute().getHandle()) {
119-
/* Do something here if the new value is 1; else you can override this method by
120-
* extending this class.
121-
* @NOTE: If you are extending this class, be sure to also call
122-
* ble.onDataWritten(this, &ExtendedHRService::onDataWritten); in
123-
* your constructor.
124-
*/
125-
}
126-
}
127-
128-
protected:
129158
void setupService(void) {
130-
GattCharacteristic *charTable[] = {&hrmRate, &hrmLocation, &controlPoint};
131-
GattService hrmService(GattService::UUID_HEART_RATE_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
132-
133-
ble.addService(hrmService);
134-
ble.onDataWritten(this, &HeartRateService::onDataWritten);
159+
GattCharacteristic *charTable[] = {
160+
&hrmRate,
161+
&hrmLocation
162+
};
163+
GattService hrmService(
164+
GattService::UUID_HEART_RATE_SERVICE,
165+
charTable,
166+
sizeof(charTable) / sizeof(GattCharacteristic*)
167+
);
168+
169+
ble.gattServer().addService(hrmService);
135170
}
136171

137172
protected:
138-
/* Private internal representation for the bytes used to work with the value of the heart rate characteristic. */
173+
/*
174+
* Heart rate measurement value.
175+
*/
139176
struct HeartRateValueBytes {
140-
static const unsigned MAX_VALUE_BYTES = 3; /* Flags, and up to two bytes for heart rate. */
177+
/* 1 byte for the Flags, and up to two bytes for heart rate value. */
178+
static const unsigned MAX_VALUE_BYTES = 3;
141179
static const unsigned FLAGS_BYTE_INDEX = 0;
142180

143181
static const unsigned VALUE_FORMAT_BITNUM = 0;
144-
static const uint8_t VALUE_FORMAT_FLAG = (1 << VALUE_FORMAT_BITNUM);
145-
146-
HeartRateValueBytes(uint8_t hrmCounter) : valueBytes() {
147-
updateHeartRate(hrmCounter);
148-
}
182+
static const uint8_t VALUE_FORMAT_FLAG = (1 << VALUE_FORMAT_BITNUM);
149183

150-
HeartRateValueBytes(uint16_t hrmCounter) : valueBytes() {
184+
HeartRateValueBytes(uint16_t hrmCounter) : valueBytes()
185+
{
151186
updateHeartRate(hrmCounter);
152187
}
153188

154-
void updateHeartRate(uint8_t hrmCounter) {
155-
valueBytes[FLAGS_BYTE_INDEX] &= ~VALUE_FORMAT_FLAG;
156-
valueBytes[FLAGS_BYTE_INDEX + 1] = hrmCounter;
157-
}
158-
159-
void updateHeartRate(uint16_t hrmCounter) {
160-
valueBytes[FLAGS_BYTE_INDEX] |= VALUE_FORMAT_FLAG;
161-
valueBytes[FLAGS_BYTE_INDEX + 1] = (uint8_t)(hrmCounter & 0xFF);
162-
valueBytes[FLAGS_BYTE_INDEX + 2] = (uint8_t)(hrmCounter >> 8);
189+
void updateHeartRate(uint16_t hrmCounter)
190+
{
191+
if (hrmCounter <= 255) {
192+
valueBytes[FLAGS_BYTE_INDEX] &= ~VALUE_FORMAT_FLAG;
193+
valueBytes[FLAGS_BYTE_INDEX + 1] = hrmCounter;
194+
} else {
195+
valueBytes[FLAGS_BYTE_INDEX] |= VALUE_FORMAT_FLAG;
196+
valueBytes[FLAGS_BYTE_INDEX + 1] = (uint8_t)(hrmCounter & 0xFF);
197+
valueBytes[FLAGS_BYTE_INDEX + 2] = (uint8_t)(hrmCounter >> 8);
198+
}
163199
}
164200

165-
uint8_t *getPointer(void) {
201+
uint8_t *getPointer(void)
202+
{
166203
return valueBytes;
167204
}
168205

169-
const uint8_t *getPointer(void) const {
206+
const uint8_t *getPointer(void) const
207+
{
170208
return valueBytes;
171209
}
172210

173-
unsigned getNumValueBytes(void) const {
174-
return 1 + ((valueBytes[FLAGS_BYTE_INDEX] & VALUE_FORMAT_FLAG) ? sizeof(uint16_t) : sizeof(uint8_t));
211+
unsigned getNumValueBytes(void) const
212+
{
213+
if (valueBytes[FLAGS_BYTE_INDEX] & VALUE_FORMAT_FLAG) {
214+
return 1 + sizeof(uint16_t);
215+
} else {
216+
return 1 + sizeof(uint8_t);
217+
}
175218
}
176219

177220
private:
178-
/* First byte: 8-bit values, no extra info. Second byte: uint8_t HRM value */
179-
/* See https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml */
180221
uint8_t valueBytes[MAX_VALUE_BYTES];
181222
};
182223

183224
protected:
184-
BLE &ble;
185-
186-
HeartRateValueBytes valueBytes;
187-
uint8_t controlPointValue;
188-
189-
GattCharacteristic hrmRate;
190-
ReadOnlyGattCharacteristic<uint8_t> hrmLocation;
191-
WriteOnlyGattCharacteristic<uint8_t> controlPoint;
225+
BLE &ble;
226+
HeartRateValueBytes valueBytes;
227+
GattCharacteristic hrmRate;
228+
ReadOnlyGattCharacteristic<uint8_t> hrmLocation;
192229
};
193230

194-
#endif /* #ifndef __BLE_HEART_RATE_SERVICE_H__*/
231+
#endif /* #ifndef MBED_BLE_HEART_RATE_SERVICE_H__*/

0 commit comments

Comments
 (0)