cycle speed and cadence service
Dependencies: BLE_API mbed nRF51822
Fork of Bluetooth_Heart_Rate_Monitor_dummy by
CycleSpeedCadenceService.h
- Committer:
- smigielski
- Date:
- 2015-05-25
- Revision:
- 71:832536f63f0b
- Parent:
- 64:43e7ddf9dba5
File content as of revision 71:832536f63f0b:
/* mbed Microcontroller Library * Copyright (c) 2006-2013 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __BLE_CYCLE_SPEED_CADENCE_SERVICE_H__ #define __BLE_CYCLE_SPEED_CADENCE_SERVICE_H__ #include "BLEDevice.h" /** * @class CycleSpeedCadenceService * @brief BLE Service for HeartRate. This BLE Service contains the location of the sensor, the heartrate in beats per minute. <br> * Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.heart_rate.xml <br> * HRM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml <br> * Location: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.body_sensor_location.xml */ class CycleSpeedCadenceService { public: /** * @enum SensorLocation * @brief Location of HeartRate sensor on body. */ enum { LOCATION_OTHER = 0, LOCATION_TOP_OF_SHOE, LOCATION_IN_SHOE, LOCATION_HIP, LOCATION_FRONT_WHEEL, LOCATION_LEFT_CRANK, LOCATION_RIGHT_CRANK, LOCATION_LEFT_PEDAL, LOCATION_RIGHT_PEDAL, LOCATION_FRONT_HUB, LOCATION_REAR_DROPOUT, LOCATION_CHAINSTAY, LOCATION_REAR_WHEEL, LOCATION_REAR_HUB, LOCATION_CHEST }; enum { UUID_SENSOR_LOCATION_CHAR = 0x2A5D, UUID_CSC_MEASUREMENT_CHAR = 0x2A5B, UUID_CSC_FEATURE_CHAR = 0x2A5C, UUID_CSC_CONTROL_POINT_CHAR = 0x2A55 }; public: /** * @brief Constructor with 8bit HRM Counter value. * * @param[ref] _ble * Reference to the underlying BLEDevice. * @param[in] hrmCounter (8-bit) * initial value for the hrm counter. * @param[in] location * Sensor's location. */ CycleSpeedCadenceService(BLEDevice &_ble, uint32_t wheelRevolutions ,uint16_t wheelEvent, uint8_t location) : ble(_ble), feautureBytes(true,false), valueBytes(wheelRevolutions,wheelEvent), cscMesurement(GattCharacteristic::UUID_CSC_MEASUREMENT_CHAR, valueBytes.getPointer(), valueBytes.getNumValueBytes(), CSCValueBytes::MAX_VALUE_BYTES, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY), cscFeatures(CycleSpeedCadenceService::UUID_CSC_FEATURE_CHAR, feautureBytes.getPointer()), sensorLocation(CycleSpeedCadenceService::UUID_SENSOR_LOCATION_CHAR, &location), controlPoint(CycleSpeedCadenceService::UUID_CSC_CONTROL_POINT_CHAR, &controlPointValue) { setupService(); } CycleSpeedCadenceService(BLEDevice &_ble, uint16_t crankRevolutions, uint16_t crankEvent, uint8_t location ) : ble(_ble), feautureBytes(false,true), valueBytes(crankRevolutions,crankEvent), cscMesurement(GattCharacteristic::UUID_CSC_MEASUREMENT_CHAR, valueBytes.getPointer(), valueBytes.getNumValueBytes(), CSCValueBytes::MAX_VALUE_BYTES, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY), cscFeatures(CycleSpeedCadenceService::UUID_CSC_FEATURE_CHAR, feautureBytes.getPointer()), sensorLocation(CycleSpeedCadenceService::UUID_SENSOR_LOCATION_CHAR, &location), controlPoint(CycleSpeedCadenceService::UUID_CSC_CONTROL_POINT_CHAR, &controlPointValue) { setupService(); } CycleSpeedCadenceService(BLEDevice &_ble, uint32_t wheelRevolutions ,uint16_t wheelEvent,uint16_t crankRevolutions, uint16_t crankEvent, uint8_t location ) : ble(_ble), feautureBytes(true,true), valueBytes(wheelRevolutions,wheelEvent,crankRevolutions,crankEvent), cscMesurement(GattCharacteristic::UUID_CSC_MEASUREMENT_CHAR, valueBytes.getPointer(), valueBytes.getNumValueBytes(), CSCValueBytes::MAX_VALUE_BYTES, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY), cscFeatures(CycleSpeedCadenceService::UUID_CSC_FEATURE_CHAR, feautureBytes.getPointer()), sensorLocation(CycleSpeedCadenceService::UUID_SENSOR_LOCATION_CHAR, &location), controlPoint(CycleSpeedCadenceService::UUID_CSC_CONTROL_POINT_CHAR, &controlPointValue) { setupService(); } /** * @brief Set a new 8-bit value for heart rate. * * @param[in] hrmCounter * HeartRate in bpm. */ void updateWheel(uint32_t wheelRevolutions ,uint16_t wheelEvent) { valueBytes.updateWheel(wheelRevolutions,wheelEvent); ble.updateCharacteristicValue(cscMesurement.getValueAttribute().getHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes()); } /** * Set a new 16-bit value for heart rate. * * @param[in] hrmCounter * HeartRate in bpm. */ void updateCrank(uint16_t crankRevolutions, uint16_t crankEvent) { valueBytes.updateCrank(crankRevolutions,crankEvent); ble.updateCharacteristicValue(cscMesurement.getValueAttribute().getHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes()); } /** * This callback allows the CycleSpeedCadenceService to receive updates to the * controlPoint Characteristic. * * @param[in] params * Information about the characterisitc being updated. */ virtual void onDataWritten(const GattCharacteristicWriteCBParams *params) { if (params->charHandle == controlPoint.getValueHandle()) { /* Do something here if the new value is 1; else you can override this method by * extending this class. * @NOTE: if you are extending this class, be sure to also call * ble.onDataWritten(this, &ExtendedHRService::onDataWritten); in * your constructor. */ } } private: void setupService(void) { static bool serviceAdded = false; /* We should only ever need to add the heart rate service once. */ if (serviceAdded) { return; } GattCharacteristic *charTable[] = {&cscMesurement, &cscFeatures, &sensorLocation, &controlPoint}; GattService cscsService(GattService::UUID_CYCLING_SPEED_AND_CADENCE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); ble.addService(cscsService); serviceAdded = true; ble.onDataWritten(this, &CycleSpeedCadenceService::onDataWritten); } private: struct CSCFeatureBytes { uint16_t cscFeaturesValue; //location change is not supported right now CSCFeatureBytes(bool wheelSupport, bool crankSupport){ cscFeaturesValue= wheelSupport | crankSupport << 1; } uint16_t *getPointer(void) { return &cscFeaturesValue; } const uint16_t *getPointer(void) const { return &cscFeaturesValue; } }; /* Private internal representation for the bytes used to work with the vaulue of the heart-rate characteristic. */ struct CSCValueBytes { static const unsigned MAX_VALUE_BYTES = 11; /* FLAGS + up to two bytes for heart-rate */ static const unsigned FLAGS_BYTE_INDEX = 0; static const unsigned WHEEL_REVOLUTIONS_INDEX = 1; static const unsigned WHEEL_EVENT_INDEX = 5; static const unsigned CRANK_REVOLUTIONS_INDEX = 1; static const unsigned CRANK_EVENT_INDEX = 3; static const unsigned WHEEL_PRESENCE_BITNUM = 0; static const uint8_t WHEEL_FLAG = (1 << WHEEL_PRESENCE_BITNUM); static const unsigned CADENCE_PRESENCE_BITNUM = 1; static const uint8_t CADENCE_FLAG = (1 << CADENCE_PRESENCE_BITNUM); //speed CSCValueBytes(uint32_t wheelRevolutions ,uint16_t wheelEvent) : valueBytes() { valueBytes[FLAGS_BYTE_INDEX] = WHEEL_FLAG; updateWheel(wheelRevolutions,wheelEvent); } //cadence CSCValueBytes(uint16_t crankRevolutions, uint16_t crankEvent ) : valueBytes() { valueBytes[FLAGS_BYTE_INDEX] = CADENCE_FLAG; updateCrank(crankRevolutions,crankEvent); } //speed + cadence CSCValueBytes(uint32_t wheelRevolutions ,uint16_t wheelEvent,uint16_t crankRevolutions, uint16_t crankEvent) : valueBytes() { valueBytes[FLAGS_BYTE_INDEX] = (WHEEL_FLAG |CADENCE_FLAG); updateWheel(wheelRevolutions,wheelEvent); updateCrank(crankRevolutions,crankEvent); } void updateWheel(uint32_t wheelRevolutions ,uint16_t wheelEvent) { valueBytes[WHEEL_REVOLUTIONS_INDEX] = (uint8_t)(wheelRevolutions & 0xFF); valueBytes[WHEEL_REVOLUTIONS_INDEX + 1] = (uint8_t)((wheelRevolutions >> 8) & 0xFF); valueBytes[WHEEL_REVOLUTIONS_INDEX + 2] = (uint8_t)((wheelRevolutions >> 16) & 0xFF); valueBytes[WHEEL_REVOLUTIONS_INDEX + 3] = (uint8_t)((wheelRevolutions >> 24) & 0xFF); valueBytes[WHEEL_EVENT_INDEX] = (uint8_t)(wheelEvent & 0xFF); valueBytes[WHEEL_EVENT_INDEX + 1] = (uint8_t)((wheelEvent >> 8) & 0xFF); } void updateCrank(uint16_t crankRevolutions, uint16_t crankEvent) { int offset = (valueBytes[FLAGS_BYTE_INDEX] & WHEEL_FLAG) ? sizeof(uint32_t)+ sizeof(uint16_t):0; valueBytes[offset + CRANK_REVOLUTIONS_INDEX] = (uint8_t)(crankRevolutions & 0xFF); valueBytes[offset + CRANK_REVOLUTIONS_INDEX + 1] = (uint8_t)((crankRevolutions >> 8) & 0xFF); valueBytes[offset + CRANK_EVENT_INDEX] = (uint8_t)(crankEvent & 0xFF); valueBytes[offset + CRANK_EVENT_INDEX + 1] = (uint8_t)((crankEvent >> 8) & 0xFF); } uint8_t *getPointer(void) { return valueBytes; } const uint8_t *getPointer(void) const { return valueBytes; } unsigned getNumValueBytes(void) const { return 1 + ((valueBytes[FLAGS_BYTE_INDEX] & WHEEL_FLAG) ? sizeof(uint32_t)+ sizeof(uint16_t):0) + ((valueBytes[FLAGS_BYTE_INDEX] & CADENCE_FLAG) ? sizeof(uint16_t) + sizeof(uint16_t):0); } private: /* First byte = 8-bit values, no extra info, Second byte = uint8_t HRM value */ /* See --> https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml */ uint8_t valueBytes[MAX_VALUE_BYTES]; }; private: BLEDevice &ble; CSCFeatureBytes feautureBytes; CSCValueBytes valueBytes; uint8_t controlPointValue; GattCharacteristic cscMesurement; ReadOnlyGattCharacteristic<uint16_t> cscFeatures; ReadOnlyGattCharacteristic<uint8_t> sensorLocation; WriteOnlyGattCharacteristic<uint8_t> controlPoint; }; #endif /* #ifndef __BLE_CYCLE_SPEED_CADENCE_SERVICE_H__*/