.
Dependencies: BLE_API nRF51822
Puck.h
- Committer:
- sigveseb
- Date:
- 2014-07-23
- Revision:
- 3:5432b38585ea
- Child:
- 4:2f2a2ac6b231
File content as of revision 3:5432b38585ea:
#ifndef __PUCK_HPP__ #define __PUCK_HPP__ #include "BLEDevice.h" #include <vector> #ifdef LOG_LEVEL_VERBOSE #define __PUCK_LOG_LEVEL_VERBOSE__ #endif #ifdef LOG_LEVEL_DEBUG #define __PUCK_LOG_LEVEL_DEBUG__ #endif #ifdef LOG_LEVEL_INFO #define __PUCK_LOG_LEVEL_INFO__ #endif #ifdef LOG_LEVEL_WARN #define __PUCK_LOG_LEVEL_WARN__ #endif #ifdef LOG_LEVEL_ERROR #define __PUCK_LOG_LEVEL_ERROR__ #endif #ifdef __PUCK_LOG_LEVEL_VERBOSE__ #define LOG_VERBOSE(fmt, ...) do { logger.printf("[V] "); logger.printf(fmt, ##__VA_ARGS__); } while(0) #define __PUCK_LOG_LEVEL_DEBUG__ #else #define LOG_VERBOSE(fmt, ...) #endif #ifdef __PUCK_LOG_LEVEL_DEBUG__ #define LOG_DEBUG(fmt, ...) do { logger.printf("[D] "); logger.printf(fmt, ##__VA_ARGS__); } while(0) #define __PUCK_LOG_LEVEL_INFO__ #else #define LOG_DEBUG(fmt, ...) #endif #ifdef __PUCK_LOG_LEVEL_INFO__ #define LOG_INFO(fmt, ...) do { logger.printf("[I] "); logger.printf(fmt, ##__VA_ARGS__); } while(0) #define __PUCK_LOG_LEVEL_WARN__ #else #define LOG_INFO(fmt, ...) #endif #ifdef __PUCK_LOG_LEVEL_WARN__ #define LOG_WARN(fmt, ...) do { logger.printf("![W] "); logger.printf(fmt, ##__VA_ARGS__); } while(0) #define __PUCK_LOG_LEVEL_ERROR__ #else #define LOG_WARN(fmt, ...) #endif #ifdef __PUCK_LOG_LEVEL_ERROR__ #define LOG_ERROR(fmt, ...) do { logger.printf("!![E] "); logger.printf(fmt, ##__VA_ARGS__); } while(0) #else #define LOG_ERROR(fmt, ...) #endif #ifdef __PUCK_LOG_LEVEL_ERROR__ Serial logger(USBTX, USBRX); #endif enum PuckState { CONNECTING, CONNECTED, ADVERTISING, DISCONNECTED }; const UUID stringToUUID(const char* str); typedef void (*CharacteristicWriteCallback)(uint8_t* value); typedef struct { const UUID* uuid; std::vector<CharacteristicWriteCallback>* callbacks; } CharacteristicWriteCallbacks; class Puck { private: Puck() {} Puck(const Puck&); Puck& operator=(const Puck&); BLEDevice ble; uint8_t beaconPayload[25]; PuckState state; std::vector<GattService*> services; std::vector<GattCharacteristic*> characteristics; std::vector<CharacteristicWriteCallbacks*> writeCallbacks; std::vector<CharacteristicWriteCallback> pendingCallbackStack; std::vector<uint8_t*> pendingCallbackParameterStack; public: static Puck &getPuck(); BLEDevice getBle() { return ble; } PuckState getState() { return state; } void setState(PuckState state); void init(uint16_t minor); void startAdvertising(); void stopAdvertising(); bool drive(); void onDataWritten(uint16_t handle); void addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties = 0xA); void onCharacteristicWrite(const UUID uuid, CharacteristicWriteCallback callback); uint8_t* getCharacteristicValue(const UUID uuid); }; Puck &Puck::getPuck() { static Puck _puckSingletonInstance; return _puckSingletonInstance; } void onDisconnection(void) { LOG_INFO("Disconnected.\n"); Puck::getPuck().setState(DISCONNECTED); } void onConnection(void) { LOG_INFO("Connected.\n"); Puck::getPuck().setState(CONNECTED); } void onDataWrittenCallback(uint16_t handle) { Puck::getPuck().onDataWritten(handle); } bool isEqualUUID(const UUID* uuidA, const UUID uuidB) { const uint8_t* uuidABase = uuidA->getBaseUUID(); const uint8_t* uuidBBase = uuidB.getBaseUUID(); if(uuidA->getShortUUID() != uuidB.getShortUUID()) { return false; } for(int i = 0; i < 16; i++) { if(uuidABase[i] != uuidBBase[i]) { return false; } } return true; } const UUID stringToUUID(const char* str) { uint8_t array[16]; for(int i = 0; i < 16; i++) { array[i] = str[i]; } return UUID(array); } void Puck::setState(PuckState state) { LOG_DEBUG("Changed state to %i\n", state); this->state = state; } void Puck::init(uint16_t minor) { /* * The Beacon payload (encapsulated within the MSD advertising data structure) * has the following composition: * 128-Bit UUID = E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61 * Major/Minor = 1337 / XXXX * Tx Power = C8 */ uint8_t beaconPayloadTemplate[] = { 0x00, 0x00, // Company identifier code (0x004C == Apple) 0x02, // ID 0x15, // length of the remaining payload 0xE2, 0x0A, 0x39, 0xF4, 0x73, 0xF5, 0x4B, 0xC4, // UUID 0xA1, 0x2F, 0x17, 0xD1, 0xAD, 0x07, 0xA9, 0x61, 0x13, 0x37, // the major value to differenciate a location (Our app requires 1337 as major number) 0x00, 0x00, // the minor value to differenciate a location (Change this to differentiate location pucks) 0xC8 // 2's complement of the Tx power (-56dB) }; beaconPayloadTemplate[22] = minor >> 8; beaconPayloadTemplate[23] = minor & 255; for (int i=0; i < 25; i++) { beaconPayload[i] = beaconPayloadTemplate[i]; } ble.init(); LOG_VERBOSE("Inited BLEDevice.\n"); setState(DISCONNECTED); ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED); LOG_VERBOSE("Accumulate advertising payload: BREDR_NOT_SUPPORTED.\n"); ble.accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, beaconPayload, sizeof(beaconPayload)); LOG_VERBOSE("Accumulate advertising payload: beacon data.\n"); ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); LOG_VERBOSE("Setting advertising type: ADV_CONNECTABLE_UNDIRECTED.\n"); int hundredMillisecondsInAdvertisingIntervalFormat = 160; ble.setAdvertisingInterval(hundredMillisecondsInAdvertisingIntervalFormat); LOG_VERBOSE("Set advertising interval: 160 (100 ms).\n"); ble.onDisconnection(onDisconnection); ble.onConnection(onConnection); ble.onDataWritten(onDataWrittenCallback); for(int i = 0; i < services.size(); i++) { ble.addService(*services[i]); LOG_VERBOSE("Added service %x to BLEDevice\n", services[i]); } LOG_INFO("Inited puck as 0x%X.\n", minor); } void Puck::startAdvertising() { ble.startAdvertising(); LOG_INFO("Starting to advertise.\n"); setState(ADVERTISING); } void Puck::stopAdvertising() { if(state == ADVERTISING) { ble.stopAdvertising(); LOG_INFO("Stopped advertising.\n"); setState(DISCONNECTED); } else { LOG_WARN("Tried to stop advertising, but advertising is already stopped!\n"); } } void Puck::addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties) { MBED_ASSERT(bytes <= 20); uint16_t size = sizeof(uint8_t) * bytes; uint8_t* value = (uint8_t*) malloc(size); GattCharacteristic* characteristic = new GattCharacteristic(characteristicUuid, value, size, size, properties); characteristics.push_back(characteristic); GattService* service = NULL; int removeIndex = -1; for(int i = 0; i < services.size(); i++) { if(isEqualUUID(&services[i]->getUUID(), serviceUuid)) { service = services[i]; removeIndex = i; break; } } GattCharacteristic** characteristics = NULL; int characteristicsLength = 0; if(service != NULL) { characteristicsLength = service->getCharacteristicCount() + 1; characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength); for(int i = 0; i < characteristicsLength; i++) { characteristics[i] = service->getCharacteristic(i); } services.erase(services.begin() + removeIndex); delete service; } else { characteristicsLength = 1; characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength); } characteristics[characteristicsLength - 1] = characteristic; service = new GattService(serviceUuid, characteristics, characteristicsLength); services.push_back(service); } bool Puck::drive() { ble.waitForEvent(); if(state == DISCONNECTED) { startAdvertising(); } while(pendingCallbackStack.size() > 0) { LOG_VERBOSE("PendingCallbackStack size: %i\n", pendingCallbackStack.size()); pendingCallbackStack.back()(pendingCallbackParameterStack.back()); pendingCallbackStack.pop_back(); pendingCallbackParameterStack.pop_back(); LOG_VERBOSE("Callback fired\n"); } return true; } void Puck::onCharacteristicWrite(const UUID uuid, CharacteristicWriteCallback callback) { CharacteristicWriteCallbacks* cb = NULL; for(int i = 0; i< writeCallbacks.size(); i++) { if(isEqualUUID(writeCallbacks[i]->uuid, uuid)) { cb = writeCallbacks[i]; break; } } if(cb == NULL) { cb = (CharacteristicWriteCallbacks*) malloc(sizeof(CharacteristicWriteCallbacks)); cb->uuid = &uuid; cb->callbacks = new std::vector<CharacteristicWriteCallback>(); writeCallbacks.push_back(cb); } cb->callbacks->push_back(callback); LOG_VERBOSE("Bound characteristic write callback (uuid: %x, callback: %x)\n", uuid, callback); } uint8_t* Puck::getCharacteristicValue(const UUID uuid) { LOG_VERBOSE("Reading characteristic value for UUID %x\n", uuid); for(int i = 0; i < characteristics.size(); i++) { GattCharacteristic* characteristic = characteristics[i]; if(isEqualUUID(&characteristic->getUUID(), uuid)) { return characteristic->getValuePtr(); } } LOG_WARN("Tried to read an unknown characteristic!"); return NULL; } void Puck::onDataWritten(uint16_t handle) { for (int i = 0; i < characteristics.size(); i++) { GattCharacteristic* characteristic = characteristics[i]; if (characteristic->getHandle() == handle) { uint16_t maxLength = characteristic->getMaxLength(); ble.readCharacteristicValue(handle, characteristic->getValuePtr(), &maxLength); for(int j = 0; j < writeCallbacks.size(); j++) { CharacteristicWriteCallbacks* characteristicWriteCallbacks = writeCallbacks[j]; if(isEqualUUID(characteristicWriteCallbacks->uuid, characteristic->getUUID())) { for(int k = 0; k < characteristicWriteCallbacks->callbacks->size(); k++) { pendingCallbackStack.push_back(characteristicWriteCallbacks->callbacks->at(k)); pendingCallbackParameterStack.push_back(characteristic->getValuePtr()); } } return; } } } } #endif // __PUCK_HPP__