Example program for the Eddystone Beacon service.
Dependencies: BLE_API mbed nRF51822 X_NUCLEO_IDB0XA1
Fork of BLE_EddystoneBeacon by
EddystoneService.cpp
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2006-2015 ARM Limited 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 #include "EddystoneService.h" 00018 00019 /* Initialise the EddystoneService using parameters from persistent storage */ 00020 EddystoneService::EddystoneService(BLE &bleIn, 00021 EddystoneParams_t ¶msIn, 00022 const PowerLevels_t &advPowerLevelsIn, 00023 const PowerLevels_t &radioPowerLevelsIn, 00024 uint32_t advConfigIntervalIn) : 00025 ble(bleIn), 00026 operationMode(EDDYSTONE_MODE_NONE), 00027 urlFrame(paramsIn.urlData, paramsIn.urlDataLength), 00028 uidFrame(paramsIn.uidNamespaceID, paramsIn.uidInstanceID), 00029 tlmFrame(paramsIn.tlmVersion), 00030 resetFlag(false), 00031 tlmBatteryVoltageCallback(NULL), 00032 tlmBeaconTemperatureCallback(NULL) 00033 { 00034 lockState = paramsIn.lockState; 00035 flags = paramsIn.flags; 00036 txPowerMode = paramsIn.txPowerMode; 00037 beaconPeriod = correctAdvertisementPeriod(paramsIn.beaconPeriod); 00038 00039 memcpy(lock, paramsIn.lock, sizeof(Lock_t)); 00040 memcpy(unlock, paramsIn.unlock, sizeof(Lock_t)); 00041 00042 eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn); 00043 } 00044 00045 /* When using this constructor we need to call setURLData, 00046 * setTMLData and setUIDData to initialise values manually 00047 */ 00048 EddystoneService::EddystoneService(BLE &bleIn, 00049 const PowerLevels_t &advPowerLevelsIn, 00050 const PowerLevels_t &radioPowerLevelsIn, 00051 uint32_t advConfigIntervalIn) : 00052 ble(bleIn), 00053 operationMode(EDDYSTONE_MODE_NONE), 00054 urlFrame(), 00055 uidFrame(), 00056 tlmFrame(), 00057 lockState(false), 00058 resetFlag(false), 00059 lock(), 00060 unlock(), 00061 flags(0), 00062 txPowerMode(0), 00063 beaconPeriod(DEFAULT_BEACON_PERIOD_MSEC), 00064 tlmBatteryVoltageCallback(NULL), 00065 tlmBeaconTemperatureCallback(NULL) 00066 { 00067 eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn); 00068 } 00069 00070 /* Setup callback to update BatteryVoltage in TLM frame */ 00071 void EddystoneService::onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn) 00072 { 00073 tlmBatteryVoltageCallback = tlmBatteryVoltageCallbackIn; 00074 } 00075 00076 /* Setup callback to update BeaconTemperature in TLM frame */ 00077 void EddystoneService::onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn) 00078 { 00079 tlmBeaconTemperatureCallback = tlmBeaconTemperatureCallbackIn; 00080 } 00081 00082 void EddystoneService::setTLMData(uint8_t tlmVersionIn) 00083 { 00084 tlmFrame.setTLMData(tlmVersionIn); 00085 } 00086 00087 void EddystoneService::setURLData(const char *urlDataIn) 00088 { 00089 urlFrame.setURLData(urlDataIn); 00090 } 00091 00092 void EddystoneService::setUIDData(const UIDNamespaceID_t *uidNamespaceIDIn, const UIDInstanceID_t *uidInstanceIDIn) 00093 { 00094 uidFrame.setUIDData(uidNamespaceIDIn, uidInstanceIDIn); 00095 } 00096 00097 EddystoneService::EddystoneError_t EddystoneService::startConfigService(void) 00098 { 00099 if (operationMode == EDDYSTONE_MODE_CONFIG) { 00100 /* Nothing to do, we are already in config mode */ 00101 return EDDYSTONE_ERROR_NONE; 00102 } else if (advConfigInterval == 0) { 00103 /* Nothing to do, the advertisement interval is 0 */ 00104 return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL; 00105 } 00106 00107 if (operationMode == EDDYSTONE_MODE_BEACON) { 00108 ble.shutdown(); 00109 /* Free unused memory */ 00110 freeBeaconFrames(); 00111 operationMode = EDDYSTONE_MODE_CONFIG; 00112 ble.init(this, &EddystoneService::bleInitComplete); 00113 return EDDYSTONE_ERROR_NONE; 00114 } 00115 00116 operationMode = EDDYSTONE_MODE_CONFIG; 00117 setupConfigService(); 00118 return EDDYSTONE_ERROR_NONE; 00119 } 00120 00121 EddystoneService::EddystoneError_t EddystoneService::startBeaconService(uint16_t consecUrlFramesIn, uint16_t consecUidFramesIn, uint16_t consecTlmFramesIn) 00122 { 00123 if (operationMode == EDDYSTONE_MODE_BEACON) { 00124 /* Nothing to do, we are already in beacon mode */ 00125 return EDDYSTONE_ERROR_NONE; 00126 } else if (!consecUrlFramesIn && !consecUidFramesIn && !consecTlmFramesIn) { 00127 /* Nothing to do, the user wants 0 consecutive frames of everything */ 00128 return EDDYSTONE_ERROR_INVALID_CONSEC_FRAMES; 00129 } else if (!beaconPeriod) { 00130 /* Nothing to do, the period is 0 for all frames */ 00131 return EDDYSTONE_ERROR_INVALID_BEACON_PERIOD; 00132 } 00133 00134 /* Setup tracking of the current advertised frame. Note that this will 00135 * cause URL or UID frames to be advertised first! 00136 */ 00137 currentAdvertisedFrame = EDDYSTONE_FRAME_TLM; 00138 consecFrames[EDDYSTONE_FRAME_URL] = consecUrlFramesIn; 00139 consecFrames[EDDYSTONE_FRAME_UID] = consecUidFramesIn; 00140 consecFrames[EDDYSTONE_FRAME_TLM] = consecTlmFramesIn; 00141 00142 memset(currentConsecFrames, 0, sizeof(uint16_t) * NUM_EDDYSTONE_FRAMES); 00143 00144 if (operationMode == EDDYSTONE_MODE_CONFIG) { 00145 ble.shutdown(); 00146 /* Free unused memory */ 00147 freeConfigCharacteristics(); 00148 operationMode = EDDYSTONE_MODE_BEACON; 00149 ble.init(this, &EddystoneService::bleInitComplete); 00150 return EDDYSTONE_ERROR_NONE; 00151 } 00152 00153 operationMode = EDDYSTONE_MODE_BEACON; 00154 setupBeaconService(); 00155 return EDDYSTONE_ERROR_NONE; 00156 } 00157 00158 /* It is not the responsibility of the Eddystone implementation to store 00159 * the configured parameters in persistent storage since this is 00160 * platform-specific. So we provide this function that returns the 00161 * configured values that need to be stored and the main application 00162 * takes care of storing them. 00163 */ 00164 void EddystoneService::getEddystoneParams(EddystoneParams_t *params) 00165 { 00166 params->lockState = lockState; 00167 params->flags = flags; 00168 params->txPowerMode = txPowerMode; 00169 params->beaconPeriod = beaconPeriod; 00170 params->tlmVersion = tlmFrame.getTLMVersion(); 00171 params->urlDataLength = urlFrame.getEncodedURLDataLength(); 00172 00173 memcpy(params->advPowerLevels, advPowerLevels, sizeof(PowerLevels_t)); 00174 memcpy(params->lock, lock, sizeof(Lock_t)); 00175 memcpy(params->unlock, unlock, sizeof(Lock_t)); 00176 memcpy(params->urlData, urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength()); 00177 memcpy(params->uidNamespaceID, uidFrame.getUIDNamespaceID(), sizeof(UIDNamespaceID_t)); 00178 memcpy(params->uidInstanceID, uidFrame.getUIDInstanceID(), sizeof(UIDInstanceID_t)); 00179 } 00180 00181 /* Helper function used only once during constructing the object to avoid 00182 * duplicated code. 00183 */ 00184 void EddystoneService::eddystoneConstructorHelper(const PowerLevels_t &advPowerLevelsIn, 00185 const PowerLevels_t &radioPowerLevelsIn, 00186 uint32_t advConfigIntervalIn) 00187 { 00188 advConfigInterval = (advConfigIntervalIn > 0) ? correctAdvertisementPeriod(advConfigIntervalIn) : 0; 00189 00190 memcpy(radioPowerLevels, radioPowerLevelsIn, sizeof(PowerLevels_t)); 00191 memcpy(advPowerLevels, advPowerLevelsIn, sizeof(PowerLevels_t)); 00192 00193 /* TODO: Note that this timer is started from the time EddystoneService 00194 * is initialised and NOT from when the device is booted. So app needs 00195 * to take care that EddystoneService is one of the first things to be 00196 * started! 00197 */ 00198 timeSinceBootTimer.start(); 00199 } 00200 00201 /* When changing modes, we shutdown and init the BLE instance, so 00202 * this is needed to complete the initialisation task. 00203 */ 00204 void EddystoneService::bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext) 00205 { 00206 if (initContext->error != BLE_ERROR_NONE) { 00207 /* Initialisation failed */ 00208 return; 00209 } 00210 00211 switch (operationMode) { 00212 case EDDYSTONE_MODE_CONFIG: 00213 setupConfigService(); 00214 break; 00215 case EDDYSTONE_MODE_BEACON: 00216 setupBeaconService(); 00217 break; 00218 default: 00219 /* Some error occurred */ 00220 break; 00221 } 00222 } 00223 00224 void EddystoneService::swapAdvertisedFrame(void) 00225 { 00226 /* This essentially works out which is the next frame to be swapped in 00227 * and updated the advertised packets. It will eventually terminate 00228 * and in the worst case the frame swapped in is the current advertised 00229 * frame. 00230 */ 00231 while (true) { 00232 currentAdvertisedFrame = (currentAdvertisedFrame + 1) % NUM_EDDYSTONE_FRAMES; 00233 00234 if (currentAdvertisedFrame == EDDYSTONE_FRAME_URL && consecFrames[EDDYSTONE_FRAME_URL] > 0) { 00235 updateAdvertisementPacket(rawUrlFrame, urlFrame.getRawFrameSize()); 00236 return; 00237 } else if (currentAdvertisedFrame == EDDYSTONE_FRAME_UID && consecFrames[EDDYSTONE_FRAME_UID] > 0) { 00238 updateAdvertisementPacket(rawUidFrame, uidFrame.getRawFrameSize()); 00239 return; 00240 } else if (currentAdvertisedFrame == EDDYSTONE_FRAME_TLM && consecFrames[EDDYSTONE_FRAME_UID] > 0) { 00241 updateRawTLMFrame(); 00242 updateAdvertisementPacket(rawTlmFrame, tlmFrame.getRawFrameSize()); 00243 return; 00244 } 00245 } 00246 } 00247 00248 /* Helper function that calls user-defined functions to update Battery Voltage and Temperature (if available), 00249 * then updates the raw frame data and finally updates the actual advertised packet. This operation must be 00250 * done fairly often because the TLM frame TimeSinceBoot must have a 0.1 secs resolution according to the 00251 * Eddystone specification. 00252 */ 00253 void EddystoneService::updateRawTLMFrame(void) 00254 { 00255 if (tlmBeaconTemperatureCallback != NULL) { 00256 tlmFrame.updateBeaconTemperature((*tlmBeaconTemperatureCallback)(tlmFrame.getBeaconTemperature())); 00257 } 00258 if (tlmBatteryVoltageCallback != NULL) { 00259 tlmFrame.updateBatteryVoltage((*tlmBatteryVoltageCallback)(tlmFrame.getBatteryVoltage())); 00260 } 00261 tlmFrame.updateTimeSinceBoot(timeSinceBootTimer.read_ms()); 00262 tlmFrame.constructTLMFrame(rawTlmFrame); 00263 } 00264 00265 void EddystoneService::updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength) 00266 { 00267 ble.gap().clearAdvertisingPayload(); 00268 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); 00269 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID)); 00270 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, rawFrameLength); 00271 } 00272 00273 void EddystoneService::setupBeaconService(void) 00274 { 00275 /* Initialise arrays to hold constructed raw frames */ 00276 if (consecFrames[EDDYSTONE_FRAME_URL] > 0) { 00277 rawUrlFrame = new uint8_t[urlFrame.getRawFrameSize()]; 00278 urlFrame.constructURLFrame(rawUrlFrame, advPowerLevels[txPowerMode]); 00279 } 00280 00281 if (consecFrames[EDDYSTONE_FRAME_UID] > 0) { 00282 rawUidFrame = new uint8_t[uidFrame.getRawFrameSize()]; 00283 uidFrame.constructUIDFrame(rawUidFrame, advPowerLevels[txPowerMode]); 00284 } 00285 00286 if (consecFrames[EDDYSTONE_FRAME_TLM] > 0) { 00287 rawTlmFrame = new uint8_t[tlmFrame.getRawFrameSize()]; 00288 /* Do not initialise because we have to reconstruct every 0.1 secs */ 00289 } 00290 00291 /* Configure advertisements */ 00292 ble.gap().setTxPower(radioPowerLevels[txPowerMode]); 00293 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED); 00294 ble.gap().setAdvertisingInterval(beaconPeriod); 00295 ble.gap().onRadioNotification(this, &EddystoneService::radioNotificationCallback); 00296 ble.gap().initRadioNotification(); 00297 00298 /* Set advertisement packet payload */ 00299 swapAdvertisedFrame(); 00300 00301 /* Start advertising */ 00302 ble.gap().startAdvertising(); 00303 } 00304 00305 void EddystoneService::setupConfigService(void) 00306 { 00307 lockStateChar = new ReadOnlyGattCharacteristic<bool>(UUID_LOCK_STATE_CHAR, &lockState); 00308 lockChar = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_LOCK_CHAR, lock); 00309 unlockChar = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_UNLOCK_CHAR, unlock); 00310 urlDataChar = new GattCharacteristic(UUID_URL_DATA_CHAR, urlFrame.getEncodedURLData(), 0, URL_DATA_MAX, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); 00311 flagsChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_FLAGS_CHAR, &flags); 00312 advPowerLevelsChar = new ReadWriteArrayGattCharacteristic<int8_t, sizeof(PowerLevels_t)>(UUID_ADV_POWER_LEVELS_CHAR, advPowerLevels); 00313 txPowerModeChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_TX_POWER_MODE_CHAR, &txPowerMode); 00314 beaconPeriodChar = new ReadWriteGattCharacteristic<uint16_t>(UUID_BEACON_PERIOD_CHAR, &beaconPeriod); 00315 resetChar = new WriteOnlyGattCharacteristic<bool>(UUID_RESET_CHAR, &resetFlag); 00316 00317 lockChar->setWriteAuthorizationCallback(this, &EddystoneService::lockAuthorizationCallback); 00318 unlockChar->setWriteAuthorizationCallback(this, &EddystoneService::unlockAuthorizationCallback); 00319 urlDataChar->setWriteAuthorizationCallback(this, &EddystoneService::urlDataWriteAuthorizationCallback); 00320 flagsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint8_t>); 00321 advPowerLevelsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<PowerLevels_t>); 00322 txPowerModeChar->setWriteAuthorizationCallback(this, &EddystoneService::powerModeAuthorizationCallback); 00323 beaconPeriodChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint16_t>); 00324 resetChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<bool>); 00325 00326 charTable[0] = lockStateChar; 00327 charTable[1] = lockChar; 00328 charTable[2] = unlockChar; 00329 charTable[3] = urlDataChar; 00330 charTable[4] = flagsChar; 00331 charTable[5] = advPowerLevelsChar; 00332 charTable[6] = txPowerModeChar; 00333 charTable[7] = beaconPeriodChar; 00334 charTable[8] = resetChar; 00335 00336 GattService configService(UUID_URL_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); 00337 00338 ble.gattServer().addService(configService); 00339 ble.gattServer().onDataWritten(this, &EddystoneService::onDataWrittenCallback); 00340 updateCharacteristicValues(); 00341 setupEddystoneConfigAdvertisements(); 00342 } 00343 00344 void EddystoneService::freeConfigCharacteristics(void) 00345 { 00346 delete lockStateChar; 00347 delete lockChar; 00348 delete unlockChar; 00349 delete urlDataChar; 00350 delete flagsChar; 00351 delete advPowerLevelsChar; 00352 delete txPowerModeChar; 00353 delete beaconPeriodChar; 00354 delete resetChar; 00355 } 00356 00357 void EddystoneService::freeBeaconFrames(void) 00358 { 00359 delete[] rawUrlFrame; 00360 delete[] rawUidFrame; 00361 delete[] rawTlmFrame; 00362 } 00363 00364 void EddystoneService::radioNotificationCallback(bool radioActive) 00365 { 00366 if (radioActive) { 00367 /* Do nothing */ 00368 return; 00369 } 00370 00371 tlmFrame.updatePduCount(); 00372 currentConsecFrames[currentAdvertisedFrame]++; 00373 00374 if (consecFrames[currentAdvertisedFrame] > currentConsecFrames[currentAdvertisedFrame]) { 00375 if (currentAdvertisedFrame == EDDYSTONE_FRAME_TLM) { 00376 /* Update the TLM frame otherwise we will not meet the 0.1 secs resolution of 00377 * the Eddystone specification. 00378 */ 00379 updateRawTLMFrame(); 00380 updateAdvertisementPacket(rawTlmFrame, tlmFrame.getRawFrameSize()); 00381 } 00382 /* Keep advertising the same frame */ 00383 return; 00384 } 00385 00386 currentConsecFrames[currentAdvertisedFrame] = 0; 00387 00388 #ifdef YOTTA_CFG_MBED_OS 00389 minar::Scheduler::postCallback(this, &EddystoneService::swapAdvertisedFrame); 00390 #else 00391 swapAdvertisedFrameTimeout.attach_us(this, &EddystoneService::swapAdvertisedFrame, 1); 00392 #endif 00393 } 00394 00395 /* 00396 * Internal helper function used to update the GATT database following any 00397 * change to the internal state of the service object. 00398 */ 00399 void EddystoneService::updateCharacteristicValues(void) 00400 { 00401 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool)); 00402 ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength()); 00403 ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t)); 00404 ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t)); 00405 ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t)); 00406 ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t)); 00407 ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t)); 00408 ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t)); 00409 } 00410 00411 void EddystoneService::setupEddystoneConfigAdvertisements(void) 00412 { 00413 ble.gap().clearAdvertisingPayload(); 00414 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); 00415 00416 /* UUID is in different order in the ADV frame (!) */ 00417 uint8_t reversedServiceUUID[sizeof(UUID_URL_BEACON_SERVICE)]; 00418 for (size_t i = 0; i < sizeof(UUID_URL_BEACON_SERVICE); i++) { 00419 reversedServiceUUID[i] = UUID_URL_BEACON_SERVICE[sizeof(UUID_URL_BEACON_SERVICE) - i - 1]; 00420 } 00421 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID)); 00422 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG); 00423 ble.gap().accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME)); 00424 ble.gap().accumulateScanResponse( 00425 GapAdvertisingData::TX_POWER_LEVEL, 00426 reinterpret_cast<uint8_t *>(&advPowerLevels[TX_POWER_MODE_LOW]), 00427 sizeof(uint8_t)); 00428 00429 ble.gap().setTxPower(radioPowerLevels[txPowerMode]); 00430 ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME)); 00431 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); 00432 ble.gap().setAdvertisingInterval(advConfigInterval); 00433 ble.gap().startAdvertising(); 00434 } 00435 00436 void EddystoneService::lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) 00437 { 00438 if (lockState) { 00439 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; 00440 } else if (authParams->len != sizeof(Lock_t)) { 00441 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; 00442 } else if (authParams->offset != 0) { 00443 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; 00444 } else { 00445 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; 00446 } 00447 } 00448 00449 void EddystoneService::unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) 00450 { 00451 if (!lockState && (authParams->len == sizeof(Lock_t))) { 00452 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; 00453 } else if (authParams->len != sizeof(Lock_t)) { 00454 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; 00455 } else if (authParams->offset != 0) { 00456 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; 00457 } else if (memcmp(authParams->data, lock, sizeof(Lock_t)) != 0) { 00458 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; 00459 } else { 00460 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; 00461 } 00462 } 00463 00464 void EddystoneService::urlDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams) 00465 { 00466 if (lockState) { 00467 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; 00468 } else if (authParams->offset != 0) { 00469 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; 00470 } else { 00471 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; 00472 } 00473 } 00474 00475 void EddystoneService::powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams) 00476 { 00477 if (lockState) { 00478 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; 00479 } else if (authParams->len != sizeof(uint8_t)) { 00480 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; 00481 } else if (authParams->offset != 0) { 00482 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; 00483 } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) { 00484 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED; 00485 } else { 00486 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; 00487 } 00488 } 00489 00490 template <typename T> 00491 void EddystoneService::basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams) 00492 { 00493 if (lockState) { 00494 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; 00495 } else if (authParams->len != sizeof(T)) { 00496 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; 00497 } else if (authParams->offset != 0) { 00498 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; 00499 } else { 00500 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; 00501 } 00502 } 00503 00504 /* 00505 * This callback is invoked when a GATT client attempts to modify any of the 00506 * characteristics of this service. Attempts to do so are also applied to 00507 * the internal state of this service object. 00508 */ 00509 void EddystoneService::onDataWrittenCallback(const GattWriteCallbackParams *writeParams) 00510 { 00511 uint16_t handle = writeParams->handle; 00512 00513 if (handle == lockChar->getValueHandle()) { 00514 memcpy(lock, writeParams->data, sizeof(Lock_t)); 00515 /* Set the state to be locked by the lock code (note: zeros are a valid lock) */ 00516 lockState = true; 00517 ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t)); 00518 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool)); 00519 } else if (handle == unlockChar->getValueHandle()) { 00520 /* Validated earlier */ 00521 lockState = false; 00522 ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t)); 00523 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool)); 00524 } else if (handle == urlDataChar->getValueHandle()) { 00525 urlFrame.setEncodedURLData(writeParams->data, writeParams->len); 00526 ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength()); 00527 } else if (handle == flagsChar->getValueHandle()) { 00528 flags = *(writeParams->data); 00529 ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t)); 00530 } else if (handle == advPowerLevelsChar->getValueHandle()) { 00531 memcpy(advPowerLevels, writeParams->data, sizeof(PowerLevels_t)); 00532 ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t)); 00533 } else if (handle == txPowerModeChar->getValueHandle()) { 00534 txPowerMode = *(writeParams->data); 00535 ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t)); 00536 } else if (handle == beaconPeriodChar->getValueHandle()) { 00537 uint16_t tmpBeaconPeriod = correctAdvertisementPeriod(*((uint16_t *)(writeParams->data))); 00538 if (tmpBeaconPeriod != beaconPeriod) { 00539 beaconPeriod = tmpBeaconPeriod; 00540 ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t)); 00541 } 00542 } else if (handle == resetChar->getValueHandle() && (*((uint8_t *)writeParams->data) != 0)) { 00543 /* Reset characteristics to default values */ 00544 flags = 0; 00545 txPowerMode = TX_POWER_MODE_LOW; 00546 beaconPeriod = DEFAULT_BEACON_PERIOD_MSEC; 00547 00548 urlFrame.setURLData(DEFAULT_URL); 00549 memset(lock, 0, sizeof(Lock_t)); 00550 00551 ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength()); 00552 ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t)); 00553 ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t)); 00554 ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t)); 00555 ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t)); 00556 } 00557 } 00558 00559 uint16_t EddystoneService::correctAdvertisementPeriod(uint16_t beaconPeriodIn) const 00560 { 00561 /* Re-map beaconPeriod to within permissible bounds if necessary. */ 00562 if (beaconPeriodIn != 0) { 00563 if (beaconPeriodIn < ble.gap().getMinAdvertisingInterval()) { 00564 return ble.gap().getMinAdvertisingInterval(); 00565 } else if (beaconPeriodIn > ble.gap().getMaxAdvertisingInterval()) { 00566 return ble.gap().getMaxAdvertisingInterval(); 00567 } 00568 } 00569 return beaconPeriodIn; 00570 }
Generated on Wed Jul 13 2022 16:41:55 by 1.7.2