Update for latest nRF51822 code changes.

Dependencies:   BLE_API nRF51822

Fork of Puck by Nordic Pucks

Revision:
18:4c95a470d778
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Puck.h.orig	Sun Mar 01 18:32:49 2015 +0000
@@ -0,0 +1,430 @@
+#ifndef __PUCK_HPP__
+#define __PUCK_HPP__
+ 
+#include "BLEDevice.h"
+#include <vector>
+#include "Log.h"
+
+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;
+  
+/**
+ *  @brief A library for easier setup and prototyping of IoT devices (pucks), by collecting everything that is common for all pucks in one place.
+ *
+ */ 
+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;
+        
+        GattCharacteristic **previousCharacteristics;
+        
+    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();
+        void disconnect();
+        bool drive();
+        int countFreeMemory();
+        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);
+        void updateCharacteristicValue(const UUID uuid, uint8_t* value, int length);
+
+        uint8_t* getCharacteristicValue(const UUID uuid);
+};
+
+/**
+ *  @brief Returns singleton instance of puck object.
+ *
+ *  @return singleton instance of puck object.
+ */
+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();
+    
+    for(int i = 0; i < 16; i++) {
+        if(uuidABase[i] != uuidBBase[i]) {
+            return false;
+        }
+    }
+    if(uuidA->getShortUUID() != uuidB.getShortUUID()) {
+        return false;
+    }
+    return true;
+}
+
+/**
+ * @brief Returns UUID representation of a 16-character string.
+ *
+ */
+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::disconnect() {
+    ble.disconnect();    
+}
+
+/**
+ *  @brief Approximates malloc-able heap space. Do not use in production code, as it may crash.
+ *
+ */
+int Puck::countFreeMemory() {
+    int blocksize = 256;
+    int amount = 0;
+    while (blocksize > 0) {
+        amount += blocksize;
+        LOG_VERBOSE("Trying to malloc %i bytes... ", amount);
+        char *p = (char *) malloc(amount);
+        if (p == NULL) {
+            LOG_VERBOSE("FAIL!\n", amount);
+            amount -= blocksize;
+            blocksize /= 2;
+        } else {
+            free(p);
+            LOG_VERBOSE("OK!\n", amount);
+        }
+    }
+    LOG_DEBUG("Free memory: %i bytes.\n", amount);
+    return amount;
+}
+
+void Puck::setState(PuckState state) {
+    LOG_DEBUG("Changed state to %i\n", state);
+    this->state = state;    
+}
+
+/**
+ *  @brief  Call after finishing configuring puck (adding services, characteristics, callbacks).
+            Starts advertising over bluetooth le. 
+ *
+ *  @parameter  minor
+ *              Minor number to use for iBeacon identifier.
+ *
+ */
+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_DEBUG("Inited BLEDevice.\n");
+    setState(DISCONNECTED);
+
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
+    LOG_DEBUG("Accumulate advertising payload: BREDR_NOT_SUPPORTED.\n");
+    
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, beaconPayload, sizeof(beaconPayload));
+    LOG_DEBUG("Accumulate advertising payload: beacon data.\n");
+    
+    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+    LOG_DEBUG("Setting advertising type: ADV_CONNECTABLE_UNDIRECTED.\n");
+    
+    int hundredMillisecondsInAdvertisingIntervalFormat = 160;
+    ble.setAdvertisingInterval(hundredMillisecondsInAdvertisingIntervalFormat); 
+    LOG_DEBUG("Set advertising interval: 160 (100 ms).\n");
+    
+    ble.onDisconnection(onDisconnection);
+    ble.onConnection(onConnection);
+    ble.onDataWritten(onDataWrittenCallback);
+    LOG_DEBUG("Hooked up internal event handlers.\n");
+    
+    for(int i = 0; i < services.size(); i++) {
+        ble.addService(*services[i]);
+        LOG_DEBUG("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");    
+    }
+}
+
+/** 
+ *  @brief  Extends the given gatt service with the given gatt characteristic.
+ *          If the service doesn't exist, it is created.
+ *
+ *  @param  serviceUuid
+            UUID of the gatt service to be extended.
+ *
+ *  @param  characteristicUuid
+ *          UUID to use for this characteristic.
+ *
+ *  @param  bytes
+ *          Length in bytes of this characteristic's value.
+ *
+ *  @param  properties
+ *          8-bit bit field containing the characteristic's properties. See @ref ble_gatt_char_properties_t.
+ *
+ *  @return Void.
+ */
+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);
+    if(value == NULL) {
+        LOG_ERROR("Unable to malloc value for characteristic. Possibly out of memory!\n");    
+    }
+    
+    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);
+        if(characteristics == NULL) {
+            LOG_ERROR("Unable to malloc array of characteristics for service creation. Possibly out of memory!\n");    
+        }
+        for(int i = 0; i < characteristicsLength; i++) {
+            characteristics[i] = service->getCharacteristic(i);    
+        }
+        services.erase(services.begin() + removeIndex);
+        delete service;
+        free(previousCharacteristics);
+    } else {
+        characteristicsLength = 1;
+        characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength);
+        if(characteristics == NULL) {
+            LOG_ERROR("Unable to malloc array of characteristics for service creation. Possibly out of memory!\n");    
+        }
+    }
+    
+    characteristics[characteristicsLength - 1] = characteristic;
+    previousCharacteristics = characteristics;
+    service = new GattService(serviceUuid, characteristics, characteristicsLength);
+    services.push_back(service);
+    LOG_DEBUG("Added characteristic.\n");
+}
+
+/** 
+ *  @brief  Update the value of the given gatt characteristic.
+ *
+ *  @param  uuid
+            UUID of the gatt characteristic to be updated.
+ *
+ *  @param  value
+ *          New value of the characteristic.
+ *
+ *  @param  length
+ *          Length in bytes of the characteristic's value.
+ *
+ *  @return Void.
+ */
+void Puck::updateCharacteristicValue(const UUID uuid, uint8_t* value, int length) {
+    GattCharacteristic* characteristic = NULL;
+    for( int i = 0; i < characteristics.size(); i++) {
+        if(isEqualUUID(&characteristics[i]->getUUID(), uuid)) {
+            characteristic = characteristics[i];
+            break;    
+        }    
+    }
+    if(characteristic != NULL) {
+        ble.updateCharacteristicValue(characteristic->getHandle(), value, length);
+        LOG_VERBOSE("Updated characteristic value.\n");
+    } else {
+        LOG_WARN("Tried to update an unkown characteristic!\n");    
+    }
+}
+
+/** 
+ *  @brief Pass control to the bluetooth stack, executing pending callbacks afterwards. Should be used inside a while condition loop.
+ *
+ *  Example:
+ *  @code
+ *  while (puck->drive()) {
+ *      // Do stuff
+ *  }
+ *  @endcode
+ *
+ * @return true.
+ *
+ */
+bool Puck::drive() {
+    ble.waitForEvent();
+    if(state == DISCONNECTED) {
+        startAdvertising();    
+    }
+    while(pendingCallbackStack.size() > 0) {
+        pendingCallbackStack.back()(pendingCallbackParameterStack.back());
+        pendingCallbackStack.pop_back();
+        pendingCallbackParameterStack.pop_back();
+    }
+    return true;
+}
+
+/** 
+ *  @brief Register callback to be triggered on characteristic write.
+ *
+ *  @parameter  uuid
+ *              UUID of the gatt characteristic to bind callback to.
+ *
+ *  @parameter  callback
+ *              CharacteristicWriteCallback to be executed on characteristic write.
+ *
+ *  @return Void.
+ *
+ */
+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));
+        if(cb == NULL) {
+            LOG_ERROR("Could not malloc CharacteristicWriteCallbacks container. Possibly out of memory!\n");    
+        }
+        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);
+}
+
+/**
+ *  @brief Returns current value of provided gatt characteristic.
+ *
+ */
+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;
+}
+
+/**
+ * @brief For internal use only. Exposed to hack around mbed framework limitation.
+ *
+ */
+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