BLE GATT-service implementation for high quantity sensor data from a MPU6050-accelerator/gyroscope

Dependencies:   BLE_API mbed nRF51822 MPU6050_lib

Revision:
10:b305e261e7d1
Parent:
9:6a28d9c0e486
Child:
12:dc46aa2edccd
--- a/MPU6050Service.h	Wed Jul 22 16:03:03 2015 +0000
+++ b/MPU6050Service.h	Tue Sep 08 13:42:58 2015 +0000
@@ -13,8 +13,9 @@
             UUID_MPU6050_SERVICE                = 0xA123,
             
             UUID_MPU6050_SENSOR_DATA_CHAR       = 0xA124,
-            UUID_MPU6050_MEASURING_RANGE_CHAR   = 0xA125,
-            UUID_MPU6050_MASTER_CLOCK_CHAR      = 0xA126
+            UUID_MPU6050_MOTION_INDICATOR_CHAR  = 0xA125,
+            UUID_MPU6050_MEASURING_RANGE_CHAR   = 0xA126,
+            UUID_MPU6050_MASTER_CLOCK_CHAR      = 0xA127
         };
         
         enum MeasuringRange
@@ -24,40 +25,72 @@
             ACCEL_RANGE_8G                      = 0x02, // 0b__10
             ACCEL_RANGE_16G                     = 0x03, // 0b__11
             
+            ACCEL_RANGE_MASK                    = 0x03, // 0b__11
+            
             GYRO_RANGE_250                      = 0x00, // 0b00__
             GYRO_RANGE_500                      = 0x04, // 0b01__
             GYRO_RANGE_1000                     = 0x08, // 0b10__
-            GYRO_RANGE_2000                     = 0x0C  // 0b11__
+            GYRO_RANGE_2000                     = 0x0C, // 0b11__
+            
+            GYRO_RANGE_MASK                     = 0x0C  // 0b11__
         };
         friend inline MeasuringRange operator|(MeasuringRange a, MeasuringRange b) { return static_cast<MeasuringRange>(static_cast<int>(a) | static_cast<int>(b)); }
+        friend inline MeasuringRange operator&(MeasuringRange a, MeasuringRange b) { return static_cast<MeasuringRange>(static_cast<int>(a) & static_cast<int>(b)); }
         
-        typedef struct
+        enum MotionIndicator
         {
-            int16_t     accelX, accelY, accelZ;
+            MOTION_IND_NEGATIVE_X               = MPU6050::MOT_NEGATIVE_X,
+            MOTION_IND_POSITIVE_X               = MPU6050::MOT_POSITIVE_X,
+            
+            MOTION_IND_NEGATIVE_Y               = MPU6050::MOT_NEGATIVE_Y,
+            MOTION_IND_POSITIVE_Y               = MPU6050::MOT_POSITIVE_Y,
+            
+            MOTION_IND_NEGATIVE_Z               = MPU6050::MOT_NEGATIVE_Z,
+            MOTION_IND_POSITIVE_Z               = MPU6050::MOT_POSITIVE_Z,
             
-            int16_t     temperature;
+            MOTION_IND_FREEFALL                 = 0x02, // 0b00000010
+            MOTION_IND_ZERO_MOTION              = MPU6050::MOT_ZERO
+        };
+        friend inline MotionIndicator operator|(MotionIndicator a, MotionIndicator b) { return static_cast<MotionIndicator>(static_cast<int>(a) | static_cast<int>(b)); }
+        friend inline MotionIndicator operator&(MotionIndicator a, MotionIndicator b) { return static_cast<MotionIndicator>(static_cast<int>(a) & static_cast<int>(b)); }
+        
+        typedef __packed struct
+        {
+            int16_t         accelX, accelY, accelZ;
+            
+            int16_t         temperature;
             
-            int16_t     gyroX, gyroY, gyroZ;
+            int16_t         gyroX, gyroY, gyroZ;
             
-            uint32_t    clock_us;
+            uint32_t        clock_us;
         } SensorData;
         
+        typedef __packed struct
+        {
+            MotionIndicator indicator;
+            
+            uint32_t        clock_us;
+            
+        } MotionIndication;
+        
         MPU6050Service
         (
             BLE                    &ble,
             MPU6050                &mpu6050,
+            PinName                 interruptPin,
             const SensorData       *initialData         = NULL, 
             const MeasuringRange    measuringRange      = ACCEL_RANGE_2G | GYRO_RANGE_250, 
             const uint32_t          masterClock_us      = 0
         ) :
             gattServer(ble.gattServer()),
             mpu(mpu6050),
-            timeAccumulationTicker(),
+            intIn(interruptPin),
+            timer(),
             
             _isRunning(false),
             updateSensorDataCharacteristicTrigger(false),
             
-            time(masterClock_us),
+            timerOffset_us(masterClock_us),
             
             sensorDataCharacteristic
             (
@@ -66,6 +99,12 @@
                 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY // improving latency by repurposing notifications and "writing" the client
                                                                     // (i.e. indicating the client but not wait for a response)
             ),
+            motionIndicatorCharaceristic
+            (
+                UUID_MPU6050_MOTION_INDICATOR_CHAR,
+                NULL,
+                GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
+            ),
             measuringRangeCharacteristic
             (
                 UUID_MPU6050_MEASURING_RANGE_CHAR,
@@ -82,12 +121,11 @@
             static bool serviceAdded = false;
             if (serviceAdded)
                 return;
-             
-            timeAccumulationTicker.attach(&MPU6050::timeAccumulationPeriodicCallback, 1.0f / (float)(1000 * 1000 * timerResolution_us));
                 
             GattCharacteristic *charTable[] =
             {
                 &sensorDataCharacteristic,
+                &motionIndicatorCharaceristic,
                 &measuringRangeCharacteristic,
                 &masterClockCharacteristic
             };
@@ -98,6 +136,8 @@
             gattServer.onDataSent(this, &MPU6050Service::dataSentCallback);
             gattServer.onDataWritten(this, &MPU6050Service::dataWrittenCallback);
             
+            intIn.rise(this, &MPU6050Service::interruptRiseCallback);
+            
             serviceAdded = true;
         }
         
@@ -113,6 +153,10 @@
                 _isRunning = false;
                 return false;
             }
+            
+            timer.stop();
+            timer.reset();
+            timer.start();
                 
             _isRunning = true;
                 
@@ -124,42 +168,42 @@
         }
         
         void stop()
-        {
+        {            
             _isRunning = false;
         }
         
         void handleService()
         {
-            if (_isRunning && updateSensorDataCharacteristicTrigger)
-                updateSensorDataCharacteristic();
+            if (_isRunning)
+            {
+                if (updateMeasuringRangeTrigger)
+                    updateMeasuringRange();
+                if (updateSensorDataCharacteristicTrigger)
+                    updateSensorDataCharacteristic();
+                if (updateMotionIndicatorCharacteristicTrigger)
+                    updateMotionIndicatorCharacteristic();
+            }
         }
         
-    private:
-        static const uint32_t                                   timerResolution_us                      = 100;
+    private:  
+        static const int                                        MAX_SENSOR_POLLS_PER_UPDATE = 15;
     
         GattServer                                             &gattServer; 
         MPU6050                                                &mpu;
-        Ticker                                                  timeAccumulationTicker;
+        InterruptIn                                             intIn;
+        Timer                                                   timer;
         
         volatile bool                                           _isRunning;
         bool                                                    updateSensorDataCharacteristicTrigger;
-        
-        uint32_t                                                time;
+        bool                                                    updateMotionIndicatorCharacteristicTrigger;
+        bool                                                    updateMeasuringRangeTrigger;
         
-        ReadOnlyGattCharacteristic<SensorData>                  sensorDataCharacteristic;
-        ReadWriteGattCharacteristic<MeasuringRange>             measuringRangeCharacteristic;
-        WriteOnlyGattCharacteristic<uint32_t>                   masterClockCharacteristic;
+        uint32_t                                                timerOffset_us;
         
-        union
-        {
-            struct
-            {
-                MPU6050::SensorReading  reading;
-                uint32_t                clock_us;
-            }           input;
-            
-            SensorData  data;
-        } converter;
+        ReadOnlyGattCharacteristic<SensorData>                  sensorDataCharacteristic;        
+        ReadOnlyGattCharacteristic<MotionIndicator>             motionIndicatorCharaceristic;
+        WriteOnlyGattCharacteristic<MeasuringRange>             measuringRangeCharacteristic;
+        WriteOnlyGattCharacteristic<uint32_t>                   masterClockCharacteristic;  
         
         void dataSentCallback(unsigned count)
         {
@@ -170,34 +214,124 @@
         
         void dataWrittenCallback(const GattWriteCallbackParams *context)
         {
-            // some writable characteristics were updated by the client
+            // update master clock immediately
+            if (context->handle == masterClockCharacteristic.getValueAttribute().getHandle())
+            {
+                timer.stop();
+                timer.reset();
+                
+                timerOffset_us = *((uint32_t*)context->data);
+                
+                timer.start();
+            }
+            // delay measuring range update
+            else if (context->handle == measuringRangeCharacteristic.getValueAttribute().getHandle())
+            {
+                updateMeasuringRangeTrigger = true;
+            }
         }
         
-        void timeAccumulationPeriodicCallback()
+        void interruptRiseCallback()
         {
-            // we do this here to achieve a better resolution
-            time += timerResolution_us;
+            updateMotionIndicatorCharacteristicTrigger = true;
         }
         
         void updateSensorDataCharacteristic()
-        {                              
-            // flood the transmission buffer of the BLE stack until it's full
-            do
-            {   
-                if (mpu.getMotionAndTemperature(&(converter.input.reading)))
-                {
-                    if (!gattServer.write(sensorDataCharacteristic.getValueHandle(), reinterpret_cast<uint8_t*>(&(converter.data)), sizeof(SensorData)) == BLE_ERROR_NONE)
+        {
+            updateSensorDataCharacteristicTrigger = false;
+              
+            int         n       = 0;
+            SensorData  data;
+              
+            for (; n < MAX_SENSOR_POLLS_PER_UPDATE; n++)
+                if (mpu.getMotionAndTemperature
+                (
+                    (int16_t*)&(data.accelX), (int16_t*)&(data.accelY), (int16_t*)&(data.accelZ),
+                    (int16_t*)&(data.temperature),
+                    (int16_t*)&(data.gyroX), (int16_t*)&(data.gyroY), (int16_t*)&(data.gyroZ), 
+                    0.0
+                ))
+                {   
+                    data.clock_us = timer.read_us() + timerOffset_us;
+                    
+                    if (gattServer.write(sensorDataCharacteristic.getValueHandle(), (uint8_t*)&data, sizeof(SensorData)) != BLE_ERROR_NONE)
                         break;
-                }                 
-                // write data in characteristic and repeat
-            } while (true);
+                }
+                
+            if (n == 0)
+                updateSensorDataCharacteristicTrigger = true;
+        }
+        
+        void updateMotionIndicatorCharacteristic()
+        {
+            updateMotionIndicatorCharacteristicTrigger = false;
             
-            // error case or buffer is full (or break out of loop);
-            // this also means the last data update will be lost, if not sent again 
-            // (we are ignoring this fact and just keep on going, because we're NOT interested in the QUALITY of data, but in the QUANTITY
+            MPU6050::Interrupt  intSet  = static_cast<MPU6050::Interrupt>(0);
+            MPU6050::Motion     motSet  = static_cast<MPU6050::Motion>(0);
+            MotionIndicator mot;
+                       
+            mpu.getInterruptStatuses(&intSet);
+            mpu.getMotionStatus(&motSet); 
+            
+            mot = *((MotionIndicator*)&motSet);
+            
+            if ((intSet & MPU6050::INT_FREEFALL) != 0)
+                mot = mot | MOTION_IND_FREEFALL;
+            else
+                mot = mot & static_cast<MotionIndicator>(~0x02);
+                
+            gattServer.write(motionIndicatorCharaceristic.getValueHandle(), (uint8_t*)&mot, sizeof(MotionIndicator));
         }
         
-        bool deviceReset()
+        void updateMeasuringRange()
+        {   
+            static const uint16_t sizeOfMeasuringRange = sizeof(MeasuringRange);
+         
+            updateMeasuringRangeTrigger = false;
+            
+            union
+            {
+                MeasuringRange  range;
+                uint8_t         bytes[sizeOfMeasuringRange];
+            } conv;
+            
+            if (gattServer.read(measuringRangeCharacteristic.getValueHandle(), conv.bytes, (uint16_t*)&sizeOfMeasuringRange) == BLE_ERROR_NONE)
+            {            
+                switch (conv.range & ACCEL_RANGE_MASK)
+                {
+                    case ACCEL_RANGE_2G:
+                        mpu.setAccelRange(MPU6050::ACCEL_RANGE_2G);
+                        break;
+                    case ACCEL_RANGE_4G:
+                        mpu.setAccelRange(MPU6050::ACCEL_RANGE_4G);
+                        break;
+                    case ACCEL_RANGE_8G:
+                        mpu.setAccelRange(MPU6050::ACCEL_RANGE_8G);
+                        break;
+                    case ACCEL_RANGE_16G:
+                        mpu.setAccelRange(MPU6050::ACCEL_RANGE_16G);
+                        break;
+                }
+                
+                switch (conv.range & GYRO_RANGE_MASK)
+                {
+                    case GYRO_RANGE_250:
+                        mpu.setGyroRange(MPU6050::GYRO_RANGE_250);
+                        break;
+                    case GYRO_RANGE_500:
+                        mpu.setGyroRange(MPU6050::GYRO_RANGE_500);
+                        break;
+                    case GYRO_RANGE_1000:
+                        mpu.setGyroRange(MPU6050::GYRO_RANGE_1000);
+                        break;
+                    case GYRO_RANGE_2000:
+                        mpu.setGyroRange(MPU6050::GYRO_RANGE_2000);
+                        break;
+                }            
+            }
+        }
+        
+        inline bool deviceReset()
         {
             uint8_t id;
             
@@ -205,8 +339,9 @@
                 mpu.getDeviceId(&id) &&
                 (id == MPU6050::MPU6050_ID) &&
                 mpu.reset() &&
-                mpu.setSleepEnabled(false) &&
-                mpu.setFIFOEnabled(false);                
+                mpu.setClockSource(MPU6050::CLOCK_SRC_INTERNAL_8MHZ) &&                
+                mpu.setInterruptsEnabled(MPU6050::INT_FREEFALL | MPU6050::INT_MOTION | MPU6050::INT_ZERO_MOTION) &&
+                mpu.setSleepEnabled(false);
         }
 };