BLE demo for the Cycling Power service.
Dependencies: BLE_API mbed nRF51822
Diff: CyclingPowerService.h
- Revision:
- 0:d9d7edb1ddfc
- Child:
- 1:e2e4def8a6bb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CyclingPowerService.h Sat Feb 27 12:27:48 2016 +0000 @@ -0,0 +1,268 @@ +/* +Copyright (c) 2016 Y. Miyakawa + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __BLE_CYCLING_POWER_SERVICE_H__ +#define __BLE_CYCLING_POWER_SERVICE_H__ + +#include "ble/BLE.h" + +#define UUID_CYCLING_POWER_SERVICE 0x1818 +#define UUID_SENSOR_LOCATION_CHAR 0x2A5D +#define UUID_CYCLING_POWER_MEASUREMENT_CHAR 0x2A63 +#define UUID_CYCLING_POWER_FEATURE_CHAR 0x2A65 +#define UUID_CYCLING_POWER_VECTOR_CHAR 0x2A64 +#define UUID_CYCLING_POWER_CONTROL_POINT_CHAR 0x2A66 + + +/** +* @class CyclingPowerService +* @brief BLE Service for CyclingPower. <br> +* Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.cycling_power.xml <br> +* CyclingPower Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.cycling_power_measurement.xml <br> +* Location: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.sensor_location.xml +*/ +class CyclingPowerService { +public: + 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 { + FEATURE_PEDAL_POWER_BALANCE = 0x00000001, + FEATURE_ACCUMULATED_TORQUE = 0x00000002, + FEATURE_WHEEL_REVOLUTION_DATA = 0x00000004, + FEATURE_CRANK_REVOLUTION_DATA = 0x00000008, + FEATURE_EXTREME_MAGNITUDES = 0x00000010, + FEATURE_EXTREME_ANGLES = 0x00000020, + FEATURE_TOP_AND_BOTTOM_DEAD_SPOT_ANGLES = 0x00000040, + FEATURE_ACCUMULATED_ENERGY = 0x00000080, + FEATURE_OFFSET_COMPENSATION_INDICATOR = 0x00000100, + FEATURE_OFFSET_COMPENSATION = 0x00000200, + FEATURE_MEASUREMENT_CONTENT_MASKING = 0x00000400, + FEATURE_MULTIPLE_SENSOR_LOCATIONS = 0x00000800, + FEATURE_CRANK_LENGTH_ADJUSTMENT = 0x00001000, + FEATURE_CHAIN_LENGTH_ADJUSTMENT = 0x00002000, + FEATURE_CHAIN_WEIGHT_ADJUSTMENT = 0x00004000, + FEATURE_SPAN_LENGTH_ADJUSTMENT = 0x00008000, + FEATURE_SENSOR_MEASUREMENT_TORQUE_BASED = 0x00010000, + FEATURE_INSTANTANEOUS_MEASUREMENT_DIRECTION = 0x00020000, + FEATURE_FACTORY_CALIBRATION_DATE = 0x00040000, + }; + + enum { + FLAG_PEDAL_POWER_BALANCE_PRESENT = 0x0001, + FLAG_PEDAL_POWER_BALANCE_REFERENCE_LEFT = 0x0002, + FLAG_ACCUMULATED_TORQUE_PRESENT = 0x0004, + FLAG_ACCUMULATED_TORQUE_SOURCE_CRANK_BASED = 0x0008, + FLAG_WHEEL_REVOLUTION_DATA_PRESENT = 0x0010, + FLAG_CRANK_REVOLUTION_DATA_PRESENT = 0x0020, + FLAG_EXTREME_FORCE_MAGNITUDES_PRESENT = 0x0040, + FLAG_EXTREME_TORQUE_MAGNITUDES_PRESENT = 0x0080, + FLAG_EXTREME_ANGLES_PRESENT = 0x0100, + FLAG_TOP_DEAD_SPOT_ANGLE_PRESENT = 0x0200, + FLAG_BOTTOM_DEAD_SPOT_ANGLE_PRESENT = 0x0400, + FLAG_ACCUMULATED_ENERGY_PRESENT = 0x0800, + FLAG_OFFSET_COMPENSATION_INDICATOR = 0x1000, + }; + + enum { + OPCODE_SET_CUMULATIVE_VALUE = 1, + OPCODE_UPDATE_SENSOR_LOCATION = 2, + OPCODE_REQUEST_SUPPORTED_SENSOR_LOCATIONS = 3, + OPCODE_SET_CRANK_LENGTH = 4, + OPCODE_REQUEST_CRANK_LENGTH = 5, + OPCODE_SET_CHAIN_LENGTH = 6, + OPCODE_REQUEST_CHAIN_LENGTH = 7, + OPCODE_SET_CHAIN_WEIGHT = 8, + OPCODE_REQUEST_CHAIN_WEIGHT = 9, + OPCODE_SET_SPAN_LENGTH = 10, + OPCODE_REQUEST_SPAN_LENGTH = 11, + OPCODE_START_OFFSET_COMPENSATION = 12, + OPCODE_MASK_CYCLING_POWER_MEASUREMENT_CHARACTERISTIC_CONTENT = 13, + OPCODE_REQUEST_SAMPLING_RATE = 14, + OPCODE_REQUEST_FACTORY_CALIBRATION_DATE = 15, + OPCODE_RESPONSE_CODE = 32, + }; + + enum { + RESPONSE_SUCCESS = 1, + RESPONSE_OP_CODE_NOT_SUPPORTED, + RESPONSE_INVALID_PARAMETER, + RESPONSE_OPERATION_FAILED, + }; + +public: + CyclingPowerService(BLE &_ble, uint32_t feature, uint8_t location) : + ble(_ble), + cyclingPowerMeasurement(UUID_CYCLING_POWER_MEASUREMENT_CHAR, measureData, + MAX_MEASURE_BYTES, MAX_MEASURE_BYTES, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY), + cyclingPowerControlPoint(UUID_CYCLING_POWER_CONTROL_POINT_CHAR, controlData, + MAX_CONTROL_BYTES, MAX_CONTROL_BYTES, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE), + cyclingPowerFeature(UUID_CYCLING_POWER_FEATURE_CHAR, &feature), + cyclingPowerLocation(UUID_SENSOR_LOCATION_CHAR, &location) { + setupService(); + } + + void updateCyclingPower(uint16_t flags, int16_t pwr, uint8_t bal, int16_t maxTorque, int16_t minTorque, uint16_t dnw, uint16_t dtw, uint16_t dnc, uint16_t dtc) { + instantaneousPower = pwr; + pedalPowerBalance = bal; + maximumTorqueMagnitude = maxTorque; + minimumTorqueMagnitude = minTorque; + + cumulativeWheelRev += dnw; + lastWheelEventTime += dtw; + cumulativeCrankRev += dnc; + lastCrankEventTime += dtc; + + unsigned i = 0; + measureData[i++] = (uint8_t)(flags & 0xFF); + measureData[i++] = (uint8_t)(flags >> 8); + + measureData[i++] = (uint8_t)(instantaneousPower & 0xFF); + measureData[i++] = (uint8_t)(instantaneousPower >> 8); + + if (flags & FLAG_PEDAL_POWER_BALANCE_PRESENT) { + measureData[i++] = pedalPowerBalance; + } + if (flags & FLAG_WHEEL_REVOLUTION_DATA_PRESENT) { + measureData[i++] = (uint8_t)(cumulativeWheelRev & 0xFF); + measureData[i++] = (uint8_t)((cumulativeWheelRev >> 8) & 0xFF); + measureData[i++] = (uint8_t)((cumulativeWheelRev >> 16) & 0xFF); + measureData[i++] = (uint8_t)((cumulativeWheelRev >> 24) & 0xFF); + + measureData[i++] = (uint8_t)(lastWheelEventTime & 0xFF); + measureData[i++] = (uint8_t)(lastWheelEventTime >> 8); + } + if (flags & FLAG_CRANK_REVOLUTION_DATA_PRESENT) { + measureData[i++] = (uint8_t)(cumulativeCrankRev & 0xFF); + measureData[i++] = (uint8_t)(cumulativeCrankRev >> 8); + + measureData[i++] = (uint8_t)(lastCrankEventTime & 0xFF); + measureData[i++] = (uint8_t)(lastCrankEventTime >> 8); + } + if (flags & FLAG_EXTREME_TORQUE_MAGNITUDES_PRESENT) { + measureData[i++] = (uint8_t)(maximumTorqueMagnitude & 0xFF); + measureData[i++] = (uint8_t)(maximumTorqueMagnitude >> 8); + + measureData[i++] = (uint8_t)(minimumTorqueMagnitude & 0xFF); + measureData[i++] = (uint8_t)(minimumTorqueMagnitude >> 8); + } + unsigned nbyte = i; + ble.updateCharacteristicValue(cyclingPowerMeasurement.getValueAttribute().getHandle(), measureData, nbyte); + } + + void indicateResponse(uint8_t opCode, uint8_t responseValue) { + controlData[0] = 0x20; // Response Code + controlData[1] = opCode; + controlData[2] = responseValue; + unsigned nbyte = 3; + ble.updateCharacteristicValue(cyclingPowerControlPoint.getValueAttribute().getHandle(), controlData, nbyte); + } + + void indicateResponse(uint8_t opCode, uint8_t responseValue, uint16_t responseParameter) { + controlData[0] = 0x20; // Response Code + controlData[1] = opCode; + controlData[2] = responseValue; + controlData[3] = (uint8_t)(responseParameter & 0xFF); + controlData[4] = (uint8_t)(responseParameter >> 8); + unsigned nbyte = 5; + ble.updateCharacteristicValue(cyclingPowerControlPoint.getValueAttribute().getHandle(), controlData, nbyte); + } + + virtual void onDataWritten(const GattWriteCallbackParams *params) { + if (params->handle == cyclingPowerControlPoint.getValueAttribute().getHandle()) { + uint32_t uu; + uint8_t opCode = params->data[0]; + switch(opCode) { + case OPCODE_SET_CUMULATIVE_VALUE: + uu = params->data[4]; + uu *= 256; + uu += params->data[3]; + uu *= 256; + uu += params->data[2]; + uu *= 256; + uu += params->data[1]; + cumulativeWheelRev = uu; + indicateResponse(opCode, CyclingPowerService::RESPONSE_SUCCESS); + break; + default: + indicateResponse(opCode, CyclingPowerService::RESPONSE_OP_CODE_NOT_SUPPORTED); + } + } + } + +protected: + void setupService(void) { + instantaneousPower = 0; + pedalPowerBalance = 50 * 2; // 50% + maximumTorqueMagnitude = 0; + minimumTorqueMagnitude = 0; + + cumulativeWheelRev = 0; + lastWheelEventTime = 0; + cumulativeCrankRev = 0; + lastCrankEventTime = 0; + + GattCharacteristic *charTable[] = {&cyclingPowerMeasurement, &cyclingPowerFeature, &cyclingPowerLocation, &cyclingPowerControlPoint}; + GattService cyclingPowerService(UUID_CYCLING_POWER_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); + + ble.addService(cyclingPowerService); + ble.onDataWritten(this, &CyclingPowerService::onDataWritten); + } + +protected: + BLE &ble; + + GattCharacteristic cyclingPowerMeasurement; + GattCharacteristic cyclingPowerControlPoint; + ReadOnlyGattCharacteristic<uint32_t> cyclingPowerFeature; + ReadOnlyGattCharacteristic<uint8_t> cyclingPowerLocation; + + static const unsigned MAX_MEASURE_BYTES = 19; + static const unsigned MAX_CONTROL_BYTES = 5; + + uint8_t measureData[MAX_MEASURE_BYTES]; + uint8_t controlData[MAX_CONTROL_BYTES]; + + int16_t instantaneousPower; + uint8_t pedalPowerBalance; + int16_t maximumTorqueMagnitude; + int16_t minimumTorqueMagnitude; + + uint32_t cumulativeWheelRev; + uint16_t lastWheelEventTime; + uint16_t cumulativeCrankRev; + uint16_t lastCrankEventTime; +}; + +#endif /* #ifndef __BLE_CYCLING_POWER_SERVICE_H__*/