BLE demo for the Cycling Speed and Cadence service.

Dependencies:   BLE_API mbed nRF51822

CyclingSpeedAndCadenceService.h

Committer:
p3miya
Date:
2016-03-10
Revision:
0:ea0fa3076efd

File content as of revision 0:ea0fa3076efd:

/*
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_SPEED_AND_CADENCE_SERVICE_H__
#define __BLE_CYCLING_SPEED_AND_CADENCE_SERVICE_H__

#include "ble/BLE.h"

#define UUID_SENSOR_LOCATION_CHAR   0x2A5D
#define UUID_CSC_CONTROL_POINT_CHAR 0x2A55


/**
* @class CyclingSpeedAndCadenceService
* @brief BLE Service for Cycling Speed and Cadence. <br>
* Service:  https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.cycling_speed_and_cadence.xml <br>
* CyclingSpeedAndCadenceService Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.csc_measurement.xml <br>
* Location: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.sensor_location.xml
*/
class CyclingSpeedAndCadenceService {
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_WHEEL_REVOLUTION_DATA      = 0x0001,
        FEATURE_CRANK_REVOLUTION_DATA      = 0x0002,
        FEATURE_MULTIPLE_SENSOR_LOCATIONS  = 0x0004,
    };

    enum {
        FLAG_WHEEL_REVOLUTION_DATA_PRESENT = 0x01,
        FLAG_CRANK_REVOLUTION_DATA_PRESENT = 0x02,
    };

    enum {
        OPCODE_SET_CUMULATIVE_VALUE               =  1,
        OPCODE_START_SENSOR_CALIBRATION           =  2,
        OPCODE_UPDATE_SENSOR_LOCATION             =  3,
        OPCODE_REQUEST_SUPPORTED_SENSOR_LOCATIONS =  4,
        OPCODE_RESPONSE_CODE                      = 16,
    };

    enum {
        RESPONSE_SUCCESS               = 1,
        RESPONSE_OP_CODE_NOT_SUPPORTED = 2,
        RESPONSE_INVALID_PARAMETER     = 3,
        RESPONSE_OPERATION_FAILED      = 4,
    };

public:
    CyclingSpeedAndCadenceService(BLE &_ble, uint16_t feature, uint8_t location) :
        ble(_ble),
        cyclingSpeedAndCadenceMeasurement(GattCharacteristic::UUID_CSC_MEASUREMENT_CHAR, measureData,
                       MAX_MEASURE_BYTES, MAX_MEASURE_BYTES,
                       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
        cyclingSpeedAndCadenceControlPoint(UUID_CSC_CONTROL_POINT_CHAR, controlData,
                       MAX_CONTROL_BYTES, MAX_CONTROL_BYTES,
                       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE),
        cyclingSpeedAndCadenceFeature(GattCharacteristic::UUID_CSC_FEATURE_CHAR, &feature),
        cyclingSpeedAndCadenceLocation(UUID_SENSOR_LOCATION_CHAR, &location) {
        setupService();
    }

    void updateCyclingSpeedAndCadence(uint8_t flags, uint16_t dnw, uint16_t dtw, uint16_t dnc, uint16_t dtc) {
        cumulativeWheelRev += dnw;
        lastWheelEventTime += dtw;
        cumulativeCrankRev += dnc;
        lastCrankEventTime += dtc;

        unsigned i = 0;
        measureData[i++] = flags;

        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);
        }
        unsigned nbyte = i;
        ble.updateCharacteristicValue(cyclingSpeedAndCadenceMeasurement.getValueAttribute().getHandle(), measureData, nbyte);
    }

    void indicateResponse(uint8_t opCode, uint8_t responseValue) {
        controlData[0] = OPCODE_RESPONSE_CODE;
        controlData[1] = opCode;
        controlData[2] = responseValue;
        unsigned nbyte = 3;
        ble.updateCharacteristicValue(cyclingSpeedAndCadenceControlPoint.getValueAttribute().getHandle(), controlData, nbyte);
    }

    void indicateResponse(uint8_t opCode, uint8_t responseValue, uint8_t responseParameter) {
        controlData[0] = OPCODE_RESPONSE_CODE;
        controlData[1] = opCode;
        controlData[2] = responseValue;
        controlData[3] = responseParameter;
        unsigned nbyte = 4;
        ble.updateCharacteristicValue(cyclingSpeedAndCadenceControlPoint.getValueAttribute().getHandle(), controlData, nbyte);
    }

    virtual void onDataWritten(const GattWriteCallbackParams *params) {
        if (params->handle == cyclingSpeedAndCadenceControlPoint.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, CyclingSpeedAndCadenceService::RESPONSE_SUCCESS);
                break;
            default:
                indicateResponse(opCode, CyclingSpeedAndCadenceService::RESPONSE_OP_CODE_NOT_SUPPORTED);
            }
        }
    }

protected:
    void setupService(void) {
        cumulativeWheelRev = 0;
        lastWheelEventTime = 0;
        cumulativeCrankRev = 0;
        lastCrankEventTime = 0;

        GattCharacteristic *charTable[] = {&cyclingSpeedAndCadenceMeasurement, &cyclingSpeedAndCadenceFeature, &cyclingSpeedAndCadenceLocation, &cyclingSpeedAndCadenceControlPoint};
        GattService         cyclingSpeedAndCadenceService(GattService::UUID_CYCLING_SPEED_AND_CADENCE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));

        ble.addService(cyclingSpeedAndCadenceService);
        ble.onDataWritten(this, &CyclingSpeedAndCadenceService::onDataWritten);
    }

protected:
    BLE &ble;

    GattCharacteristic                   cyclingSpeedAndCadenceMeasurement;
    GattCharacteristic                   cyclingSpeedAndCadenceControlPoint;
    ReadOnlyGattCharacteristic<uint16_t> cyclingSpeedAndCadenceFeature;
    ReadOnlyGattCharacteristic<uint8_t>  cyclingSpeedAndCadenceLocation;

    static const unsigned MAX_MEASURE_BYTES  = 11;
    static const unsigned MAX_CONTROL_BYTES  = 4;

    uint8_t measureData[MAX_MEASURE_BYTES];
    uint8_t controlData[MAX_CONTROL_BYTES];

    uint32_t cumulativeWheelRev;
    uint16_t lastWheelEventTime;
    uint16_t cumulativeCrankRev;
    uint16_t lastCrankEventTime;
};

#endif /* #ifndef __BLE_CYCLING_SPEED_AND_CADENCE_SERVICE_H__*/