Odometry Pedometer using nRF51822 and ADXL345

Dependencies:   ADXL345 BLE_API mbed nRF51822

Fork of BLE_CycleSpeedCadence by Robert Walker

Files at this revision

API Documentation at this revision

Comitter:
tenfoot
Date:
Sun Aug 16 13:23:48 2015 +0000
Parent:
70:0a19690e8d23
Child:
72:a15b8451829f
Commit message:
Initial cycle speed / cadence

Changed in this revision

CyclingSpeedAndCadenceService.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CyclingSpeedAndCadenceService.h	Sun Aug 16 13:23:48 2015 +0000
@@ -0,0 +1,222 @@
+/* 
+ * Copyright (c) 2015 Robert Walker
+ *
+ * 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_CYCLING_SPEED_AND_CADENCE_SERVICE_H__
+#define __BLE_CYCLING_SPEED_AND_CADENCE_SERVICE_H__
+
+#include "ble/BLE.h"
+
+/**
+* @class CyclingSpeedAndCadenceService
+* @brief BLE Service for Cycling Speed and Cadence. This BLE Service contains the location of the sensor, the total wheel revolutions, total crank revolutiosn. <br>
+* Service:  https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.cycling_speed_and_cadence.xml <br>
+* CSC 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 SensorLocation
+    * @brief Location of sensor on bike.
+    */
+    enum {
+        LOCATION_OTHER,        /*!< Other */
+        LOCATION_TOP_OF_SHOE,  /*!< Top of shoe */
+        LOCATION_IN_SHOE,      /*!< In shoe */
+        LOCATION_HIP,          /*!< Hip */
+        LOCATION_FRONT_WHEEL,  /*!< Front Wheel */
+        LOCATION_LEFT_CRANK,   /*!< Left Crank */
+        LOCATION_RIGHT_CRANK,  /*!< Right Crank */
+        LOCATION_LEFT_PEDAL,   /*!< Left Pedal */
+        LOCATION_RIGHT_PEDAL,  /*!< Right Pedal */
+        LOCATION_FRONT_HUB,    /*!< Front Hub */
+        LOCATION_REAR_DROPOUT, /*!< Rear Dropout */
+        LOCATION_CHAINSTAY,    /*!< Chainstay */
+        LOCATION_REAR_WHEEL,   /*!< Rear Wheel */
+        LOCATION_REAR_HUB,     /*!< Rear Hub */
+        LOCATION_CHEST,        /*!< Chest */
+    };
+    
+    enum {
+        UUID_SENSOR_LOCATION_CHAR = 0x2A5D,
+        UUID_SC_CONTROL_POINT_CHAR = 0x2A55
+    };
+
+public:
+    /**
+     * @brief Constructor with initial counter values.
+     *
+     * @param[ref] _ble
+     *               Reference to the underlying BLE.
+     * @param[in] wheelCounter (32-bit)
+     *               initial value for the wheel counter.
+     * @param[in] crankCounter (32-bit)
+     *               initial value for the crank counter.
+     * @param[in] location
+     *               Sensor's location.
+     */
+    CyclingSpeedAndCadenceService(BLE &_ble, uint8_t location) :
+        ble(_ble),
+        csc(GattCharacteristic::UUID_CSC_MEASUREMENT_CHAR, valueBytes.getPointer(),
+            valueBytes.getNumValueBytes(), SpeedCadenceValueBytes::MAX_BYTES,
+            GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
+        scLocation(UUID_SENSOR_LOCATION_CHAR, &location),
+        controlPoint(UUID_SC_CONTROL_POINT_CHAR, &controlPointValue) {
+        setupService();
+    }
+
+    /**
+     * @brief Set a new value for wheel revolutions.
+     *
+     * @param[in] wheelCounter
+     *                  Total wheel revolutions.
+     * @param[in] eventTime
+     *                  Time of event.
+     */
+    void updateWheelCounter(uint32_t wheelCounter, uint16_t eventTime) {
+        valueBytes.updateWheelCounter(wheelCounter, eventTime);
+        ble.gattServer().write(csc.getValueHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes());
+    }
+
+    /**
+     * @brief Set a new value for crank revolutions.
+     *
+     * @param[in] crankCounter
+     *                  Total crank revolutions.
+     * @param[in] eventTime
+     *                  Time of event.
+     */
+    void updateCrankCounter(uint16_t crankCounter, uint16_t eventTime) {
+        valueBytes.updateCrankCounter(crankCounter, eventTime);
+        ble.gattServer().write(csc.getValueHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes());
+    }
+
+    void updateCounters(uint32_t wheelCounter, uint16_t crankCounter, uint16_t eventTime) {
+        valueBytes.updateWheelCounter(wheelCounter, eventTime);
+        valueBytes.updateCrankCounter(crankCounter, eventTime);
+        ble.gattServer().write(csc.getValueHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes());
+    }
+
+    /**
+     * This callback allows the CyclingSpeedAndCadenceService to receive updates to the
+     * controlPoint Characteristic.
+     *
+     * @param[in] params
+     *     Information about the characterisitc being updated.
+     */
+    virtual void onDataWritten(const GattWriteCallbackParams *params) {
+        if (params->handle == controlPoint.getValueAttribute().getHandle()) {
+            /* 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.
+             */
+        }
+    }
+
+protected:
+    void setupService(void) {
+        GattCharacteristic *charTable[] = {&csc, &scLocation, &controlPoint};
+        GattService         cscService(GattService::UUID_CYCLING_SPEED_AND_CADENCE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
+
+        ble.addService(cscService);
+        ble.onDataWritten(this, &CyclingSpeedAndCadenceService::onDataWritten);
+    }
+
+protected:
+    /* Private internal representation for the bytes used to work with the value of the speed cadence characteristic. */
+    struct SpeedCadenceValueBytes {
+        static const uint16_t MAX_BYTES = (1 + 4 + 2 + 2 + 2);
+        static const uint8_t FLAG_WHEEL_PRESENT = (1 << 0);
+        static const uint8_t FLAG_CRANK_PRESENT = (1 << 1);
+
+        SpeedCadenceValueBytes()
+            : flags(0)
+        {
+            updateWheelCounter(1, 0);
+            updateCrankCounter(1, 0);
+        }
+
+        void updateWheelCounter(uint32_t _wheelCounter, uint16_t _when) {
+            flags |= FLAG_WHEEL_PRESENT;
+            wheelCounter = _wheelCounter;
+            lastWheelEvent = _when;
+        }
+
+        void updateCrankCounter(uint16_t _crankCounter, uint16_t _when) {
+            flags |= FLAG_CRANK_PRESENT;
+            crankCounter = _crankCounter;
+            lastCrankEvent = _when;
+        }
+
+        uint8_t       *getPointer(void) {
+            pack();
+            return valueBytes;
+        }
+
+        const uint8_t *getPointer(void) const {
+            pack();
+            return valueBytes;
+        }
+
+        unsigned       getNumValueBytes(void) const {
+            return 1 +
+                ((flags & FLAG_WHEEL_PRESENT) ? (4+2) : 0) +
+                ((flags & FLAG_CRANK_PRESENT) ? (2+2) : 0);
+        }
+
+    private:
+        void pack() const
+        {
+            valueBytes[0] = flags;
+            uint8_t* p = &valueBytes[1];
+            if (flags & FLAG_WHEEL_PRESENT)
+            {
+                *(uint32_t*)(p) = wheelCounter;
+                p += 4;
+                *(uint16_t*)(p) = lastWheelEvent;
+                p += 2;
+            }
+            if (flags & FLAG_CRANK_PRESENT)
+            {
+                *(uint16_t*)(p) = crankCounter;
+                p += 2;
+                *(uint16_t*)(p) = lastCrankEvent;
+                p += 2;
+            }
+        }
+        
+        uint8_t flags;
+        uint32_t wheelCounter;
+        uint16_t lastWheelEvent;
+        uint16_t crankCounter;
+        uint16_t lastCrankEvent;
+        mutable uint8_t valueBytes[MAX_BYTES];
+    };
+
+protected:
+    BLE                 &ble;
+
+    SpeedCadenceValueBytes  valueBytes;
+    uint8_t              controlPointValue;
+
+    GattCharacteristic                   csc;
+    ReadOnlyGattCharacteristic<uint8_t>  scLocation;
+    WriteOnlyGattCharacteristic<uint8_t> controlPoint;
+};
+
+#endif /* #ifndef __BLE_CYCLING_SPEED_AND_CADENCE_SERVICE_H__*/
\ No newline at end of file
--- a/main.cpp	Tue Aug 11 21:58:13 2015 +0000
+++ b/main.cpp	Sun Aug 16 13:23:48 2015 +0000
@@ -16,15 +16,15 @@
 
 #include "mbed.h"
 #include "ble/BLE.h"
-#include "ble/services/HeartRateService.h"
+#include "CyclingSpeedAndCadenceService.h"
 #include "ble/services/BatteryService.h"
 #include "ble/services/DeviceInformationService.h"
 
 BLE  ble;
 DigitalOut led1(LED1);
 
-const static char     DEVICE_NAME[]        = "HRM1";
-static const uint16_t uuid16_list[]        = {GattService::UUID_HEART_RATE_SERVICE,
+const static char     DEVICE_NAME[]        = "CSC1";
+static const uint16_t uuid16_list[]        = {GattService::UUID_CYCLING_SPEED_AND_CADENCE,
                                               GattService::UUID_DEVICE_INFORMATION_SERVICE};
 static volatile bool  triggerSensorPolling = false;
 
@@ -52,22 +52,24 @@
     ble.gap().onDisconnection(disconnectionCallback);
 
     /* Setup primary service. */
-    uint8_t hrmCounter = 100; // init HRM to 100bps
-    HeartRateService hrService(ble, hrmCounter, HeartRateService::LOCATION_FINGER);
+    uint32_t wheelCounter = 100; // init Wheel to 100revs
+    uint16_t crankCounter = 10; // init crank to 10revs
+    CyclingSpeedAndCadenceService cscService(ble, CyclingSpeedAndCadenceService::LOCATION_CHAINSTAY);
 
     /* Setup auxiliary service. */
-    DeviceInformationService deviceInfo(ble, "ARM", "Model1", "SN1", "hw-rev1", "fw-rev1", "soft-rev1");
+    DeviceInformationService deviceInfo(ble, "ROB", "Model1", "SN1", "hw-rev1", "fw-rev1", "soft-rev1");
 
     /* Setup advertising. */
     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
-    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::CYCLING_SPEED_AND_CADENCE_SENSOR);
     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
     ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
     ble.gap().setAdvertisingInterval(1000); /* 1000ms */
     ble.gap().startAdvertising();
 
     // infinite loop
+    uint16_t when = 0;
     while (1) {
         // check for trigger from periodicCallback()
         if (triggerSensorPolling && ble.getGapState().connected) {
@@ -75,15 +77,12 @@
 
             // Do blocking calls or whatever is necessary for sensor polling.
             // In our case, we simply update the HRM measurement.
-            hrmCounter++;
-
-            //  100 <= HRM bps <=175
-            if (hrmCounter == 175) {
-                hrmCounter = 100;
-            }
+            wheelCounter += 3;
+            crankCounter++;
+            when += 1024;
 
             // update bps
-            hrService.updateHeartRate(hrmCounter);
+            cscService.updateCounters(wheelCounter, crankCounter, when);
         } else {
             ble.waitForEvent(); // low power wait for event
         }