Press buttons to activate the LED.
Dependencies: BLE_API nRF51822
Fork of Puck by
Diff: Puck.h
- Revision:
- 3:5432b38585ea
- Child:
- 4:91506772210d
- Child:
- 5:2f2a2ac6b231
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Puck.h Wed Jul 23 13:00:42 2014 +0000 @@ -0,0 +1,334 @@ +#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__ \ No newline at end of file