-

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