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

Dependencies:   BLE_API mbed nRF51822 MPU6050_lib

MPU6050Service.h

Committer:
fruediger
Date:
2015-07-13
Revision:
7:af3e2b9c137a
Parent:
6:c1db7e82d76a
Child:
9:6a28d9c0e486

File content as of revision 7:af3e2b9c137a:

#pragma once
#ifndef __MPU6050_SERVICE_H__
#define __MPU6050_SERVICE_H__

#include "BLE.h"

typedef struct
{
    uint16_t    a1, a2, a3;
    uint16_t    temp;
    uint16_t    g1, g2, g3;
} mpu6050SensorReading_t;

typedef struct
{
    mpu6050SensorReading_t  data;
    uint32_t                clock_us; // ~ 1h 11min 34s 967ms 296us
                                      // (so we have to resynchronize our clock approx. every hour)
} mpu6050SensorData_t;

typedef enum
{
    ACCEL_RANGE_2G  = 0x00, // 0b__00
    ACCEL_RANGE_4G  = 0x01, // 0b__01
    ACCEL_RANGE_8G  = 0x02, // 0b__10
    ACCEL_RANGE_16G = 0x03, // 0b__11
    
    GYRO_RANGE_250  = 0x00, // 0b00__
    GYRO_RANGE_500  = 0x04, // 0b01__
    GYRO_RANGE_1000 = 0x08, // 0b10__
    GYRO_RANGE_2000 = 0x0C  // 0b11__
} mpu6050MeasuringRange_t;

inline mpu6050MeasuringRange_t operator|(mpu6050MeasuringRange_t a, mpu6050MeasuringRange_t b) { return static_cast<mpu6050MeasuringRange_t>(static_cast<int>(a) | static_cast<int>(b)); }

class MPU6050Service
{
    public:        
        enum
        {
            UUID_MP6050_SERVICE                 = 0xA123,
            
            UUID_MPU6050_SENSOR_DATA_CHAR       = 0xA124,
            UUID_MPU6050_MEASURING_RANGE_CHAR   = 0xA125,
            UUID_MPU6050_MASTER_CLOCK_CHAR      = 0xA126
        };
        
        MPU6050Service
        (
            BLE                            &ble, 
            const mpu6050SensorData_t      *initialData         = NULL, 
            const mpu6050MeasuringRange_t   measuringRange      = ACCEL_RANGE_2G | GYRO_RANGE_250, 
            const uint32_t                  masterClock_us      = 0
        ) :
            gattServer(ble.gattServer()),
            sensorDataCharacteristic
            (
                UUID_MPU6050_SENSOR_DATA_CHAR,
                (mpu6050SensorData_t*)initialData,
                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)
            ),
            measuringRangeCharacteristic
            (
                UUID_MPU6050_MEASURING_RANGE_CHAR,
                (mpu6050MeasuringRange_t*)&measuringRange,
                GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE // also improving latency 
            ),
            masterClockCharacteristic
            (
                UUID_MPU6050_MASTER_CLOCK_CHAR,
                (uint32_t*)&masterClock_us,
                GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE // look above again
            )
        {
            static bool serviceAdded = false;
            if (serviceAdded)
                return;
                
            GattCharacteristic *charTable[] =
            {
                &sensorDataCharacteristic,
                &measuringRangeCharacteristic,
                &masterClockCharacteristic
            };
            
            GattService mpu6050Service(UUID_MP6050_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic*));
            
            gattServer.addService(mpu6050Service);
            gattServer.onDataSent(this, &MPU6050Service::dataSentCallback);
            gattServer.onDataWritten(this, &MPU6050Service::dataWrittenCallback);
            
            serviceAdded = true;
        }
        
        void start()
        {
            // sending data once will invoke the dataSent(...) callback later on
            // which will be looped by the event driven behavior of the BLE API (because it will send data again on it's own)
            updateSensorDataCharacteristic();
        }
        
    private:
        GattServer                                             &gattServer; 
        
        ReadOnlyGattCharacteristic<mpu6050SensorData_t>         sensorDataCharacteristic;
        WriteOnlyGattCharacteristic<mpu6050MeasuringRange_t>    measuringRangeCharacteristic;
        WriteOnlyGattCharacteristic<uint32_t>                   masterClockCharacteristic;
        
        union
        {
            struct
            {
                uint8_t accel_x_h,  accel_x_l;
                uint8_t accel_y_h,  accel_y_l;
                uint8_t accel_z_h,  accel_z_l;
                
                uint8_t temp_h,     temp_l;
                
                uint8_t gyro_x_h,   gyro_x_l;
                uint8_t gyro_y_h,   gyro_y_l;
                uint8_t gyro_z_h,   gyro_z_l;
            } registers;
            
            mpu6050SensorReading_t data;
        } converter;
        
        void dataSentCallback(unsigned count)
        {
            // we came here, if we just sent data, 
            // so lets keep the BLE stack busy by sending data again
            updateSensorDataCharacteristic();
        }
        
        void dataWrittenCallback(const GattWriteCallbackParams *context)
        {
            // some writable characteristics were updated by the client
        }
        
        void updateSensorDataCharacteristic()
        {         
            mpu6050SensorData_t data;                       
            // flood the transmission buffer of the BLE stack until it's full
            do
            {          
                // TODO: poll and accumulate data
                
                // ...
                
                data.data = converter.data;
                data.clock_us = 0;  
                // write data in characteristic and repeat
            } while (gattServer.write(sensorDataCharacteristic.getValueHandle(), reinterpret_cast<uint8_t*>(&data), sizeof(mpu6050SensorData_t)) == BLE_ERROR_NONE);
            
            // 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
        }
};

#endif