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