A library for easier setup and prototyping of IoT devices (pucks), by collecting everything that is common for all pucks in one place.

Dependencies:   BLE_API nRF51822

Dependents:   ir-puck display-puck ir-puck2 BLE_ScoringDevice ... more

/media/uploads/stiaje/header.jpg

Introduction

Raspberry Pi took the maker community by storm when it launched in 2012. With its internet access it allowed small projects to be internet-of-things enabled. We have created a platform to take this one step further.

Our platform, called the Puck platform, is an internet of things platform for mbed. mbed makes it easy to program embedded hardware for people new to embedded systems. Our platform is built upon the first mbed chip with Bluetooth, the nRF51822 created by Nordic Semiconductor. We hope to create a community around these BLE devices where people contribute to the project, and share their designs with each other. Everything is open-source, of course, with lots of supporting materials.

We make it easy to rapidly prototype and develop Bluetooth LE enabled devices - get up and running in under 10 lines of code.

Tutorials and in-depth documentation is available at the project's GitHub page

Pucks

We've developed a handful of awesome examples to demonstrate the platform. These examples are named 'Pucks'. By talking to the internet through your smartphone, the barrier to creating your own Internet of Things device is lower than ever.

Files at this revision

API Documentation at this revision

Comitter:
stiaje
Date:
Fri Aug 01 07:45:38 2014 +0000
Parent:
8:49ffd38fb401
Parent:
9:ba0527c6b6d0
Child:
11:e620c41de9c3
Commit message:
Merge 'Update display puck to use new lib'

Changed in this revision

Puck.h Show annotated file Show diff for this revision Revisions of this file
Puck.h.orig Show annotated file Show diff for this revision Revisions of this file
--- a/Puck.h	Thu Jul 24 14:27:55 2014 +0000
+++ b/Puck.h	Fri Aug 01 07:45:38 2014 +0000
@@ -5,7 +5,7 @@
 #include <vector>
 #include "Log.h"
 
- enum PuckState {
+enum PuckState {
     CONNECTING,
     CONNECTED,
     ADVERTISING,
@@ -48,11 +48,15 @@
         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 onCharacteristicWrite(const UUID* uuid, CharacteristicWriteCallback callback);
         void updateCharacteristicValue(const UUID uuid, uint8_t* value, int length);
+
         uint8_t* getCharacteristicValue(const UUID uuid);
 };
 
@@ -79,14 +83,15 @@
 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;
         }
     }
+    if(uuidA->getShortUUID() != uuidB.getShortUUID()) {
+        return false;
+    }
     return true;
 }
 
@@ -98,6 +103,30 @@
     return UUID(array);
 }
 
+void Puck::disconnect() {
+    ble.disconnect();    
+}
+
+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;    
@@ -129,29 +158,30 @@
     }
     
     ble.init();
-    LOG_VERBOSE("Inited BLEDevice.\n");
+    LOG_DEBUG("Inited BLEDevice.\n");
     setState(DISCONNECTED);
 
     ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
-    LOG_VERBOSE("Accumulate advertising payload: BREDR_NOT_SUPPORTED.\n");
+    LOG_DEBUG("Accumulate advertising payload: BREDR_NOT_SUPPORTED.\n");
     
     ble.accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, beaconPayload, sizeof(beaconPayload));
-    LOG_VERBOSE("Accumulate advertising payload: beacon data.\n");
+    LOG_DEBUG("Accumulate advertising payload: beacon data.\n");
     
     ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
-    LOG_VERBOSE("Setting advertising type: ADV_CONNECTABLE_UNDIRECTED.\n");
+    LOG_DEBUG("Setting advertising type: ADV_CONNECTABLE_UNDIRECTED.\n");
     
     int hundredMillisecondsInAdvertisingIntervalFormat = 160;
     ble.setAdvertisingInterval(hundredMillisecondsInAdvertisingIntervalFormat); 
-    LOG_VERBOSE("Set advertising interval: 160 (100 ms).\n");
+    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_VERBOSE("Added service %x to BLEDevice\n", services[i]);
+        LOG_DEBUG("Added service %x to BLEDevice\n", services[i]);
     }
     
     LOG_INFO("Inited puck as 0x%X.\n", minor);
@@ -176,12 +206,18 @@
 
 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)) {
@@ -195,6 +231,9 @@
     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);    
         }
@@ -204,11 +243,16 @@
     } 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");
 }
 
 
@@ -234,27 +278,28 @@
         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) {
+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)) {
+        if(isEqualUUID(writeCallbacks[i]->uuid, *uuid)) {
             cb = writeCallbacks[i];    
             break;
         }
     }
     if(cb == NULL) {
         cb = (CharacteristicWriteCallbacks*) malloc(sizeof(CharacteristicWriteCallbacks));
-        cb->uuid = &uuid;
+        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);
     }
@@ -289,8 +334,8 @@
                         pendingCallbackStack.push_back(characteristicWriteCallbacks->callbacks->at(k));
                         pendingCallbackParameterStack.push_back(characteristic->getValuePtr());
                     }
+                    return;
                 }
-                return;
             }
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Puck.h.orig	Fri Aug 01 07:45:38 2014 +0000
@@ -0,0 +1,301 @@
+#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;
+  
+ 
+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();
+        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);
+        void updateCharacteristicValue(const UUID uuid, uint8_t* value, int length);
+        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;
+        free(previousCharacteristics);
+    } else {
+        characteristicsLength = 1;
+        characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength);
+    }
+    characteristics[characteristicsLength - 1] = characteristic;
+    previousCharacteristics = characteristics;
+    service = new GattService(serviceUuid, characteristics, characteristicsLength);
+    services.push_back(service);
+}
+
+
+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");    
+    }
+}
+
+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