Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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__
Generated on Tue Jul 12 2022 17:25:21 by
1.7.2