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

Dependencies:   BLE_API mbed nRF51822 MPU6050_lib

main.cpp

Committer:
fruediger
Date:
2015-09-22
Revision:
13:496c0f5e8a61
Parent:
12:dc46aa2edccd

File content as of revision 13:496c0f5e8a61:

/** Includes */

#include "common.h"
#include "mbed.h"
#include "BLE.h"
#include "MPU6050Service.h"
#include "PowerService.h"
#include "services/DeviceInformationService.h"

/** Constants */

static const char           deviceName[]                    = "nano bear";
static const char           deviceManufacturers[]           = "???";
static const char           deviceModelNumber[]             = "";
static const char           deviceSerialNumber[]            = XSTRING_(MBED_BUILD_TIMESTAMP);
static const char           deviceHardwareRev[]             = "0.1";
static const char           deviceFirmwareRev[]             = "0.1";
static const char           deviceSoftwareRev[]             = "";          

static const uint16_t       minimumConnectionInterval       = Gap::MSEC_TO_GAP_DURATION_UNITS(20); // 20ms
static const uint16_t       maximumConnectionInterval       = Gap::MSEC_TO_GAP_DURATION_UNITS(40); // 40ms;
static const uint16_t       slaveLatency                    = 0;

static const float          ledTimeout                      = 0.05f;
static const float          batteryCritBlinkSequ[]          = { 0.000f, 0.146f, 0.500f, 0.854f, 1.000f, 0.854f, 0.500f, 0.146f };
static const float          connectedBlinkSequ[]            = { 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f,
                                                                1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f,                                                            
                                                                1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f,                                                           
                                                                1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 
                                                                1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f,                                                           
                                                                1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, 1.000f,                                                           
                                                                1.000f, 0.962f, 0.854f, 0.691f, 0.500f, 0.309f, 0.146f, 0.038f,
                                                                0.000f, 0.038f, 0.146f, 0.309f, 0.500f, 0.691f, 0.854f, 0.962f };
static const float          disconnectedBlinkSequ[]         = { 0.000f, 0.038f, 0.146f, 0.309f, 0.500f, 0.691f, 0.854f, 0.962f,
                                                                1.000f, 0.962f, 0.854f, 0.691f, 0.500f, 0.309f, 0.146f, 0.038f,
                                                                0.000f, 0.038f, 0.146f, 0.309f, 0.500f, 0.691f, 0.854f, 0.962f,
                                                                1.000f, 0.962f, 0.854f, 0.691f, 0.500f, 0.309f, 0.146f, 0.038f,
                                                                0.000f, 0.038f, 0.146f, 0.309f, 0.500f, 0.691f, 0.854f, 0.962f,
                                                                1.000f, 0.962f, 0.854f, 0.691f, 0.500f, 0.309f, 0.146f, 0.038f,
                                                                0.000f, 0.000f, 0.000f, 0.000f, 0.000f, 0.000f, 0.000f, 0.000f,                                                            
                                                                0.000f, 0.000f, 0.000f, 0.000f, 0.000f, 0.000f, 0.000f, 0.000f,                                                            
                                                                0.000f, 0.000f, 0.000f, 0.000f, 0.000f, 0.000f, 0.000f, 0.000f };

/** Power determination stuff:
 *
 *  The following is the schematic circuit ...
 *
 *       I0->      ____
 *      --------*-|____|-*-----------                   ... where ...
 *      |       |   R3   |          |                       
 *      |       |        |     _____|_____                    |
 *      |      | |      | |   |           |                  | |        ____
 *      |   R1 | |   R4 | |   | BLE nano  |                  | |  and -|____|-  are resistors ...
 *      |      |_|      |_|   |           |--...             |_|
 *      |       |~0->    |    |           |                   |
 *      | Uref1 *--------|----| P0_4      |--...
 *  + __|__     |        |~0->|           |             ... and ...
 * U0  ___      |  Uref2 *----| P0_5      |--...
 *      |       |        |    |           |               + __|__  
 *      |      | |      | |   |           |--...             ___   is the constant voltage source
 *      |   R2 | |   R5 | |   |           |                   |    (a battery in this case)
 *      |      |_|      |_|   |___________|
 *      |       |        |          |
 *      |       |        |          |
 *      *-------*--------*----------*--------...
 *                                 _|_
 *
 *
 *  Therefore the input values are
 *
 *    U0 = (R1 / R2 + 1) * Uref1
 *
 *    I0 = ((R1 + R3) / R2 + 1) / R3 * Uref1 - (R4 / R5 + 1) / R3 * Uref2
 *                              
 */
 
#define R1              1000.0f
#define R2              1000.0f
#define R3              10.0f
#define R4              1000.0f
#define R5              1000.0f
#define NANO_OP_VOLT    3.3f
                                                                
static const float          powerAccumulationTimeout        = 0.005f;

static const float          voltFac                         = ((R1 / R2) + 1.0f) * NANO_OP_VOLT;
static const float          currFac1                        = ((((R1 + R3) / R2) + 1.0f) / R3) * NANO_OP_VOLT;
static const float          currFac2                        = (((R4 / R5) + 1.0f) / R3) * NANO_OP_VOLT;

static const float          powerServiceUpdateTimeout       = 0.1f;



/** Global variables */

BLE                         ble;
Gap::ConnectionParams_t     fast;

MPU6050                     mpu(I2C_SDA0, I2C_SCL0, MPU6050::ADDRESS_0);

PwmOut                      btLed(P0_28);
PwmOut                      pwrLed(P0_29);
DigitalOut                  aliveLed(LED);

AnalogIn                    uRef1(P0_4);
AnalogIn                    uRef2(P0_5);

InterruptIn                 intIn(P0_9);

Ticker                      powerAccumulationTicker;
Ticker                      powerServiceUpdateTicker;
Ticker                      ledTicker;



/** Callback and handler functions */

// Accumulate discharge

static float U0 = 0.0f;
static float I0 = 0.0f;
static float Q  = 0.0f;

void powerAccumulationPeriodicCallback()
{
    // we have to do it in here, since we want a good approximation of the dt-timestep
    // since these are simple calculations this won't take much time
    
    register float u1 = uRef1.read();
    
    U0 = u1 * voltFac;
    I0 = (u1 * currFac1) - (uRef2.read() * currFac2);
    Q += I0 * powerAccumulationTimeout;
}



// Update the power service characteristics

static bool powerServiceUpdateTrigger = false;

void powerServiceUpdateHandler(PowerService* powerService)
{    
    powerServiceUpdateTrigger = false;
    
    powerService->updateVoltage(U0);
    powerService->updateCurrent(I0 * 1000);             // mA
    powerService->updateDischarge(Q * 0.277777777f);    // mAh
}

void powerServiceUpdatePeriodicCallback()
{    
    powerServiceUpdateTrigger = true;
}


// Update the bluetooth connection state LED

static bool ledTrigger = false;
static bool batteryCritical = false;

void ledHandler(Gap &gap)
{   
    ledTrigger = false;
    
    static uint32_t     cnt                         = 0;
    static const int    batteryCritBlinkSequSize    = sizeof(batteryCritBlinkSequ) / sizeof(float);    
    static const int    connectedBlinkSequSize      = sizeof(connectedBlinkSequ) / sizeof(float);
    static const int    disconnectedBlinkSequSize   = sizeof(disconnectedBlinkSequ) / sizeof(float);  
    
    aliveLed = !aliveLed;
    
    if (gap.getState().connected)
        btLed = connectedBlinkSequ[cnt % connectedBlinkSequSize];        
    else
        btLed = disconnectedBlinkSequ[cnt % disconnectedBlinkSequSize];
    
    if (batteryCritical)
        pwrLed = batteryCritBlinkSequ[cnt % batteryCritBlinkSequSize];
    else
        pwrLed = 1;
        
    cnt++;
}

void ledPeriodicCallback()
{
    ledTrigger = true;
}



// Connection timeout

static bool timeoutTrigger = false;

void timeoutHandler(Gap &gap, MPU6050Service *mpu)
{    
    timeoutTrigger = false;
    
    mpu->stop();    
    gap.startAdvertising();
}

void timeoutCallback(Gap::TimeoutSource_t source)
{    
    timeoutTrigger = true;
}



// Disconnection 

static bool disconnectionTrigger = false;

void disconnectionHandler(Gap &gap, MPU6050Service *mpu)
{    
    disconnectionTrigger = false;
    
    mpu->stop();
    gap.startAdvertising();
}

void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
{
    timeoutTrigger = true;
}


// Connection

static bool connectionTrigger = false;

void connectionHandler(MPU6050Service *mpu6050)
{    
    connectionTrigger = false;
    
    mpu6050->start();
}

void connectionCallback(const Gap::ConnectionCallbackParams_t *params)
{        
    // update the connection parameters with the tuned fast ones
    ble.updateConnectionParams(params->handle, &fast);
    
    connectionTrigger = true;
}



/** Main */

int main()
{   
    btLed       = 1.0f;
    pwrLed      = 1.0f;
    aliveLed    = 0;

    ble.init();
    
    Gap &gap = ble.gap(); 
        
    powerAccumulationTicker.attach(powerAccumulationPeriodicCallback, powerAccumulationTimeout);
    powerServiceUpdateTicker.attach(powerServiceUpdatePeriodicCallback, powerServiceUpdateTimeout);
    ledTicker.attach(ledPeriodicCallback, ledTimeout);
    
    gap.onTimeout(timeoutCallback);   
    gap.onDisconnection(disconnectionCallback);
    gap.onConnection(connectionCallback);
    
    gap.setDeviceName((const uint8_t*)deviceName);
    
    gap.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    gap.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (const uint8_t*)deviceName, sizeof(deviceName));
    gap.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    
    //TODO: DECISION: advertising at minimal interval may consumes to much power, but is way faster
    gap.setAdvertisingInterval(gap.getMinAdvertisingInterval());
    
    // tune the preferred connection parameters to enable transfer more often
    // TODO: DECISION: also waht about power consumption?
    gap.getPreferredConnectionParams(&fast);
    fast.minConnectionInterval = minimumConnectionInterval;
    fast.maxConnectionInterval = maximumConnectionInterval;
    fast.slaveLatency = slaveLatency;
    gap.setPreferredConnectionParams(&fast);
      
    DeviceInformationService    deviceInformation(ble, deviceManufacturers, deviceModelNumber, deviceSerialNumber, deviceHardwareRev, deviceFirmwareRev, deviceSoftwareRev);    
    PowerService                powerService(ble);
    MPU6050Service              mpu6050(ble, mpu, &intIn);    
    
    gap.startAdvertising();
    
    while (true)
    {        
        // Handle all stuff within the user context
            
        if (powerServiceUpdateTrigger)
            powerServiceUpdateHandler(&powerService);
            
        if (ledTrigger)
            ledHandler(gap);
            
        if (connectionTrigger)
            connectionHandler(&mpu6050);
            
        if (disconnectionTrigger)
            disconnectionHandler(gap, &mpu6050);
            
        if (timeoutTrigger)
            timeoutHandler(gap, &mpu6050);
        
        ble.waitForEvent();
        
        mpu6050.handleService();
    }
}