Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
