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

Fork of Puck by Nordic Pucks

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Puck.h Source File

Puck.h

00001 /**
00002  * Copyright 2014 Nordic Semiconductor
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *  http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License
00015  */
00016 
00017 
00018 #ifndef __PUCK_HPP__
00019 #define __PUCK_HPP__
00020  
00021 #include "BLEDevice.h"
00022 #include "mbed.h"
00023 #include "Log.h"
00024 #include <vector>
00025 
00026 enum PuckState {
00027     CONNECTING,
00028     CONNECTED,
00029     ADVERTISING,
00030     DISCONNECTED
00031 };
00032 
00033 const UUID stringToUUID(const char* str);
00034 
00035 typedef void (*CharacteristicWriteCallback)(uint8_t* value);
00036  
00037  typedef struct {
00038      const UUID* uuid;
00039      std::vector<CharacteristicWriteCallback>* callbacks;
00040 } CharacteristicWriteCallbacks;
00041   
00042  
00043 class Puck {
00044     private:
00045         Puck() {}
00046         Puck(const Puck&);
00047         Puck& operator=(const Puck&);
00048         
00049         BLEDevice ble;        
00050         uint8_t beaconPayload[25];
00051         PuckState state;
00052         std::vector<GattService*> services;
00053         std::vector<GattCharacteristic*> characteristics;
00054         std::vector<CharacteristicWriteCallbacks*> writeCallbacks;
00055         std::vector<CharacteristicWriteCallback> pendingCallbackStack;
00056         std::vector<uint8_t*> pendingCallbackParameterStack;
00057         
00058         GattCharacteristic **previousCharacteristics;
00059         
00060     public:
00061         static Puck &getPuck();
00062         
00063         BLEDevice &getBle() { return ble; }
00064         PuckState getState() { return state; }
00065         void setState(PuckState state);        
00066         void init(uint16_t minor);
00067         void startAdvertising();
00068         void stopAdvertising();
00069         void disconnect();
00070         bool drive();
00071         int countFreeMemory();
00072         void onDataWritten(uint16_t handle);
00073         void addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties = 0xA);
00074 
00075         void onCharacteristicWrite(const UUID* uuid, CharacteristicWriteCallback callback);
00076         void updateCharacteristicValue(const UUID uuid, uint8_t* value, int length);
00077 
00078         uint8_t* getCharacteristicValue(const UUID uuid);
00079 };
00080 
00081 Puck &Puck::getPuck() {
00082     static Puck _puckSingletonInstance;
00083     return _puckSingletonInstance;
00084 }
00085 
00086 void onDisconnection(Gap::Handle_t handle, Gap::DisconnectionReason_t disconnectReason) {
00087     LOG_INFO("Disconnected.\n");
00088     Puck::getPuck().setState(DISCONNECTED);
00089 }
00090 
00091 void onConnection(Gap::Handle_t handle,
00092         Gap::addr_type_t peerAddrType,
00093         const Gap::address_t peerAddr,
00094         const Gap::ConnectionParams_t * connectionParams) {
00095     LOG_INFO("Connected.\n");
00096     Puck::getPuck().setState(CONNECTED);
00097 }
00098 
00099 void onDataWrittenCallback(const GattCharacteristicWriteCBParams *context) {
00100     Puck::getPuck().onDataWritten(context->charHandle);
00101 }
00102 
00103 bool isEqualUUID(const UUID* uuidA, const UUID uuidB) {
00104     const uint8_t* uuidABase = uuidA->getBaseUUID();
00105     const uint8_t* uuidBBase = uuidB.getBaseUUID();
00106     
00107     for(int i = 0; i < 16; i++) {
00108         if(uuidABase[i] != uuidBBase[i]) {
00109             return false;
00110         }
00111     }
00112     if(uuidA->getShortUUID() != uuidB.getShortUUID()) {
00113         return false;
00114     }
00115     return true;
00116 }
00117 
00118 const UUID stringToUUID(const char* str) {
00119     uint8_t array[16];
00120     for(int i = 0; i < 16; i++) {
00121         array[i] = str[i];    
00122     }
00123     return UUID(array);
00124 }
00125 
00126 void Puck::disconnect() {
00127     ble.disconnect(Gap::LOCAL_HOST_TERMINATED_CONNECTION);    
00128 }
00129 
00130 int Puck::countFreeMemory() {
00131     int blocksize = 256;
00132     int amount = 0;
00133     while (blocksize > 0) {
00134         amount += blocksize;
00135         LOG_VERBOSE("Trying to malloc %i bytes... ", amount);
00136         char *p = (char *) malloc(amount);
00137         if (p == NULL) {
00138             LOG_VERBOSE("FAIL!\n", amount);
00139             amount -= blocksize;
00140             blocksize /= 2;
00141         } else {
00142             free(p);
00143             LOG_VERBOSE("OK!\n", amount);
00144         }
00145     }
00146     LOG_DEBUG("Free memory: %i bytes.\n", amount);
00147     return amount;
00148 }
00149 
00150 void Puck::setState(PuckState state) {
00151     LOG_DEBUG("Changed state to %i\n", state);
00152     this->state = state;    
00153 }
00154 
00155 void Puck::init(uint16_t minor) {
00156         /*
00157      * The Beacon payload (encapsulated within the MSD advertising data structure)
00158      * has the following composition:
00159      * 128-Bit UUID = E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61
00160      * Major/Minor  = 1337 / XXXX
00161      * Tx Power     = C8
00162      */
00163     uint8_t beaconPayloadTemplate[] = {
00164         0x00, 0x00, // Company identifier code (0x004C == Apple)
00165         0x02,       // ID
00166         0x15,       // length of the remaining payload
00167         0xE2, 0x0A, 0x39, 0xF4, 0x73, 0xF5, 0x4B, 0xC4, // UUID
00168         0xA1, 0x2F, 0x17, 0xD1, 0xAD, 0x07, 0xA9, 0x61,
00169         0x13, 0x37, // the major value to differenciate a location (Our app requires 1337 as major number)
00170         0x00, 0x00, // the minor value to differenciate a location (Change this to differentiate location pucks)
00171         0xC8        // 2's complement of the Tx power (-56dB)
00172     };
00173     beaconPayloadTemplate[22] = minor >> 8;
00174     beaconPayloadTemplate[23] = minor & 255;
00175     
00176     for (int i=0; i < 25; i++) {
00177         beaconPayload[i] = beaconPayloadTemplate[i];
00178     }
00179     
00180     ble.init();
00181     LOG_DEBUG("Inited BLEDevice.\n");
00182     setState(DISCONNECTED);
00183 
00184     ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
00185     LOG_DEBUG("Accumulate advertising payload: BREDR_NOT_SUPPORTED.\n");
00186     
00187     ble.accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, beaconPayload, sizeof(beaconPayload));
00188     LOG_DEBUG("Accumulate advertising payload: beacon data.\n");
00189     
00190     ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
00191     LOG_DEBUG("Setting advertising type: ADV_CONNECTABLE_UNDIRECTED.\n");
00192     
00193     int hundredMillisecondsInAdvertisingIntervalFormat = 160;
00194     ble.setAdvertisingInterval(hundredMillisecondsInAdvertisingIntervalFormat); 
00195     LOG_DEBUG("Set advertising interval: 160 (100 ms).\n");
00196     
00197     ble.onDisconnection(onDisconnection);
00198     ble.onConnection(onConnection);
00199     ble.onDataWritten(onDataWrittenCallback);
00200     LOG_DEBUG("Hooked up internal event handlers.\n");
00201     
00202     for(int i = 0; i < services.size(); i++) {
00203         ble.addService(*services[i]);
00204         LOG_DEBUG("Added service %x to BLEDevice\n", services[i]);
00205     }
00206     
00207     LOG_INFO("Inited puck as 0x%X.\n", minor);
00208 }
00209 
00210 void Puck::startAdvertising() {
00211     ble.startAdvertising();
00212     LOG_INFO("Starting to advertise.\n");
00213     setState(ADVERTISING);
00214 }
00215 
00216 void Puck::stopAdvertising() {
00217     if(state == ADVERTISING) {
00218         ble.stopAdvertising();
00219         LOG_INFO("Stopped advertising.\n");
00220         setState(DISCONNECTED);
00221     } else {
00222         LOG_WARN("Tried to stop advertising, but advertising is already stopped!\n");    
00223     }
00224 }
00225 
00226 
00227 void Puck::addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties) {
00228     MBED_ASSERT(bytes <= 20);
00229     uint16_t size = sizeof(uint8_t) * bytes;
00230     uint8_t* value = (uint8_t*) malloc(size);
00231     if(value == NULL) {
00232         LOG_ERROR("Unable to malloc value for characteristic. Possibly out of memory!\n");    
00233     }
00234     
00235     GattCharacteristic* characteristic = new GattCharacteristic(characteristicUuid, value, size, size, properties);
00236     characteristics.push_back(characteristic);
00237     
00238     
00239     GattService* service = NULL;
00240     
00241     int removeIndex = -1;
00242     for(int i = 0; i < services.size(); i++) {
00243         if(isEqualUUID(&services[i]->getUUID(), serviceUuid)) {
00244             service = services[i];
00245             removeIndex = i;
00246             break;
00247         }
00248     }
00249     GattCharacteristic** characteristics = NULL;
00250     int characteristicsLength = 0;
00251     if(service != NULL) {
00252         characteristicsLength = service->getCharacteristicCount() + 1;
00253         characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength);
00254         if(characteristics == NULL) {
00255             LOG_ERROR("Unable to malloc array of characteristics for service creation. Possibly out of memory!\n");    
00256         }
00257         for(int i = 0; i < characteristicsLength; i++) {
00258             characteristics[i] = service->getCharacteristic(i);    
00259         }
00260         services.erase(services.begin() + removeIndex);
00261         delete service;
00262         free(previousCharacteristics);
00263     } else {
00264         characteristicsLength = 1;
00265         characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength);
00266         if(characteristics == NULL) {
00267             LOG_ERROR("Unable to malloc array of characteristics for service creation. Possibly out of memory!\n");    
00268         }
00269     }
00270     
00271     characteristics[characteristicsLength - 1] = characteristic;
00272     previousCharacteristics = characteristics;
00273     service = new GattService(serviceUuid, characteristics, characteristicsLength);
00274     services.push_back(service);
00275     LOG_DEBUG("Added characteristic.\n");
00276 }
00277 
00278 void Puck::updateCharacteristicValue(const UUID uuid, uint8_t* value, int length) {
00279     GattCharacteristic* characteristic = NULL;
00280     for(int i = 0; i < characteristics.size(); i++) {
00281         GattAttribute &gattAttribute = characteristics[i]->getValueAttribute();
00282         if(isEqualUUID(&gattAttribute.getUUID(), uuid)) {
00283             characteristic = characteristics[i];
00284             break;
00285         }
00286     }
00287     if(characteristic != NULL) {
00288         ble.updateCharacteristicValue(characteristic->getValueHandle(), value, length);
00289         LOG_VERBOSE("Updated characteristic value.\n");
00290     } else {
00291         LOG_WARN("Tried to update an unkown characteristic!\n");    
00292     }
00293 }
00294 
00295 bool Puck::drive() {
00296     if(state == DISCONNECTED) {
00297         startAdvertising();
00298     }
00299 
00300     ble.waitForEvent();
00301 
00302     while(pendingCallbackStack.size() > 0) {
00303         pendingCallbackStack.back()(pendingCallbackParameterStack.back());
00304         pendingCallbackStack.pop_back();
00305         pendingCallbackParameterStack.pop_back();
00306     }
00307     return true;
00308 }
00309 
00310 
00311 void Puck::onCharacteristicWrite(const UUID* uuid, CharacteristicWriteCallback callback) {
00312     CharacteristicWriteCallbacks* cb = NULL;
00313     for(int i = 0; i< writeCallbacks.size(); i++) {
00314         if(isEqualUUID(writeCallbacks[i]->uuid, *uuid)) {
00315             cb = writeCallbacks[i];    
00316             break;
00317         }
00318     }
00319     if(cb == NULL) {
00320         cb = (CharacteristicWriteCallbacks*) malloc(sizeof(CharacteristicWriteCallbacks));
00321         if(cb == NULL) {
00322             LOG_ERROR("Could not malloc CharacteristicWriteCallbacks container. Possibly out of memory!\n");    
00323         }
00324         cb->uuid = uuid;
00325         cb->callbacks = new std::vector<CharacteristicWriteCallback>();
00326         writeCallbacks.push_back(cb);
00327     }
00328     cb->callbacks->push_back(callback);
00329     LOG_VERBOSE("Bound characteristic write callback (uuid: %x, callback: %x)\n", uuid, callback);
00330 }
00331 
00332 
00333 uint8_t* Puck::getCharacteristicValue(const UUID uuid) {
00334     LOG_VERBOSE("Reading characteristic value for UUID %x\n", uuid);
00335     for(int i = 0; i < characteristics.size(); i++) {
00336         GattAttribute &gattAttribute = characteristics[i]->getValueAttribute();
00337         if(isEqualUUID(&gattAttribute.getUUID(), uuid)) {
00338             return gattAttribute.getValuePtr();
00339         }
00340     }
00341     LOG_WARN("Tried to read an unknown characteristic!");
00342     return NULL;
00343 }
00344 
00345 
00346 void Puck::onDataWritten(uint16_t handle) {
00347     for (int i = 0; i < characteristics.size(); i++) {
00348         GattCharacteristic* characteristic = characteristics[i];
00349         
00350         if (characteristic->getValueHandle() == handle) {
00351             GattAttribute &gattAttribute = characteristic->getValueAttribute();
00352 
00353             for(int j = 0; j < writeCallbacks.size(); j++) {    
00354                 CharacteristicWriteCallbacks* characteristicWriteCallbacks = writeCallbacks[j];
00355 
00356                 if(isEqualUUID(characteristicWriteCallbacks->uuid, gattAttribute.getUUID())) {
00357                     for(int k = 0; k < characteristicWriteCallbacks->callbacks->size(); k++) {
00358                         pendingCallbackStack.push_back(characteristicWriteCallbacks->callbacks->at(k));
00359                         pendingCallbackParameterStack.push_back(gattAttribute.getValuePtr());
00360                     }
00361                     return;
00362                 }
00363             }
00364         }
00365     }
00366 }
00367 
00368 
00369  #endif // __PUCK_HPP__