Abraham Marsen / Mbed 2 deprecated Jazz_Hands_Nordic

Dependencies:   mbed

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  *  @brief A library for easier setup and prototyping of IoT devices (pucks), by collecting everything that is common for all pucks in one place.
00044  *
00045  */ 
00046 class Puck {
00047     private:
00048         Puck() {}
00049         Puck(const Puck&);
00050         Puck& operator=(const Puck&);
00051         
00052         BLEDevice ble;        
00053         uint8_t beaconPayload[25];
00054         PuckState state;
00055         std::vector<GattService*> services;
00056         std::vector<GattCharacteristic*> characteristics;
00057         std::vector<CharacteristicWriteCallbacks*> writeCallbacks;
00058         std::vector<CharacteristicWriteCallback> pendingCallbackStack;
00059         std::vector<const uint8_t*> pendingCallbackParameterDataStack;
00060         std::vector<uint8_t> pendingCallbackParameterLengthStack;
00061         
00062         GattCharacteristic **previousCharacteristics;
00063         
00064     public:
00065         static Puck &getPuck();
00066         
00067         BLEDevice &getBle() { return ble; }
00068         PuckState getState() { return state; }
00069         void setState(PuckState state);        
00070         void init(uint16_t minor);
00071         void startAdvertising();
00072         void stopAdvertising();
00073         void disconnect();
00074         bool drive();
00075         int countFreeMemory();
00076         void onDataWritten(GattAttribute::Handle_t handle, const uint8_t* data, const uint8_t length);
00077         void addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties = 0xA);
00078 
00079         void onCharacteristicWrite(const UUID* uuid, CharacteristicWriteCallback callback);
00080         void updateCharacteristicValue(const UUID uuid, uint8_t* value, int length);
00081 
00082         uint8_t* getCharacteristicValue(const UUID uuid);
00083 };
00084 
00085 /**
00086  *  @brief Returns singleton instance of puck object.
00087  *
00088  *  @return singleton instance of puck object.
00089  */
00090 Puck &Puck::getPuck() {
00091     static Puck _puckSingletonInstance;
00092     return _puckSingletonInstance;
00093 }
00094 
00095 void onDisconnection(Gap::Handle_t handle, Gap::DisconnectionReason_t disconnectReason) {
00096     LOG_INFO("Disconnected.\n");
00097     Puck::getPuck().setState(DISCONNECTED);
00098 }
00099 
00100 void onConnection(Gap::Handle_t handle,
00101         Gap::addr_type_t peerAddrType,
00102         const Gap::address_t peerAddr,
00103         const Gap::ConnectionParams_t * connectionParams) {
00104     LOG_INFO("Connected.\n");
00105     Puck::getPuck().setState(CONNECTED);
00106 }
00107 
00108 void onDataWrittenCallback(const GattCharacteristicWriteCBParams *context) {
00109     Puck::getPuck().onDataWritten(context->charHandle, context->data, context->len);
00110 }
00111 
00112 bool isEqualUUID(const UUID* uuidA, const UUID uuidB) {
00113     const uint8_t* uuidABase = uuidA->getBaseUUID();
00114     const uint8_t* uuidBBase = uuidB.getBaseUUID();
00115     
00116     for(int i = 0; i < 16; i++) {
00117         if(uuidABase[i] != uuidBBase[i]) {
00118             return false;
00119         }
00120     }
00121     if(uuidA->getShortUUID() != uuidB.getShortUUID()) {
00122         return false;
00123     }
00124     return true;
00125 }
00126 
00127 /**
00128  * @brief Returns UUID representation of a 16-character string.
00129  *
00130  */
00131 const UUID stringToUUID(const char* str) {
00132     uint8_t array[16];
00133     for(int i = 0; i < 16; i++) {
00134         array[i] = str[i];    
00135     }
00136     return UUID(array);
00137 }
00138 
00139 void Puck::disconnect() {
00140     ble.disconnect(Gap::LOCAL_HOST_TERMINATED_CONNECTION);    
00141 }
00142 
00143 /**
00144  *  @brief Approximates malloc-able heap space. Do not use in production code, as it may crash.
00145  *
00146  */
00147 int Puck::countFreeMemory() {
00148     int blocksize = 256;
00149     int amount = 0;
00150     while (blocksize > 0) {
00151         amount += blocksize;
00152         LOG_VERBOSE("Trying to malloc %i bytes... ", amount);
00153         char *p = (char *) malloc(amount);
00154         if (p == NULL) {
00155             LOG_VERBOSE("FAIL!\n", amount);
00156             amount -= blocksize;
00157             blocksize /= 2;
00158         } else {
00159             free(p);
00160             LOG_VERBOSE("OK!\n", amount);
00161         }
00162     }
00163     LOG_DEBUG("Free memory: %i bytes.\n", amount);
00164     return amount;
00165 }
00166 
00167 void Puck::setState(PuckState state) {
00168     LOG_DEBUG("Changed state to %i\n", state);
00169     this->state = state;    
00170 }
00171 
00172 /**
00173  *  @brief  Call after finishing configuring puck (adding services, characteristics, callbacks).
00174             Starts advertising over bluetooth le. 
00175  *
00176  *  @parameter  minor
00177  *              Minor number to use for iBeacon identifier.
00178  *
00179  */
00180 void Puck::init(uint16_t minor) {
00181     /*
00182      * The Beacon payload (encapsulated within the MSD advertising data structure)
00183      * has the following composition:
00184      * 128-Bit UUID = E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61
00185      * Major/Minor  = 1337 / XXXX
00186      * Tx Power     = C8
00187      */
00188     uint8_t beaconPayloadTemplate[] = {
00189         0x00, 0x00, // Company identifier code (0x004C == Apple)
00190         0x02,       // ID
00191         0x15,       // length of the remaining payload
00192         0xE2, 0x0A, 0x39, 0xF4, 0x73, 0xF5, 0x4B, 0xC4, // UUID
00193         0xA1, 0x2F, 0x17, 0xD1, 0xAD, 0x07, 0xA9, 0x61,
00194         0x13, 0x37, // the major value to differenciate a location (Our app requires 1337 as major number)
00195         0x00, 0x00, // the minor value to differenciate a location (Change this to differentiate location pucks)
00196         0xC8        // 2's complement of the Tx power (-56dB)
00197     };
00198     beaconPayloadTemplate[22] = minor >> 8;
00199     beaconPayloadTemplate[23] = minor & 255;
00200     
00201     for (int i=0; i < 25; i++) {
00202         beaconPayload[i] = beaconPayloadTemplate[i];
00203     }
00204     
00205     ble.init();
00206     LOG_DEBUG("Inited BLEDevice.\n");
00207     setState(DISCONNECTED);
00208     
00209     char deviceName[10];
00210     sprintf(&deviceName[0], "Puck %04X", minor);
00211     deviceName[9] = '\0';
00212     ble.setDeviceName((const uint8_t*) deviceName);
00213     
00214     ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
00215     LOG_DEBUG("Accumulate advertising payload: BREDR_NOT_SUPPORTED | LE_GENERAL_DISCOVERABLE.\n");
00216     
00217     ble.accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, beaconPayload, sizeof(beaconPayload));
00218     LOG_DEBUG("Accumulate advertising payload: beacon data.\n");
00219     
00220     ble.setAdvertisingType (GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
00221     LOG_DEBUG("Setting advertising type: ADV_CONNECTABLE_UNDIRECTED.\n");
00222     
00223     int hundredMillisecondsInAdvertisingIntervalFormat = 160;
00224     ble.setAdvertisingInterval (hundredMillisecondsInAdvertisingIntervalFormat); 
00225     LOG_DEBUG("Set advertising interval: 160 (100 ms).\n");
00226     
00227     ble.onDisconnection(onDisconnection);
00228     ble.onConnection(onConnection);
00229     ble.onDataWritten(onDataWrittenCallback);
00230     LOG_DEBUG("Hooked up internal event handlers.\n");
00231     
00232     for(int i = 0; i < services.size(); i++) {
00233         ble.addService(*services[i]);
00234         LOG_DEBUG("Added service %x to BLEDevice\n", services[i]);
00235     }
00236     
00237     LOG_INFO("Inited puck as 0x%X.\n", minor);
00238 }
00239 
00240 void Puck::startAdvertising() {
00241     ble.startAdvertising();
00242     LOG_INFO("Starting to advertise.\n");
00243     setState(ADVERTISING);
00244 }
00245 
00246 void Puck::stopAdvertising() {
00247     if(state == ADVERTISING) {
00248         ble.stopAdvertising();
00249         LOG_INFO("Stopped advertising.\n");
00250         setState(DISCONNECTED);
00251     } else {
00252         LOG_WARN("Tried to stop advertising, but advertising is already stopped!\n");    
00253     }
00254 }
00255 
00256 /** 
00257  *  @brief  Extends the given gatt service with the given gatt characteristic.
00258  *          If the service doesn't exist, it is created.
00259  *
00260  *  @param  serviceUuid
00261             UUID of the gatt service to be extended.
00262  *
00263  *  @param  characteristicUuid
00264  *          UUID to use for this characteristic.
00265  *
00266  *  @param  bytes
00267  *          Length in bytes of this characteristic's value.
00268  *
00269  *  @param  properties
00270  *          8-bit bit field containing the characteristic's properties. See @ref ble_gatt_char_properties_t.
00271  *
00272  *  @return Void.
00273  */
00274 void Puck::addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties) {
00275     MBED_ASSERT(bytes <= 20);
00276     uint16_t size = sizeof(uint8_t) * bytes;
00277     uint8_t* value = (uint8_t*) malloc(size);
00278     if(value == NULL) {
00279         LOG_ERROR("Unable to malloc value for characteristic. Possibly out of memory!\n");    
00280     }
00281     
00282     GattCharacteristic* characteristic = new GattCharacteristic(characteristicUuid, value, size, size, properties);
00283     characteristics.push_back(characteristic);
00284     
00285     
00286     GattService* service = NULL;
00287     
00288     int removeIndex = -1;
00289     for(int i = 0; i < services.size(); i++) {
00290         if(isEqualUUID(&services[i]->getUUID(), serviceUuid)) {
00291             service = services[i];
00292             removeIndex = i;
00293             break;
00294         }
00295     }
00296     GattCharacteristic** characteristics = NULL;
00297     int characteristicsLength = 0;
00298     if(service != NULL) {
00299         characteristicsLength = service->getCharacteristicCount() + 1;
00300         characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength);
00301         if(characteristics == NULL) {
00302             LOG_ERROR("Unable to malloc array of characteristics for service creation. Possibly out of memory!\n");    
00303         }
00304         for(int i = 0; i < characteristicsLength; i++) {
00305             characteristics[i] = service->getCharacteristic(i);    
00306         }
00307         services.erase(services.begin() + removeIndex);
00308         delete service;
00309         free(previousCharacteristics);
00310     } else {
00311         characteristicsLength = 1;
00312         characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength);
00313         if(characteristics == NULL) {
00314             LOG_ERROR("Unable to malloc array of characteristics for service creation. Possibly out of memory!\n");    
00315         }
00316     }
00317     
00318     characteristics[characteristicsLength - 1] = characteristic;
00319     previousCharacteristics = characteristics;
00320     service = new GattService(serviceUuid, characteristics, characteristicsLength);
00321     services.push_back(service);
00322     LOG_DEBUG("Added characteristic.\n");
00323 }
00324 
00325 
00326 /** 
00327  *  @brief  Update the value of the given gatt characteristic.
00328  *
00329  *  @param  uuid
00330             UUID of the gatt characteristic to be updated.
00331  *
00332  *  @param  value
00333  *          New value of the characteristic.
00334  *
00335  *  @param  length
00336  *          Length in bytes of the characteristic's value.
00337  *
00338  *  @return Void.
00339  */
00340 void Puck::updateCharacteristicValue(const UUID uuid, uint8_t* value, int length) {
00341     GattCharacteristic* characteristic = NULL;
00342     for(int i = 0; i < characteristics.size(); i++) {
00343         GattAttribute &gattAttribute = characteristics[i]->getValueAttribute();
00344         if(isEqualUUID(&gattAttribute.getUUID(), uuid)) {
00345             characteristic = characteristics[i];
00346             break;
00347         }
00348     }
00349     if(characteristic != NULL) {
00350         ble.updateCharacteristicValue (characteristic->getValueHandle(), value, length);
00351         LOG_VERBOSE("Updated characteristic value.\n");
00352     } else {
00353         LOG_WARN("Tried to update an unkown characteristic!\n");    
00354     }
00355 }
00356 
00357 /** 
00358  *  @brief Pass control to the bluetooth stack, executing pending callbacks afterwards. Should be used inside a while condition loop.
00359  *
00360  *  Example:
00361  *  @code
00362  *  while (puck->drive()) {
00363  *      // Do stuff
00364  *  }
00365  *  @endcode
00366  *
00367  * @return true.
00368  *
00369  */
00370 bool Puck::drive() {
00371     if(state == DISCONNECTED) {
00372         startAdvertising();
00373     }
00374 
00375     ble.waitForEvent();
00376 
00377     while(pendingCallbackStack.size() > 0) {
00378         pendingCallbackStack.back()(pendingCallbackParameterDataStack.back(), pendingCallbackParameterLengthStack.back());
00379         pendingCallbackStack.pop_back();
00380         pendingCallbackParameterDataStack.pop_back();
00381         pendingCallbackParameterLengthStack.pop_back();
00382     }
00383     return true;
00384 }
00385 
00386 /** 
00387  *  @brief Register callback to be triggered on characteristic write.
00388  *
00389  *  @parameter  uuid
00390  *              UUID of the gatt characteristic to bind callback to.
00391  *
00392  *  @parameter  callback
00393  *              CharacteristicWriteCallback to be executed on characteristic write.It's signature should be void (*CharacteristicWriteCallback)(const uint8_t* value, uint8_t length); "value" is the value that was written, and "length" is the length of the value that was written.
00394  *
00395  *  @return Void.
00396  *
00397  */
00398 void Puck::onCharacteristicWrite(const UUID* uuid, CharacteristicWriteCallback callback) {
00399     CharacteristicWriteCallbacks* cb = NULL;
00400     for(int i = 0; i< writeCallbacks.size(); i++) {
00401         if(isEqualUUID(writeCallbacks[i]->uuid, *uuid)) {
00402             cb = writeCallbacks[i];    
00403             break;
00404         }
00405     }
00406     if(cb == NULL) {
00407         cb = (CharacteristicWriteCallbacks*) malloc(sizeof(CharacteristicWriteCallbacks));
00408         if(cb == NULL) {
00409             LOG_ERROR("Could not malloc CharacteristicWriteCallbacks container. Possibly out of memory!\n");    
00410         }
00411         cb->uuid = uuid;
00412         cb->callbacks = new std::vector<CharacteristicWriteCallback>();
00413         writeCallbacks.push_back(cb);
00414     }
00415     cb->callbacks->push_back(callback);
00416     LOG_VERBOSE("Bound characteristic write callback (uuid: %x, callback: %x)\n", uuid, callback);
00417 }
00418 
00419 /**
00420  *  @brief Returns current value of provided gatt characteristic.
00421  *
00422  */
00423 uint8_t* Puck::getCharacteristicValue(const UUID uuid) {
00424     LOG_VERBOSE("Reading characteristic value for UUID %x\n", uuid);
00425     for(int i = 0; i < characteristics.size(); i++) {
00426         GattAttribute &gattAttribute = characteristics[i]->getValueAttribute();
00427         if(isEqualUUID(&gattAttribute.getUUID(), uuid)) {
00428             return gattAttribute.getValuePtr();
00429         }
00430     }
00431     LOG_WARN("Tried to read an unknown characteristic!");
00432     return NULL;
00433 }
00434 
00435 /**
00436  * @brief For internal use only. Exposed to hack around mbed framework limitation.
00437  *
00438  */
00439 void Puck::onDataWritten(GattAttribute::Handle_t handle, const uint8_t* data, uint8_t length) {
00440     for (int i = 0; i < characteristics.size(); i++) {
00441         GattCharacteristic* characteristic = characteristics[i];
00442         
00443         if (characteristic->getValueHandle() == handle) {
00444             GattAttribute &gattAttribute = characteristic->getValueAttribute();
00445 
00446             for(int j = 0; j < writeCallbacks.size(); j++) {    
00447                 CharacteristicWriteCallbacks* characteristicWriteCallbacks = writeCallbacks[j];
00448 
00449                 if(isEqualUUID(characteristicWriteCallbacks->uuid, gattAttribute.getUUID())) {
00450                     for(int k = 0; k < characteristicWriteCallbacks->callbacks->size(); k++) {
00451                         pendingCallbackStack.push_back(characteristicWriteCallbacks->callbacks->at(k));
00452                         
00453                         pendingCallbackParameterDataStack.push_back(data);
00454                         pendingCallbackParameterLengthStack.push_back(length);
00455                     }
00456                     return;
00457                 }
00458             }
00459         }
00460     }
00461 }
00462 
00463 
00464  #endif // __PUCK_HPP__