
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-09-22
- Revision:
- 13:496c0f5e8a61
- Parent:
- 12:dc46aa2edccd
File content as of revision 13:496c0f5e8a61:
#pragma once #ifndef __MPU6050_SERVICE_H__ #define __MPU6050_SERVICE_H__ #include "BLE.h" #include "MPU6050.h" class MPU6050Service { public: enum { UUID_MPU6050_SERVICE = 0xA123, UUID_MPU6050_SENSOR_DATA_CHAR = 0xA124, UUID_MPU6050_MOTION_INDICATOR_CHAR = 0xA125, UUID_MPU6050_MEASURING_RANGE_CHAR = 0xA126, UUID_MPU6050_MASTER_CLOCK_CHAR = 0xA127 }; enum MeasuringRange { ACCEL_RANGE_2G = 0x00, // 0b__00 ACCEL_RANGE_4G = 0x01, // 0b__01 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_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)); } enum MotionIndicator { 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, 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; uint32_t clock_us; } SensorData; typedef __packed struct { MotionIndicator indicator; uint32_t clock_us; } MotionIndication; MPU6050Service ( BLE &ble, MPU6050 &mpu6050, InterruptIn *interruptIn = NULL, const SensorData *initialData = NULL, const MeasuringRange measuringRange = ACCEL_RANGE_2G | GYRO_RANGE_250, const uint32_t masterClock_us = 0 ) : gattServer(ble.gattServer()), mpu(mpu6050), intIn(interruptIn), timer(), _isRunning(false), updateSensorDataCharacteristicTrigger(false), updateMotionIndicatorCharacteristicTrigger(false), updateMeasuringRangeTrigger(false), timerOffset_us(masterClock_us), sensorDataCharacteristic ( UUID_MPU6050_SENSOR_DATA_CHAR, (SensorData*)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) ), motionIndicatorCharaceristic ( UUID_MPU6050_MOTION_INDICATOR_CHAR, NULL, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY ), measuringRangeCharacteristic ( UUID_MPU6050_MEASURING_RANGE_CHAR, (MeasuringRange*)&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, &motionIndicatorCharaceristic, &measuringRangeCharacteristic, &masterClockCharacteristic }; GattService mpu6050Service(UUID_MPU6050_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic*)); gattServer.addService(mpu6050Service); gattServer.onDataSent(this, &MPU6050Service::dataSentCallback); gattServer.onDataWritten(this, &MPU6050Service::dataWrittenCallback); if (intIn != NULL) intIn->rise(this, &MPU6050Service::interruptRiseCallback); serviceAdded = true; } bool isRunning() { return _isRunning; } bool start() { if (!deviceReset()) { _isRunning = false; return false; } timer.stop(); timer.reset(); timer.start(); _isRunning = true; // 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 and so on) updateSensorDataCharacteristicTrigger = true; return true; } void stop() { _isRunning = false; } void handleService() { if (_isRunning) { if (updateMeasuringRangeTrigger) updateMeasuringRange(); if (updateSensorDataCharacteristicTrigger) updateSensorDataCharacteristic(); if (updateMotionIndicatorCharacteristicTrigger) updateMotionIndicatorCharacteristic(); } } private: static const int MAX_SENSOR_POLLS_PER_UPDATE = 10; GattServer &gattServer; MPU6050 &mpu; InterruptIn *intIn; Timer timer; volatile bool _isRunning; bool updateSensorDataCharacteristicTrigger; bool updateMotionIndicatorCharacteristicTrigger; bool updateMeasuringRangeTrigger; uint32_t timerOffset_us; ReadOnlyGattCharacteristic<SensorData> sensorDataCharacteristic; ReadOnlyGattCharacteristic<MotionIndicator> motionIndicatorCharaceristic; WriteOnlyGattCharacteristic<MeasuringRange> measuringRangeCharacteristic; WriteOnlyGattCharacteristic<uint32_t> masterClockCharacteristic; void dataSentCallback(unsigned count) { // we came here, if we just sent data, // so lets keep the BLE stack busy by sending data again updateSensorDataCharacteristicTrigger = true; } void dataWrittenCallback(const GattWriteCallbackParams *context) { // 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 interruptRiseCallback() { updateMotionIndicatorCharacteristicTrigger = true; } void updateSensorDataCharacteristic() { 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; } if (n == 0) updateSensorDataCharacteristicTrigger = true; } void updateMotionIndicatorCharacteristic() { updateMotionIndicatorCharacteristicTrigger = false; 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)); } 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; return mpu.getDeviceId(&id) && (id == MPU6050::MPU6050_ID) && mpu.reset() && mpu.setClockSource(MPU6050::CLOCK_SRC_INTERNAL_8MHZ) && mpu.setInterruptsEnabled(MPU6050::INT_FREEFALL | MPU6050::INT_MOTION | MPU6050::INT_ZERO_MOTION) && mpu.setSleepEnabled(false); } }; #endif