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