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