Graduation Thesis, use Nucleo and X-Nucleo BLE

Dependencies:   PulseSensor GSM Thermometer KalmanFilter

Committer:
DuyLionTran
Date:
Thu May 24 17:37:31 2018 +0000
Revision:
17:b7c2db3e7282
Parent:
16:0325e647496f
Child:
25:8621ebb6ea0c
send decode float for HR

Who changed what in which revision?

UserRevisionLine numberNew contents of line
DuyLionTran 0:64ca984b3efd 1 #ifndef __BLE_HEALTHCARE_SERVICE_H__
DuyLionTran 0:64ca984b3efd 2 #define __BLE_HEALTHCARE_SERVICE_H__
DuyLionTran 0:64ca984b3efd 3
DuyLionTran 0:64ca984b3efd 4 #include "ble/BLE.h"
DuyLionTran 0:64ca984b3efd 5
DuyLionTran 0:64ca984b3efd 6 /**
DuyLionTran 0:64ca984b3efd 7 * @class HealthCareService
DuyLionTran 0:64ca984b3efd 8 * @brief
DuyLionTran 0:64ca984b3efd 9 */
DuyLionTran 0:64ca984b3efd 10 class HealthCareService {
DuyLionTran 0:64ca984b3efd 11 public:
DuyLionTran 0:64ca984b3efd 12 /**
DuyLionTran 0:64ca984b3efd 13 * @brief Location of sensor on the body.
DuyLionTran 0:64ca984b3efd 14 */
DuyLionTran 0:64ca984b3efd 15 enum TemperatureSensorLocation_t {
DuyLionTran 17:b7c2db3e7282 16 TEMPERATURE_LOCATION_UNKNOWN = 0, /*!< Unknown. */
DuyLionTran 17:b7c2db3e7282 17 TEMPERATURE_LOCATION_ARMPIT = 1, /*!< Armpit. */
DuyLionTran 17:b7c2db3e7282 18 TEMPERATURE_LOCATION_BODY, /*!< Body. */
DuyLionTran 17:b7c2db3e7282 19 TEMPERATURE_LOCATION_EAR, /*!< Ear. */
DuyLionTran 17:b7c2db3e7282 20 TEMPERATURE_LOCATION_FINGER, /*!< Finger. */
DuyLionTran 17:b7c2db3e7282 21 TEMPERATURE_LOCATION_GI_TRACT, /*!< GI tract */
DuyLionTran 17:b7c2db3e7282 22 TEMPERATURE_LOCATION_MOUTH, /*!< Mouth. */
DuyLionTran 17:b7c2db3e7282 23 TEMPERATURE_LOCATION_RECTUM, /*!< Rectum. */
DuyLionTran 17:b7c2db3e7282 24 TEMPERATURE_LOCATION_TOE, /*!< Toe. */
DuyLionTran 17:b7c2db3e7282 25 TEMPERATURE_LOCATION_EAR_DRUM, /*!< Eardrum. */
DuyLionTran 0:64ca984b3efd 26 };
DuyLionTran 0:64ca984b3efd 27 /**
DuyLionTran 0:64ca984b3efd 28 * Intended location of the heart rate sensor.
DuyLionTran 0:64ca984b3efd 29 */
DuyLionTran 0:64ca984b3efd 30 enum HeartRateSensorLocation_t {
DuyLionTran 17:b7c2db3e7282 31 HRM_LOCATION_OTHER = 0, /*!< Other location. */
DuyLionTran 0:64ca984b3efd 32 HRM_LOCATION_CHEST = 1, /*!< Chest. */
DuyLionTran 0:64ca984b3efd 33 HRM_LOCATION_WRIST = 2, /*!< Wrist. */
DuyLionTran 0:64ca984b3efd 34 HRM_LOCATION_FINGER, /*!< Finger. */
DuyLionTran 0:64ca984b3efd 35 HRM_LOCATION_HAND, /*!< Hand. */
DuyLionTran 0:64ca984b3efd 36 HRM_LOCATION_EAR_LOBE, /*!< Ear lob. */
DuyLionTran 0:64ca984b3efd 37 HRM_LOCATION_FOOT, /*!< Foot. */
DuyLionTran 8:3384286a8498 38 };
DuyLionTran 8:3384286a8498 39
DuyLionTran 9:be09a9bf2e2e 40 const static uint16_t DEVICE_INFO_SERVICE_UUID = 0x180A;
DuyLionTran 8:3384286a8498 41 const static uint16_t USER_DATA_SERVICE_UUID = 0x181C;
DuyLionTran 8:3384286a8498 42 const static uint16_t USER_DATA_DIGITAL_CHARACTERISTIC_UUID = 0x2A57;
DuyLionTran 0:64ca984b3efd 43
DuyLionTran 0:64ca984b3efd 44 public:
DuyLionTran 0:64ca984b3efd 45 /**
DuyLionTran 0:64ca984b3efd 46 * @param[in] _ble Reference to the BLE device.
DuyLionTran 0:64ca984b3efd 47 * @param[in] initialTemp Initial value in celsius.
DuyLionTran 0:64ca984b3efd 48 * @param[in] _tempLocation Location of the thermometer.
DuyLionTran 0:64ca984b3efd 49 * @param[in] hrmCounter Heart beats per minute measured by the heart rate sensor.
DuyLionTran 0:64ca984b3efd 50 * @param[in] _HRMlocation Intended location of the heart rate sensor.
DuyLionTran 8:3384286a8498 51 * @param[in] controlVal_1 Optional control value 1
DuyLionTran 0:64ca984b3efd 52 */
DuyLionTran 0:64ca984b3efd 53 HealthCareService(BLE &_ble,
DuyLionTran 0:64ca984b3efd 54 float initialTemp,
DuyLionTran 0:64ca984b3efd 55 uint8_t _tempLocation,
DuyLionTran 0:64ca984b3efd 56 // ) :
DuyLionTran 17:b7c2db3e7282 57 uint16_t hrmCounter,
DuyLionTran 8:3384286a8498 58 uint8_t _hrmLocation,
DuyLionTran 8:3384286a8498 59 uint8_t controlVal_1
DuyLionTran 8:3384286a8498 60 ) :
DuyLionTran 0:64ca984b3efd 61 ble(_ble),
DuyLionTran 0:64ca984b3efd 62 temperatureValueByte(initialTemp),
DuyLionTran 1:9eadd2dc4b6e 63 hrmValueByte(hrmCounter),
DuyLionTran 2:16f6cfcd7505 64 tempMeasurement(
DuyLionTran 2:16f6cfcd7505 65 GattCharacteristic::UUID_TEMPERATURE_MEASUREMENT_CHAR,
DuyLionTran 2:16f6cfcd7505 66 (TemperatureValueBytes *)temperatureValueByte.getPointer(),
DuyLionTran 2:16f6cfcd7505 67 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
DuyLionTran 2:16f6cfcd7505 68 tempLocation(
DuyLionTran 2:16f6cfcd7505 69 GattCharacteristic::UUID_TEMPERATURE_TYPE_CHAR,
DuyLionTran 2:16f6cfcd7505 70 &_tempLocation),
DuyLionTran 2:16f6cfcd7505 71 hrmRate(
DuyLionTran 2:16f6cfcd7505 72 GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR,
DuyLionTran 2:16f6cfcd7505 73 hrmValueByte.getPointer(),
DuyLionTran 2:16f6cfcd7505 74 hrmValueByte.getNumValueBytes(),
DuyLionTran 2:16f6cfcd7505 75 HeartRateValueBytes::MAX_VALUE_BYTES,
DuyLionTran 2:16f6cfcd7505 76 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
DuyLionTran 0:64ca984b3efd 77 hrmLocation(
DuyLionTran 0:64ca984b3efd 78 GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR,
DuyLionTran 8:3384286a8498 79 reinterpret_cast<uint8_t*>(&_hrmLocation)),
DuyLionTran 8:3384286a8498 80 controlState(USER_DATA_DIGITAL_CHARACTERISTIC_UUID,
DuyLionTran 11:5a4313edf10d 81 &controlVal_1)
DuyLionTran 11:5a4313edf10d 82 /* UUID_GLUCOSE_SERVICE UUID_HEALTH_THERMOMETER_SERVICE */
DuyLionTran 0:64ca984b3efd 83
DuyLionTran 0:64ca984b3efd 84 {
DuyLionTran 2:16f6cfcd7505 85 GattCharacteristic *HealthThermChars[] = {&tempMeasurement, &tempLocation};
DuyLionTran 11:5a4313edf10d 86 GattService HealthThermService(GattService::UUID_HEALTH_THERMOMETER_SERVICE, HealthThermChars, sizeof(HealthThermChars) / sizeof(GattCharacteristic *));
DuyLionTran 2:16f6cfcd7505 87 ble.addService(HealthThermService);
DuyLionTran 2:16f6cfcd7505 88
DuyLionTran 8:3384286a8498 89 GattCharacteristic *HRMChars[] = {&hrmRate, &hrmLocation};
DuyLionTran 2:16f6cfcd7505 90 GattService HRMService(GattService::UUID_HEART_RATE_SERVICE , HRMChars, sizeof(HRMChars) / sizeof(GattCharacteristic *));
DuyLionTran 2:16f6cfcd7505 91 ble.addService(HRMService);
DuyLionTran 8:3384286a8498 92
DuyLionTran 8:3384286a8498 93 GattCharacteristic *CONChars[] = {&controlState};
DuyLionTran 8:3384286a8498 94 GattService CONService(USER_DATA_SERVICE_UUID , CONChars, sizeof(CONChars) / sizeof(GattCharacteristic *));
DuyLionTran 8:3384286a8498 95 ble.addService(CONService);
DuyLionTran 0:64ca984b3efd 96 }
DuyLionTran 0:64ca984b3efd 97
DuyLionTran 8:3384286a8498 98 GattAttribute::Handle_t getValueHandle() const
DuyLionTran 8:3384286a8498 99 {
DuyLionTran 8:3384286a8498 100 return controlState.getValueHandle();
DuyLionTran 8:3384286a8498 101 }
DuyLionTran 8:3384286a8498 102
DuyLionTran 16:0325e647496f 103 GattAttribute::Handle_t getTypeHandle() const
DuyLionTran 16:0325e647496f 104 {
DuyLionTran 16:0325e647496f 105 return tempLocation.getValueHandle();
DuyLionTran 16:0325e647496f 106 }
DuyLionTran 16:0325e647496f 107
DuyLionTran 16:0325e647496f 108 GattAttribute::Handle_t getLocationHandle() const
DuyLionTran 16:0325e647496f 109 {
DuyLionTran 16:0325e647496f 110 return hrmLocation.getValueHandle();
DuyLionTran 16:0325e647496f 111 }
DuyLionTran 16:0325e647496f 112
DuyLionTran 0:64ca984b3efd 113 /**
DuyLionTran 0:64ca984b3efd 114 * @brief Update the temperature being broadcast.
DuyLionTran 0:64ca984b3efd 115 * @param[in] temperature Floating point value of the temperature.
DuyLionTran 0:64ca984b3efd 116 */
DuyLionTran 0:64ca984b3efd 117 void updateTemperature(float temperature) {
DuyLionTran 0:64ca984b3efd 118 if (ble.getGapState().connected) {
DuyLionTran 0:64ca984b3efd 119 temperatureValueByte.updateTemperature(temperature);
DuyLionTran 0:64ca984b3efd 120 ble.gattServer().write(tempMeasurement.getValueHandle(), temperatureValueByte.getPointer(), sizeof(TemperatureValueBytes));
DuyLionTran 0:64ca984b3efd 121 }
DuyLionTran 0:64ca984b3efd 122 }
DuyLionTran 0:64ca984b3efd 123
DuyLionTran 0:64ca984b3efd 124 /**
DuyLionTran 14:e6029b780879 125 * @brief Update the temperature location.
DuyLionTran 0:64ca984b3efd 126 * @param loc New location value.
DuyLionTran 0:64ca984b3efd 127 */
DuyLionTran 16:0325e647496f 128 void updateType(uint8_t loc) {
DuyLionTran 0:64ca984b3efd 129 ble.gattServer().write(tempLocation.getValueHandle(), reinterpret_cast<uint8_t *>(&loc), sizeof(uint8_t));
DuyLionTran 0:64ca984b3efd 130 }
DuyLionTran 0:64ca984b3efd 131
DuyLionTran 0:64ca984b3efd 132 /**
DuyLionTran 16:0325e647496f 133 * @brief Update the temperature location.
DuyLionTran 16:0325e647496f 134 * @param loc New location value.
DuyLionTran 16:0325e647496f 135 */
DuyLionTran 16:0325e647496f 136 void updateLocation(uint8_t loc) {
DuyLionTran 16:0325e647496f 137 ble.gattServer().write(hrmLocation.getValueHandle(), reinterpret_cast<uint8_t *>(&loc), sizeof(uint8_t));
DuyLionTran 16:0325e647496f 138 }
DuyLionTran 16:0325e647496f 139
DuyLionTran 16:0325e647496f 140 /**
DuyLionTran 0:64ca984b3efd 141 * Update the heart rate that the service exposes.
DuyLionTran 0:64ca984b3efd 142 * The server sends a notification of the new value to clients that have
DuyLionTran 0:64ca984b3efd 143 * subscribed to updates of the heart rate measurement characteristic; clients
DuyLionTran 0:64ca984b3efd 144 * reading the heart rate measurement characteristic after the update obtain
DuyLionTran 0:64ca984b3efd 145 * the updated value.
DuyLionTran 0:64ca984b3efd 146 *
DuyLionTran 0:64ca984b3efd 147 * @param[in] hrmCounter Heart rate measured in BPM.
DuyLionTran 0:64ca984b3efd 148 * @attention This function must be called in the execution context of the BLE stack.
DuyLionTran 0:64ca984b3efd 149 */
DuyLionTran 0:64ca984b3efd 150 void updateHeartRate(uint16_t hrmCounter) {
DuyLionTran 1:9eadd2dc4b6e 151 hrmValueByte.updateHeartRate(hrmCounter);
DuyLionTran 1:9eadd2dc4b6e 152 ble.gattServer().write(
DuyLionTran 1:9eadd2dc4b6e 153 hrmRate.getValueHandle(),
DuyLionTran 1:9eadd2dc4b6e 154 hrmValueByte.getPointer(),
DuyLionTran 1:9eadd2dc4b6e 155 hrmValueByte.getNumValueBytes()
DuyLionTran 1:9eadd2dc4b6e 156 );
DuyLionTran 0:64ca984b3efd 157 }
DuyLionTran 0:64ca984b3efd 158
DuyLionTran 0:64ca984b3efd 159 private:
DuyLionTran 0:64ca984b3efd 160 /* Private internal representation for the bytes used to work with the vaulue of the temperature characteristic. */
DuyLionTran 0:64ca984b3efd 161 struct TemperatureValueBytes {
DuyLionTran 0:64ca984b3efd 162 static const unsigned OFFSET_OF_FLAGS = 0;
DuyLionTran 0:64ca984b3efd 163 static const unsigned OFFSET_OF_VALUE = OFFSET_OF_FLAGS + sizeof(uint8_t);
DuyLionTran 0:64ca984b3efd 164 static const unsigned SIZEOF_VALUE_BYTES = sizeof(uint8_t) + sizeof(float);
DuyLionTran 0:64ca984b3efd 165
DuyLionTran 0:64ca984b3efd 166 static const unsigned TEMPERATURE_UNITS_FLAG_POS = 0;
DuyLionTran 0:64ca984b3efd 167 static const unsigned TIMESTAMP_FLAG_POS = 1;
DuyLionTran 0:64ca984b3efd 168 static const unsigned TEMPERATURE_TYPE_FLAG_POS = 2;
DuyLionTran 0:64ca984b3efd 169
DuyLionTran 0:64ca984b3efd 170 static const uint8_t TEMPERATURE_UNITS_CELSIUS = 0;
DuyLionTran 0:64ca984b3efd 171 static const uint8_t TEMPERATURE_UNITS_FAHRENHEIT = 1;
DuyLionTran 0:64ca984b3efd 172
DuyLionTran 0:64ca984b3efd 173 TemperatureValueBytes(float initialTemperature) : bytes() {
DuyLionTran 0:64ca984b3efd 174 /* Assumption: temperature values are expressed in celsius */
DuyLionTran 0:64ca984b3efd 175 bytes[OFFSET_OF_FLAGS] = (TEMPERATURE_UNITS_CELSIUS << TEMPERATURE_UNITS_FLAG_POS) |
DuyLionTran 0:64ca984b3efd 176 (false << TIMESTAMP_FLAG_POS) |
DuyLionTran 0:64ca984b3efd 177 (false << TEMPERATURE_TYPE_FLAG_POS);
DuyLionTran 0:64ca984b3efd 178 updateTemperature(initialTemperature);
DuyLionTran 0:64ca984b3efd 179 }
DuyLionTran 0:64ca984b3efd 180
DuyLionTran 0:64ca984b3efd 181 void updateTemperature(float temp) {
DuyLionTran 0:64ca984b3efd 182 uint32_t temp_ieee11073 = quick_ieee11073_from_float(temp);
DuyLionTran 0:64ca984b3efd 183 memcpy(&bytes[OFFSET_OF_VALUE], &temp_ieee11073, sizeof(float));
DuyLionTran 0:64ca984b3efd 184 }
DuyLionTran 0:64ca984b3efd 185
DuyLionTran 0:64ca984b3efd 186 uint8_t *getPointer(void) {
DuyLionTran 0:64ca984b3efd 187 return bytes;
DuyLionTran 0:64ca984b3efd 188 }
DuyLionTran 0:64ca984b3efd 189
DuyLionTran 0:64ca984b3efd 190 const uint8_t *getPointer(void) const {
DuyLionTran 0:64ca984b3efd 191 return bytes;
DuyLionTran 0:64ca984b3efd 192 }
DuyLionTran 0:64ca984b3efd 193
DuyLionTran 0:64ca984b3efd 194 private:
DuyLionTran 0:64ca984b3efd 195 /**
DuyLionTran 0:64ca984b3efd 196 * @brief A very quick conversion between a float temperature and 11073-20601 FLOAT-Type.
DuyLionTran 0:64ca984b3efd 197 * @param temperature The temperature as a float.
DuyLionTran 0:64ca984b3efd 198 * @return The temperature in 11073-20601 FLOAT-Type format.
DuyLionTran 0:64ca984b3efd 199 */
DuyLionTran 0:64ca984b3efd 200 uint32_t quick_ieee11073_from_float(float temperature) {
DuyLionTran 0:64ca984b3efd 201 uint8_t exponent = 0xFE; //Exponent is -2
DuyLionTran 0:64ca984b3efd 202 uint32_t mantissa = (uint32_t)(temperature * 100);
DuyLionTran 0:64ca984b3efd 203
DuyLionTran 0:64ca984b3efd 204 return (((uint32_t)exponent) << 24) | mantissa;
DuyLionTran 0:64ca984b3efd 205 }
DuyLionTran 0:64ca984b3efd 206
DuyLionTran 0:64ca984b3efd 207 private:
DuyLionTran 0:64ca984b3efd 208 /* First byte: 8-bit flags. Second field is a float holding the temperature value. */
DuyLionTran 0:64ca984b3efd 209 /* See https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml */
DuyLionTran 0:64ca984b3efd 210 uint8_t bytes[SIZEOF_VALUE_BYTES];
DuyLionTran 0:64ca984b3efd 211 };
DuyLionTran 0:64ca984b3efd 212
DuyLionTran 0:64ca984b3efd 213 protected:
DuyLionTran 0:64ca984b3efd 214 /*
DuyLionTran 0:64ca984b3efd 215 * Heart rate measurement value.
DuyLionTran 0:64ca984b3efd 216 */
DuyLionTran 0:64ca984b3efd 217 struct HeartRateValueBytes {
DuyLionTran 0:64ca984b3efd 218 /* 1 byte for the Flags, and up to two bytes for heart rate value. */
DuyLionTran 0:64ca984b3efd 219 static const unsigned MAX_VALUE_BYTES = 3;
DuyLionTran 0:64ca984b3efd 220 static const unsigned FLAGS_BYTE_INDEX = 0;
DuyLionTran 0:64ca984b3efd 221
DuyLionTran 0:64ca984b3efd 222 static const unsigned VALUE_FORMAT_BITNUM = 0;
DuyLionTran 0:64ca984b3efd 223 static const uint8_t VALUE_FORMAT_FLAG = (1 << VALUE_FORMAT_BITNUM);
DuyLionTran 0:64ca984b3efd 224
DuyLionTran 0:64ca984b3efd 225 HeartRateValueBytes(uint16_t hrmCounter) : valueBytes() {
DuyLionTran 0:64ca984b3efd 226 updateHeartRate(hrmCounter);
DuyLionTran 0:64ca984b3efd 227 }
DuyLionTran 0:64ca984b3efd 228
DuyLionTran 0:64ca984b3efd 229 void updateHeartRate(uint16_t hrmCounter) {
DuyLionTran 0:64ca984b3efd 230 if (hrmCounter <= 255) {
DuyLionTran 0:64ca984b3efd 231 valueBytes[FLAGS_BYTE_INDEX] &= ~VALUE_FORMAT_FLAG;
DuyLionTran 0:64ca984b3efd 232 valueBytes[FLAGS_BYTE_INDEX + 1] = hrmCounter;
DuyLionTran 0:64ca984b3efd 233 } else {
DuyLionTran 0:64ca984b3efd 234 valueBytes[FLAGS_BYTE_INDEX] |= VALUE_FORMAT_FLAG;
DuyLionTran 0:64ca984b3efd 235 valueBytes[FLAGS_BYTE_INDEX + 1] = (uint8_t)(hrmCounter & 0xFF);
DuyLionTran 0:64ca984b3efd 236 valueBytes[FLAGS_BYTE_INDEX + 2] = (uint8_t)(hrmCounter >> 8);
DuyLionTran 0:64ca984b3efd 237 }
DuyLionTran 0:64ca984b3efd 238 }
DuyLionTran 0:64ca984b3efd 239
DuyLionTran 0:64ca984b3efd 240 uint8_t *getPointer(void) {
DuyLionTran 0:64ca984b3efd 241 return valueBytes;
DuyLionTran 0:64ca984b3efd 242 }
DuyLionTran 0:64ca984b3efd 243
DuyLionTran 0:64ca984b3efd 244 const uint8_t *getPointer(void) const {
DuyLionTran 0:64ca984b3efd 245 return valueBytes;
DuyLionTran 0:64ca984b3efd 246 }
DuyLionTran 0:64ca984b3efd 247
DuyLionTran 0:64ca984b3efd 248 unsigned getNumValueBytes(void) const {
DuyLionTran 0:64ca984b3efd 249 if (valueBytes[FLAGS_BYTE_INDEX] & VALUE_FORMAT_FLAG) {
DuyLionTran 0:64ca984b3efd 250 return 1 + sizeof(uint16_t);
DuyLionTran 0:64ca984b3efd 251 }
DuyLionTran 0:64ca984b3efd 252 else {
DuyLionTran 0:64ca984b3efd 253 return 1 + sizeof(uint8_t);
DuyLionTran 0:64ca984b3efd 254 }
DuyLionTran 0:64ca984b3efd 255 }
DuyLionTran 0:64ca984b3efd 256
DuyLionTran 0:64ca984b3efd 257 private:
DuyLionTran 0:64ca984b3efd 258 uint8_t valueBytes[MAX_VALUE_BYTES];
DuyLionTran 0:64ca984b3efd 259 };
DuyLionTran 0:64ca984b3efd 260
DuyLionTran 0:64ca984b3efd 261
DuyLionTran 0:64ca984b3efd 262 protected:
DuyLionTran 0:64ca984b3efd 263 BLE &ble;
DuyLionTran 0:64ca984b3efd 264 TemperatureValueBytes temperatureValueByte;
DuyLionTran 1:9eadd2dc4b6e 265 HeartRateValueBytes hrmValueByte;
DuyLionTran 0:64ca984b3efd 266
DuyLionTran 0:64ca984b3efd 267 ReadOnlyGattCharacteristic<TemperatureValueBytes> tempMeasurement;
DuyLionTran 14:e6029b780879 268 // ReadOnlyGattCharacteristic<uint8_t> tempLocation;
DuyLionTran 14:e6029b780879 269 ReadWriteGattCharacteristic<uint8_t> tempLocation;
DuyLionTran 0:64ca984b3efd 270
DuyLionTran 2:16f6cfcd7505 271 GattCharacteristic hrmRate;
DuyLionTran 15:00a1c0ea570c 272 // ReadOnlyGattCharacteristic<uint8_t> hrmLocation;
DuyLionTran 15:00a1c0ea570c 273 ReadWriteGattCharacteristic<uint8_t> hrmLocation;
DuyLionTran 8:3384286a8498 274
DuyLionTran 8:3384286a8498 275 ReadWriteGattCharacteristic<uint8_t> controlState;
DuyLionTran 0:64ca984b3efd 276 };
DuyLionTran 0:64ca984b3efd 277
DuyLionTran 0:64ca984b3efd 278
DuyLionTran 0:64ca984b3efd 279 #endif /* __BLE_HEALTHCARE_SERVICE_H__ */