BLE demo for the Cycling Power service.

Dependencies:   BLE_API mbed nRF51822

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers CyclingPowerService.h Source File

CyclingPowerService.h

00001 /*
00002 Copyright (c) 2016 Y. Miyakawa
00003 
00004 Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
00005 and associated documentation files (the "Software"), to deal in the Software without restriction, 
00006 including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
00007 and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
00008 subject to the following conditions:
00009 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
00010 
00011 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
00012 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
00013 PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
00014 FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
00015 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00016 */
00017 
00018 #ifndef __BLE_CYCLING_POWER_SERVICE_H__
00019 #define __BLE_CYCLING_POWER_SERVICE_H__
00020 
00021 #include "ble/BLE.h"
00022 
00023 #define UUID_CYCLING_POWER_SERVICE            0x1818
00024 #define UUID_SENSOR_LOCATION_CHAR             0x2A5D
00025 #define UUID_CYCLING_POWER_MEASUREMENT_CHAR   0x2A63
00026 #define UUID_CYCLING_POWER_FEATURE_CHAR       0x2A65
00027 #define UUID_CYCLING_POWER_VECTOR_CHAR        0x2A64
00028 #define UUID_CYCLING_POWER_CONTROL_POINT_CHAR 0x2A66
00029 
00030 
00031 /**
00032 * @class CyclingPowerService
00033 * @brief BLE Service for CyclingPower. <br>
00034 * Service:  https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.cycling_power.xml <br>
00035 * CyclingPower Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.cycling_power_measurement.xml <br>
00036 * Location: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.sensor_location.xml
00037 */
00038 class CyclingPowerService {
00039 public:
00040     enum {
00041         LOCATION_OTHER = 0,
00042         LOCATION_TOP_OF_SHOE,
00043         LOCATION_IN_SHOE,
00044         LOCATION_HIP,
00045         LOCATION_FRONT_WHEEL,
00046         LOCATION_LEFT_CRANK,
00047         LOCATION_RIGHT_CRANK,
00048         LOCATION_LEFT_PEDAL,
00049         LOCATION_RIGHT_PEDAL,
00050         LOCATION_FRONT_HUB,
00051         LOCATION_REAR_DROPOUT,
00052         LOCATION_CHAINSTAY,
00053         LOCATION_REAR_WHEEL,
00054         LOCATION_REAR_HUB,
00055         LOCATION_CHEST,
00056     };
00057 
00058     enum {
00059         FEATURE_PEDAL_POWER_BALANCE                 = 0x00000001,
00060         FEATURE_ACCUMULATED_TORQUE                  = 0x00000002,
00061         FEATURE_WHEEL_REVOLUTION_DATA               = 0x00000004,
00062         FEATURE_CRANK_REVOLUTION_DATA               = 0x00000008,
00063         FEATURE_EXTREME_MAGNITUDES                  = 0x00000010,
00064         FEATURE_EXTREME_ANGLES                      = 0x00000020,
00065         FEATURE_TOP_AND_BOTTOM_DEAD_SPOT_ANGLES     = 0x00000040,
00066         FEATURE_ACCUMULATED_ENERGY                  = 0x00000080,
00067         FEATURE_OFFSET_COMPENSATION_INDICATOR       = 0x00000100,
00068         FEATURE_OFFSET_COMPENSATION                 = 0x00000200,
00069         FEATURE_MEASUREMENT_CONTENT_MASKING         = 0x00000400,
00070         FEATURE_MULTIPLE_SENSOR_LOCATIONS           = 0x00000800,
00071         FEATURE_CRANK_LENGTH_ADJUSTMENT             = 0x00001000,
00072         FEATURE_CHAIN_LENGTH_ADJUSTMENT             = 0x00002000,
00073         FEATURE_CHAIN_WEIGHT_ADJUSTMENT             = 0x00004000,
00074         FEATURE_SPAN_LENGTH_ADJUSTMENT              = 0x00008000,
00075         FEATURE_SENSOR_MEASUREMENT_TORQUE_BASED     = 0x00010000,
00076         FEATURE_INSTANTANEOUS_MEASUREMENT_DIRECTION = 0x00020000,
00077         FEATURE_FACTORY_CALIBRATION_DATE            = 0x00040000,
00078     };
00079 
00080     enum {
00081         FLAG_PEDAL_POWER_BALANCE_PRESENT           = 0x0001,
00082         FLAG_PEDAL_POWER_BALANCE_REFERENCE_LEFT    = 0x0002,
00083         FLAG_ACCUMULATED_TORQUE_PRESENT            = 0x0004,
00084         FLAG_ACCUMULATED_TORQUE_SOURCE_CRANK_BASED = 0x0008,
00085         FLAG_WHEEL_REVOLUTION_DATA_PRESENT         = 0x0010,
00086         FLAG_CRANK_REVOLUTION_DATA_PRESENT         = 0x0020,
00087         FLAG_EXTREME_FORCE_MAGNITUDES_PRESENT      = 0x0040,
00088         FLAG_EXTREME_TORQUE_MAGNITUDES_PRESENT     = 0x0080,
00089         FLAG_EXTREME_ANGLES_PRESENT                = 0x0100,
00090         FLAG_TOP_DEAD_SPOT_ANGLE_PRESENT           = 0x0200,
00091         FLAG_BOTTOM_DEAD_SPOT_ANGLE_PRESENT        = 0x0400,
00092         FLAG_ACCUMULATED_ENERGY_PRESENT            = 0x0800,
00093         FLAG_OFFSET_COMPENSATION_INDICATOR         = 0x1000,
00094     };
00095 
00096     enum {
00097         OPCODE_SET_CUMULATIVE_VALUE               =  1,
00098         OPCODE_UPDATE_SENSOR_LOCATION             =  2,
00099         OPCODE_REQUEST_SUPPORTED_SENSOR_LOCATIONS =  3,
00100         OPCODE_SET_CRANK_LENGTH                   =  4,
00101         OPCODE_REQUEST_CRANK_LENGTH               =  5,
00102         OPCODE_SET_CHAIN_LENGTH                   =  6,
00103         OPCODE_REQUEST_CHAIN_LENGTH               =  7,
00104         OPCODE_SET_CHAIN_WEIGHT                   =  8,
00105         OPCODE_REQUEST_CHAIN_WEIGHT               =  9,
00106         OPCODE_SET_SPAN_LENGTH                    = 10,
00107         OPCODE_REQUEST_SPAN_LENGTH                = 11,
00108         OPCODE_START_OFFSET_COMPENSATION          = 12,
00109         OPCODE_MASK_CYCLING_POWER_MEASUREMENT_CHARACTERISTIC_CONTENT = 13,
00110         OPCODE_REQUEST_SAMPLING_RATE              = 14,
00111         OPCODE_REQUEST_FACTORY_CALIBRATION_DATE   = 15,
00112         OPCODE_RESPONSE_CODE                      = 32,
00113     };
00114 
00115     enum {
00116         RESPONSE_SUCCESS = 1,
00117         RESPONSE_OP_CODE_NOT_SUPPORTED,
00118         RESPONSE_INVALID_PARAMETER,
00119         RESPONSE_OPERATION_FAILED,
00120     };
00121 
00122 public:
00123     CyclingPowerService(BLE &_ble, uint32_t feature, uint8_t location) :
00124         ble(_ble),
00125         cyclingPowerMeasurement(UUID_CYCLING_POWER_MEASUREMENT_CHAR, measureData,
00126                        MAX_MEASURE_BYTES, MAX_MEASURE_BYTES,
00127                        GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
00128         cyclingPowerControlPoint(UUID_CYCLING_POWER_CONTROL_POINT_CHAR, controlData,
00129                        MAX_CONTROL_BYTES, MAX_CONTROL_BYTES,
00130                        GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE),
00131         cyclingPowerFeature(UUID_CYCLING_POWER_FEATURE_CHAR, &feature),
00132         cyclingPowerLocation(UUID_SENSOR_LOCATION_CHAR, &location) {
00133         setupService();
00134     }
00135 
00136     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) {
00137         instantaneousPower = pwr;
00138         pedalPowerBalance  = bal;
00139         maximumTorqueMagnitude = maxTorque;
00140         minimumTorqueMagnitude = minTorque;
00141 
00142         cumulativeWheelRev += dnw;
00143         lastWheelEventTime += dtw;
00144         cumulativeCrankRev += dnc;
00145         lastCrankEventTime += dtc;
00146 
00147         unsigned i = 0;
00148         measureData[i++] = (uint8_t)(flags & 0xFF);
00149         measureData[i++] = (uint8_t)(flags >> 8);
00150 
00151         measureData[i++] = (uint8_t)(instantaneousPower & 0xFF);
00152         measureData[i++] = (uint8_t)(instantaneousPower >> 8);
00153 
00154         if (flags & FLAG_PEDAL_POWER_BALANCE_PRESENT) {
00155             measureData[i++] = pedalPowerBalance;
00156         }
00157         if (flags & FLAG_WHEEL_REVOLUTION_DATA_PRESENT) {
00158             measureData[i++] = (uint8_t)(cumulativeWheelRev & 0xFF);
00159             measureData[i++] = (uint8_t)((cumulativeWheelRev >>  8) & 0xFF);
00160             measureData[i++] = (uint8_t)((cumulativeWheelRev >> 16) & 0xFF);
00161             measureData[i++] = (uint8_t)((cumulativeWheelRev >> 24) & 0xFF);
00162 
00163             measureData[i++] = (uint8_t)(lastWheelEventTime & 0xFF);
00164             measureData[i++] = (uint8_t)(lastWheelEventTime >> 8);
00165         }
00166         if (flags & FLAG_CRANK_REVOLUTION_DATA_PRESENT) {
00167             measureData[i++] = (uint8_t)(cumulativeCrankRev & 0xFF);
00168             measureData[i++] = (uint8_t)(cumulativeCrankRev >> 8);
00169 
00170             measureData[i++] = (uint8_t)(lastCrankEventTime & 0xFF);
00171             measureData[i++] = (uint8_t)(lastCrankEventTime >> 8);
00172         }
00173         if (flags & FLAG_EXTREME_TORQUE_MAGNITUDES_PRESENT) {
00174             measureData[i++] = (uint8_t)(maximumTorqueMagnitude & 0xFF);
00175             measureData[i++] = (uint8_t)(maximumTorqueMagnitude >> 8);
00176 
00177             measureData[i++] = (uint8_t)(minimumTorqueMagnitude & 0xFF);
00178             measureData[i++] = (uint8_t)(minimumTorqueMagnitude >> 8);
00179         }
00180         unsigned nbyte = i;
00181         ble.updateCharacteristicValue(cyclingPowerMeasurement.getValueAttribute().getHandle(), measureData, nbyte);
00182     }
00183 
00184     void indicateResponse(uint8_t opCode, uint8_t responseValue) {
00185         controlData[0] = 0x20;    // Response Code
00186         controlData[1] = opCode;
00187         controlData[2] = responseValue;
00188         unsigned nbyte = 3;
00189         ble.updateCharacteristicValue(cyclingPowerControlPoint.getValueAttribute().getHandle(), controlData, nbyte);
00190     }
00191 
00192     void indicateResponse(uint8_t opCode, uint8_t responseValue, uint16_t responseParameter) {
00193         controlData[0] = 0x20;    // Response Code
00194         controlData[1] = opCode;
00195         controlData[2] = responseValue;
00196         controlData[3] = (uint8_t)(responseParameter & 0xFF);
00197         controlData[4] = (uint8_t)(responseParameter >> 8);
00198         unsigned nbyte = 5;
00199         ble.updateCharacteristicValue(cyclingPowerControlPoint.getValueAttribute().getHandle(), controlData, nbyte);
00200     }
00201 
00202     virtual void onDataWritten(const GattWriteCallbackParams *params) {
00203         if (params->handle == cyclingPowerControlPoint.getValueAttribute().getHandle()) {
00204             uint32_t uu;
00205             uint8_t opCode = params->data[0];
00206             switch(opCode) {
00207             case OPCODE_SET_CUMULATIVE_VALUE:
00208                 uu  = params->data[4];
00209                 uu *= 256;
00210                 uu += params->data[3];
00211                 uu *= 256;
00212                 uu += params->data[2];
00213                 uu *= 256;
00214                 uu += params->data[1];
00215                 cumulativeWheelRev = uu;
00216                 indicateResponse(opCode, CyclingPowerService::RESPONSE_SUCCESS);
00217                 break;
00218             default:
00219                 indicateResponse(opCode, CyclingPowerService::RESPONSE_OP_CODE_NOT_SUPPORTED);
00220             }
00221         }
00222     }
00223 
00224 protected:
00225     void setupService(void) {
00226         instantaneousPower = 0;
00227         pedalPowerBalance = 50 * 2;   // 50%
00228         maximumTorqueMagnitude = 0;
00229         minimumTorqueMagnitude = 0;
00230 
00231         cumulativeWheelRev = 0;
00232         lastWheelEventTime = 0;
00233         cumulativeCrankRev = 0;
00234         lastCrankEventTime = 0;
00235 
00236         GattCharacteristic *charTable[] = {&cyclingPowerMeasurement, &cyclingPowerFeature, &cyclingPowerLocation, &cyclingPowerControlPoint};
00237         GattService         cyclingPowerService(UUID_CYCLING_POWER_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
00238 
00239         ble.addService(cyclingPowerService);
00240         ble.onDataWritten(this, &CyclingPowerService::onDataWritten);
00241     }
00242 
00243 protected:
00244     BLE &ble;
00245 
00246     GattCharacteristic                   cyclingPowerMeasurement;
00247     GattCharacteristic                   cyclingPowerControlPoint;
00248     ReadOnlyGattCharacteristic<uint32_t> cyclingPowerFeature;
00249     ReadOnlyGattCharacteristic<uint8_t>  cyclingPowerLocation;
00250 
00251     static const unsigned MAX_MEASURE_BYTES  = 19;
00252     static const unsigned MAX_CONTROL_BYTES  = 5;
00253 
00254     uint8_t measureData[MAX_MEASURE_BYTES];
00255     uint8_t controlData[MAX_CONTROL_BYTES];
00256 
00257     int16_t instantaneousPower;
00258     uint8_t pedalPowerBalance;
00259     int16_t maximumTorqueMagnitude;
00260     int16_t minimumTorqueMagnitude;
00261 
00262     uint32_t cumulativeWheelRev;
00263     uint16_t lastWheelEventTime;
00264     uint16_t cumulativeCrankRev;
00265     uint16_t lastCrankEventTime;
00266 };
00267 
00268 #endif /* #ifndef __BLE_CYCLING_POWER_SERVICE_H__*/