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.
source/EddystoneService.cpp@0:ed0152b5c495, 2016-09-19 (annotated)
- Committer:
- roywant
- Date:
- Mon Sep 19 00:59:11 2016 +0000
- Revision:
- 0:ed0152b5c495
Initial commit
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| roywant | 0:ed0152b5c495 | 1 | /* mbed Microcontroller Library |
| roywant | 0:ed0152b5c495 | 2 | * Copyright (c) 2006-2015 ARM Limited |
| roywant | 0:ed0152b5c495 | 3 | * |
| roywant | 0:ed0152b5c495 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| roywant | 0:ed0152b5c495 | 5 | * you may not use this file except in compliance with the License. |
| roywant | 0:ed0152b5c495 | 6 | * You may obtain a copy of the License at |
| roywant | 0:ed0152b5c495 | 7 | * |
| roywant | 0:ed0152b5c495 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| roywant | 0:ed0152b5c495 | 9 | * |
| roywant | 0:ed0152b5c495 | 10 | * Unless required by applicable law or agreed to in writing, software |
| roywant | 0:ed0152b5c495 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| roywant | 0:ed0152b5c495 | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| roywant | 0:ed0152b5c495 | 13 | * See the License for the specific language governing permissions and |
| roywant | 0:ed0152b5c495 | 14 | * limitations under the License. |
| roywant | 0:ed0152b5c495 | 15 | */ |
| roywant | 0:ed0152b5c495 | 16 | |
| roywant | 0:ed0152b5c495 | 17 | #include "EddystoneService.h" |
| roywant | 0:ed0152b5c495 | 18 | #include "EntropySource/EntropySource.h" |
| roywant | 0:ed0152b5c495 | 19 | |
| roywant | 0:ed0152b5c495 | 20 | /* Use define zero for production, 1 for testing to allow connection at any time */ |
| roywant | 0:ed0152b5c495 | 21 | #define DEFAULT_REMAIN_CONNECTABLE 0x01 |
| roywant | 0:ed0152b5c495 | 22 | |
| roywant | 0:ed0152b5c495 | 23 | const char * const EddystoneService::slotDefaultUrls[] = EDDYSTONE_DEFAULT_SLOT_URLS; |
| roywant | 0:ed0152b5c495 | 24 | |
| roywant | 0:ed0152b5c495 | 25 | // Static timer used as time since boot |
| roywant | 0:ed0152b5c495 | 26 | Timer EddystoneService::timeSinceBootTimer; |
| roywant | 0:ed0152b5c495 | 27 | |
| roywant | 0:ed0152b5c495 | 28 | /* |
| roywant | 0:ed0152b5c495 | 29 | * CONSTRUCTOR #1 Used on 1st boot (after reflash) |
| roywant | 0:ed0152b5c495 | 30 | */ |
| roywant | 0:ed0152b5c495 | 31 | EddystoneService::EddystoneService(BLE &bleIn, |
| roywant | 0:ed0152b5c495 | 32 | const PowerLevels_t &advTxPowerLevelsIn, |
| roywant | 0:ed0152b5c495 | 33 | const PowerLevels_t &radioTxPowerLevelsIn, |
| roywant | 0:ed0152b5c495 | 34 | event_queue_t &evQ, |
| roywant | 0:ed0152b5c495 | 35 | uint32_t advConfigIntervalIn) : |
| roywant | 0:ed0152b5c495 | 36 | ble(bleIn), |
| roywant | 0:ed0152b5c495 | 37 | operationMode(EDDYSTONE_MODE_NONE), |
| roywant | 0:ed0152b5c495 | 38 | uidFrame(), |
| roywant | 0:ed0152b5c495 | 39 | urlFrame(), |
| roywant | 0:ed0152b5c495 | 40 | tlmFrame(), |
| roywant | 0:ed0152b5c495 | 41 | eidFrame(), |
| roywant | 0:ed0152b5c495 | 42 | tlmBatteryVoltageCallback(NULL), |
| roywant | 0:ed0152b5c495 | 43 | tlmBeaconTemperatureCallback(NULL), |
| roywant | 0:ed0152b5c495 | 44 | radioManagerCallbackHandle(NULL), |
| roywant | 0:ed0152b5c495 | 45 | deviceName(DEFAULT_DEVICE_NAME), |
| roywant | 0:ed0152b5c495 | 46 | eventQueue(evQ), |
| roywant | 0:ed0152b5c495 | 47 | nextEidSlot(0) |
| roywant | 0:ed0152b5c495 | 48 | { |
| roywant | 0:ed0152b5c495 | 49 | LOG(("1st Boot: ")); |
| roywant | 0:ed0152b5c495 | 50 | LOG((BUILD_VERSION_STR)); |
| roywant | 0:ed0152b5c495 | 51 | if (advConfigIntervalIn != 0) { |
| roywant | 0:ed0152b5c495 | 52 | if (advConfigIntervalIn < ble.gap().getMinAdvertisingInterval()) { |
| roywant | 0:ed0152b5c495 | 53 | advConfigInterval = ble.gap().getMinAdvertisingInterval(); |
| roywant | 0:ed0152b5c495 | 54 | } else if (advConfigIntervalIn > ble.gap().getMaxAdvertisingInterval()) { |
| roywant | 0:ed0152b5c495 | 55 | advConfigInterval = ble.gap().getMaxAdvertisingInterval(); |
| roywant | 0:ed0152b5c495 | 56 | } else { |
| roywant | 0:ed0152b5c495 | 57 | advConfigInterval = advConfigIntervalIn; |
| roywant | 0:ed0152b5c495 | 58 | } |
| roywant | 0:ed0152b5c495 | 59 | } |
| roywant | 0:ed0152b5c495 | 60 | memcpy(radioTxPowerLevels, radioTxPowerLevelsIn, sizeof(PowerLevels_t)); |
| roywant | 0:ed0152b5c495 | 61 | memcpy(advTxPowerLevels, advTxPowerLevelsIn, sizeof(PowerLevels_t)); |
| roywant | 0:ed0152b5c495 | 62 | |
| roywant | 0:ed0152b5c495 | 63 | // 1st Boot so reset everything to factory values |
| roywant | 0:ed0152b5c495 | 64 | doFactoryReset(); // includes genBeaconKeys |
| roywant | 0:ed0152b5c495 | 65 | |
| roywant | 0:ed0152b5c495 | 66 | LOG(("After FactoryReset in 1st Boot Init: genBeaconKeyRC=%d\r\n", genBeaconKeyRC)); |
| roywant | 0:ed0152b5c495 | 67 | /* TODO: Note that this timer is started from the time EddystoneService |
| roywant | 0:ed0152b5c495 | 68 | * is initialised and NOT from when the device is booted. |
| roywant | 0:ed0152b5c495 | 69 | */ |
| roywant | 0:ed0152b5c495 | 70 | timeSinceBootTimer.start(); |
| roywant | 0:ed0152b5c495 | 71 | |
| roywant | 0:ed0152b5c495 | 72 | /* Set the device name at startup */ |
| roywant | 0:ed0152b5c495 | 73 | ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName)); |
| roywant | 0:ed0152b5c495 | 74 | |
| roywant | 0:ed0152b5c495 | 75 | } |
| roywant | 0:ed0152b5c495 | 76 | |
| roywant | 0:ed0152b5c495 | 77 | /* |
| roywant | 0:ed0152b5c495 | 78 | * Constuctor #2: Used on 2nd+ boot: EddystoneService parameters derived from persistent storage |
| roywant | 0:ed0152b5c495 | 79 | */ |
| roywant | 0:ed0152b5c495 | 80 | EddystoneService::EddystoneService(BLE &bleIn, |
| roywant | 0:ed0152b5c495 | 81 | EddystoneParams_t ¶msIn, |
| roywant | 0:ed0152b5c495 | 82 | const PowerLevels_t &radioTxPowerLevelsIn, |
| roywant | 0:ed0152b5c495 | 83 | event_queue_t &evQ, |
| roywant | 0:ed0152b5c495 | 84 | uint32_t advConfigIntervalIn) : |
| roywant | 0:ed0152b5c495 | 85 | ble(bleIn), |
| roywant | 0:ed0152b5c495 | 86 | operationMode(EDDYSTONE_MODE_NONE), |
| roywant | 0:ed0152b5c495 | 87 | uidFrame(), |
| roywant | 0:ed0152b5c495 | 88 | urlFrame(), |
| roywant | 0:ed0152b5c495 | 89 | tlmFrame(), |
| roywant | 0:ed0152b5c495 | 90 | eidFrame(), |
| roywant | 0:ed0152b5c495 | 91 | tlmBatteryVoltageCallback(NULL), |
| roywant | 0:ed0152b5c495 | 92 | tlmBeaconTemperatureCallback(NULL), |
| roywant | 0:ed0152b5c495 | 93 | radioManagerCallbackHandle(NULL), |
| roywant | 0:ed0152b5c495 | 94 | deviceName(DEFAULT_DEVICE_NAME), |
| roywant | 0:ed0152b5c495 | 95 | eventQueue(evQ), |
| roywant | 0:ed0152b5c495 | 96 | nextEidSlot(0) |
| roywant | 0:ed0152b5c495 | 97 | { |
| roywant | 0:ed0152b5c495 | 98 | LOG(("2nd (>=) Boot: ")); |
| roywant | 0:ed0152b5c495 | 99 | LOG((BUILD_VERSION_STR)); |
| roywant | 0:ed0152b5c495 | 100 | memcpy(capabilities, paramsIn.capabilities, sizeof(Capability_t)); |
| roywant | 0:ed0152b5c495 | 101 | activeSlot = paramsIn.activeSlot; |
| roywant | 0:ed0152b5c495 | 102 | memcpy(radioTxPowerLevels, radioTxPowerLevelsIn, sizeof(PowerLevels_t)); |
| roywant | 0:ed0152b5c495 | 103 | memcpy(slotRadioTxPowerLevels, paramsIn.slotRadioTxPowerLevels, sizeof(SlotTxPowerLevels_t)); |
| roywant | 0:ed0152b5c495 | 104 | memcpy(advTxPowerLevels, paramsIn.advTxPowerLevels, sizeof(PowerLevels_t)); |
| roywant | 0:ed0152b5c495 | 105 | memcpy(slotAdvTxPowerLevels, paramsIn.slotAdvTxPowerLevels, sizeof(SlotTxPowerLevels_t)); |
| roywant | 0:ed0152b5c495 | 106 | memcpy(slotAdvIntervals, paramsIn.slotAdvIntervals, sizeof(SlotAdvIntervals_t)); |
| roywant | 0:ed0152b5c495 | 107 | lockState = paramsIn.lockState; |
| roywant | 0:ed0152b5c495 | 108 | memcpy(unlockKey, paramsIn.unlockKey, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 109 | memcpy(unlockToken, paramsIn.unlockToken, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 110 | memcpy(challenge, paramsIn.challenge, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 111 | memset(slotCallbackHandles, 0, sizeof(SlotCallbackHandles_t)); |
| roywant | 0:ed0152b5c495 | 112 | memcpy(slotStorage, paramsIn.slotStorage, sizeof(SlotStorage_t)); |
| roywant | 0:ed0152b5c495 | 113 | memcpy(slotFrameTypes, paramsIn.slotFrameTypes, sizeof(SlotFrameTypes_t)); |
| roywant | 0:ed0152b5c495 | 114 | memcpy(slotEidRotationPeriodExps, paramsIn.slotEidRotationPeriodExps, sizeof(SlotEidRotationPeriodExps_t)); |
| roywant | 0:ed0152b5c495 | 115 | memcpy(slotEidIdentityKeys, paramsIn.slotEidIdentityKeys, sizeof(SlotEidIdentityKeys_t)); |
| roywant | 0:ed0152b5c495 | 116 | remainConnectable = paramsIn.remainConnectable; |
| roywant | 0:ed0152b5c495 | 117 | |
| roywant | 0:ed0152b5c495 | 118 | if (advConfigIntervalIn != 0) { |
| roywant | 0:ed0152b5c495 | 119 | if (advConfigIntervalIn < ble.gap().getMinAdvertisingInterval()) { |
| roywant | 0:ed0152b5c495 | 120 | advConfigInterval = ble.gap().getMinAdvertisingInterval(); |
| roywant | 0:ed0152b5c495 | 121 | } else if (advConfigIntervalIn > ble.gap().getMaxAdvertisingInterval()) { |
| roywant | 0:ed0152b5c495 | 122 | advConfigInterval = ble.gap().getMaxAdvertisingInterval(); |
| roywant | 0:ed0152b5c495 | 123 | } else { |
| roywant | 0:ed0152b5c495 | 124 | advConfigInterval = advConfigIntervalIn; |
| roywant | 0:ed0152b5c495 | 125 | } |
| roywant | 0:ed0152b5c495 | 126 | } |
| roywant | 0:ed0152b5c495 | 127 | |
| roywant | 0:ed0152b5c495 | 128 | // Generate fresh private and public ECDH keys for EID |
| roywant | 0:ed0152b5c495 | 129 | genEIDBeaconKeys(); |
| roywant | 0:ed0152b5c495 | 130 | |
| roywant | 0:ed0152b5c495 | 131 | // Recompute EID Slot Data |
| roywant | 0:ed0152b5c495 | 132 | for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) { |
| roywant | 0:ed0152b5c495 | 133 | uint8_t* frame = slotToFrame(slot); |
| roywant | 0:ed0152b5c495 | 134 | switch (slotFrameTypes[slot]) { |
| roywant | 0:ed0152b5c495 | 135 | case EDDYSTONE_FRAME_EID: |
| roywant | 0:ed0152b5c495 | 136 | eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000); |
| roywant | 0:ed0152b5c495 | 137 | eidFrame.setAdvTxPower(frame, slotAdvTxPowerLevels[slot]); |
| roywant | 0:ed0152b5c495 | 138 | break; |
| roywant | 0:ed0152b5c495 | 139 | default: ; |
| roywant | 0:ed0152b5c495 | 140 | } |
| roywant | 0:ed0152b5c495 | 141 | } |
| roywant | 0:ed0152b5c495 | 142 | |
| roywant | 0:ed0152b5c495 | 143 | |
| roywant | 0:ed0152b5c495 | 144 | /* TODO: Note that this timer is started from the time EddystoneService |
| roywant | 0:ed0152b5c495 | 145 | * is initialised and NOT from when the device is booted. |
| roywant | 0:ed0152b5c495 | 146 | */ |
| roywant | 0:ed0152b5c495 | 147 | timeSinceBootTimer.start(); |
| roywant | 0:ed0152b5c495 | 148 | |
| roywant | 0:ed0152b5c495 | 149 | /* Set the device name at startup */ |
| roywant | 0:ed0152b5c495 | 150 | ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName)); |
| roywant | 0:ed0152b5c495 | 151 | } |
| roywant | 0:ed0152b5c495 | 152 | |
| roywant | 0:ed0152b5c495 | 153 | // Regenerate the beacon keys |
| roywant | 0:ed0152b5c495 | 154 | void EddystoneService::genEIDBeaconKeys(void) { |
| roywant | 0:ed0152b5c495 | 155 | genBeaconKeyRC = -1; |
| roywant | 0:ed0152b5c495 | 156 | #ifdef GEN_BEACON_KEYS_AT_INIT |
| roywant | 0:ed0152b5c495 | 157 | memset(privateEcdhKey, 0, 32); |
| roywant | 0:ed0152b5c495 | 158 | memset(publicEcdhKey, 0, 32); |
| roywant | 0:ed0152b5c495 | 159 | genBeaconKeyRC = eidFrame.genBeaconKeys(privateEcdhKey, publicEcdhKey); |
| roywant | 0:ed0152b5c495 | 160 | swapEndianArray(publicEcdhKey, publicEcdhKeyLE, 32); |
| roywant | 0:ed0152b5c495 | 161 | #endif |
| roywant | 0:ed0152b5c495 | 162 | } |
| roywant | 0:ed0152b5c495 | 163 | |
| roywant | 0:ed0152b5c495 | 164 | /** |
| roywant | 0:ed0152b5c495 | 165 | * Factory reset all parmeters: used at initial boot, and activated from Char 11 |
| roywant | 0:ed0152b5c495 | 166 | */ |
| roywant | 0:ed0152b5c495 | 167 | void EddystoneService::doFactoryReset(void) |
| roywant | 0:ed0152b5c495 | 168 | { |
| roywant | 0:ed0152b5c495 | 169 | memset(slotCallbackHandles, 0, sizeof(SlotCallbackHandles_t)); |
| roywant | 0:ed0152b5c495 | 170 | radioManagerCallbackHandle = NULL; |
| roywant | 0:ed0152b5c495 | 171 | memcpy(capabilities, CAPABILITIES_DEFAULT, CAP_HDR_LEN); |
| roywant | 0:ed0152b5c495 | 172 | // Line above leaves powerlevels blank; Line below fills them in |
| roywant | 0:ed0152b5c495 | 173 | memcpy(capabilities + CAP_HDR_LEN, radioTxPowerLevels, sizeof(PowerLevels_t)); |
| roywant | 0:ed0152b5c495 | 174 | activeSlot = DEFAULT_SLOT; |
| roywant | 0:ed0152b5c495 | 175 | // Intervals |
| roywant | 0:ed0152b5c495 | 176 | uint16_t buf1[] = EDDYSTONE_DEFAULT_SLOT_INTERVALS; |
| roywant | 0:ed0152b5c495 | 177 | for (int i = 0; i < MAX_ADV_SLOTS; i++) { |
| roywant | 0:ed0152b5c495 | 178 | // Ensure all slot periods are in range |
| roywant | 0:ed0152b5c495 | 179 | buf1[i] = correctAdvertisementPeriod(buf1[i]); |
| roywant | 0:ed0152b5c495 | 180 | } |
| roywant | 0:ed0152b5c495 | 181 | memcpy(slotAdvIntervals, buf1, sizeof(SlotAdvIntervals_t)); |
| roywant | 0:ed0152b5c495 | 182 | // Radio and Adv TX Power |
| roywant | 0:ed0152b5c495 | 183 | int8_t buf2[] = EDDYSTONE_DEFAULT_SLOT_TX_POWERS; |
| roywant | 0:ed0152b5c495 | 184 | for (int i = 0; i< MAX_ADV_SLOTS; i++) { |
| roywant | 0:ed0152b5c495 | 185 | slotRadioTxPowerLevels[i] = buf2[i]; |
| roywant | 0:ed0152b5c495 | 186 | slotAdvTxPowerLevels[i] = advTxPowerLevels[radioTxPowerToIndex(buf2[i])]; |
| roywant | 0:ed0152b5c495 | 187 | } |
| roywant | 0:ed0152b5c495 | 188 | // Lock |
| roywant | 0:ed0152b5c495 | 189 | lockState = UNLOCKED; |
| roywant | 0:ed0152b5c495 | 190 | uint8_t defKeyBuf[] = EDDYSTONE_DEFAULT_UNLOCK_KEY; |
| roywant | 0:ed0152b5c495 | 191 | memcpy(unlockKey, defKeyBuf, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 192 | memset(unlockToken, 0, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 193 | memset(challenge, 0, sizeof(Lock_t)); // NOTE: challenge is randomized on first unlockChar read; |
| roywant | 0:ed0152b5c495 | 194 | |
| roywant | 0:ed0152b5c495 | 195 | // Generate ECDH Beacon Key Pair (Private/Public) |
| roywant | 0:ed0152b5c495 | 196 | genEIDBeaconKeys(); |
| roywant | 0:ed0152b5c495 | 197 | |
| roywant | 0:ed0152b5c495 | 198 | memcpy(slotEidIdentityKeys, slotDefaultEidIdentityKeys, sizeof(SlotEidIdentityKeys_t)); |
| roywant | 0:ed0152b5c495 | 199 | uint8_t buf4[] = EDDYSTONE_DEFAULT_SLOT_EID_ROTATION_PERIOD_EXPS; |
| roywant | 0:ed0152b5c495 | 200 | memcpy(slotEidRotationPeriodExps, buf4, sizeof(SlotEidRotationPeriodExps_t)); |
| roywant | 0:ed0152b5c495 | 201 | memset(slotEidNextRotationTimes, 0, sizeof(SlotEidNextRotationTimes_t)); |
| roywant | 0:ed0152b5c495 | 202 | // Slot Data Type Defaults |
| roywant | 0:ed0152b5c495 | 203 | uint8_t buf3[] = EDDYSTONE_DEFAULT_SLOT_TYPES; |
| roywant | 0:ed0152b5c495 | 204 | memcpy(slotFrameTypes, buf3, sizeof(SlotFrameTypes_t)); |
| roywant | 0:ed0152b5c495 | 205 | // Initialize Slot Data Defaults |
| roywant | 0:ed0152b5c495 | 206 | int eidSlot; |
| roywant | 0:ed0152b5c495 | 207 | for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) { |
| roywant | 0:ed0152b5c495 | 208 | uint8_t* frame = slotToFrame(slot); |
| roywant | 0:ed0152b5c495 | 209 | switch (slotFrameTypes[slot]) { |
| roywant | 0:ed0152b5c495 | 210 | case EDDYSTONE_FRAME_UID: |
| roywant | 0:ed0152b5c495 | 211 | uidFrame.setData(frame, slotAdvTxPowerLevels[slot], reinterpret_cast<const uint8_t*>(slotDefaultUids[slot])); |
| roywant | 0:ed0152b5c495 | 212 | break; |
| roywant | 0:ed0152b5c495 | 213 | case EDDYSTONE_FRAME_URL: |
| roywant | 0:ed0152b5c495 | 214 | urlFrame.setUnencodedUrlData(frame, slotAdvTxPowerLevels[slot], slotDefaultUrls[slot]); |
| roywant | 0:ed0152b5c495 | 215 | break; |
| roywant | 0:ed0152b5c495 | 216 | case EDDYSTONE_FRAME_TLM: |
| roywant | 0:ed0152b5c495 | 217 | tlmFrame.setTLMData(TLMFrame::DEFAULT_TLM_VERSION); |
| roywant | 0:ed0152b5c495 | 218 | tlmFrame.setData(frame); |
| roywant | 0:ed0152b5c495 | 219 | eidSlot = getEidSlot(); |
| roywant | 0:ed0152b5c495 | 220 | if (eidSlot != NO_EID_SLOT_SET) { |
| roywant | 0:ed0152b5c495 | 221 | LOG(("EID slot Set in FactoryReset\r\n")); |
| roywant | 0:ed0152b5c495 | 222 | tlmFrame.encryptData(frame, slotEidIdentityKeys[eidSlot], slotEidRotationPeriodExps[eidSlot], timeSinceBootTimer.read_ms() / 1000); |
| roywant | 0:ed0152b5c495 | 223 | } |
| roywant | 0:ed0152b5c495 | 224 | break; |
| roywant | 0:ed0152b5c495 | 225 | case EDDYSTONE_FRAME_EID: |
| roywant | 0:ed0152b5c495 | 226 | nextEidSlot = slot; |
| roywant | 0:ed0152b5c495 | 227 | eidFrame.setData(frame, slotAdvTxPowerLevels[slot], reinterpret_cast<const uint8_t*>(allSlotsDefaultEid)); |
| roywant | 0:ed0152b5c495 | 228 | eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000); |
| roywant | 0:ed0152b5c495 | 229 | break; |
| roywant | 0:ed0152b5c495 | 230 | } |
| roywant | 0:ed0152b5c495 | 231 | } |
| roywant | 0:ed0152b5c495 | 232 | #ifdef DONT_REMAIN_CONNECTABLE |
| roywant | 0:ed0152b5c495 | 233 | remainConnectable = REMAIN_CONNECTABLE_UNSET; |
| roywant | 0:ed0152b5c495 | 234 | #else |
| roywant | 0:ed0152b5c495 | 235 | remainConnectable = REMAIN_CONNECTABLE_SET; |
| roywant | 0:ed0152b5c495 | 236 | #endif |
| roywant | 0:ed0152b5c495 | 237 | factoryReset = false; |
| roywant | 0:ed0152b5c495 | 238 | } |
| roywant | 0:ed0152b5c495 | 239 | |
| roywant | 0:ed0152b5c495 | 240 | /* Setup callback to update BatteryVoltage in TLM frame */ |
| roywant | 0:ed0152b5c495 | 241 | void EddystoneService::onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn) |
| roywant | 0:ed0152b5c495 | 242 | { |
| roywant | 0:ed0152b5c495 | 243 | tlmBatteryVoltageCallback = tlmBatteryVoltageCallbackIn; |
| roywant | 0:ed0152b5c495 | 244 | } |
| roywant | 0:ed0152b5c495 | 245 | |
| roywant | 0:ed0152b5c495 | 246 | /* Setup callback to update BeaconTemperature in TLM frame */ |
| roywant | 0:ed0152b5c495 | 247 | void EddystoneService::onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn) |
| roywant | 0:ed0152b5c495 | 248 | { |
| roywant | 0:ed0152b5c495 | 249 | tlmBeaconTemperatureCallback = tlmBeaconTemperatureCallbackIn; |
| roywant | 0:ed0152b5c495 | 250 | } |
| roywant | 0:ed0152b5c495 | 251 | |
| roywant | 0:ed0152b5c495 | 252 | EddystoneService::EddystoneError_t EddystoneService::startEddystoneBeaconAdvertisements(void) |
| roywant | 0:ed0152b5c495 | 253 | { |
| roywant | 0:ed0152b5c495 | 254 | stopEddystoneBeaconAdvertisements(); |
| roywant | 0:ed0152b5c495 | 255 | |
| roywant | 0:ed0152b5c495 | 256 | bool intervalValidFlag = false; |
| roywant | 0:ed0152b5c495 | 257 | for (int i = 0; i < MAX_ADV_SLOTS; i++) { |
| roywant | 0:ed0152b5c495 | 258 | if (slotAdvIntervals[i] != 0) { |
| roywant | 0:ed0152b5c495 | 259 | intervalValidFlag = true; |
| roywant | 0:ed0152b5c495 | 260 | } |
| roywant | 0:ed0152b5c495 | 261 | } |
| roywant | 0:ed0152b5c495 | 262 | |
| roywant | 0:ed0152b5c495 | 263 | if (!intervalValidFlag) { |
| roywant | 0:ed0152b5c495 | 264 | /* Nothing to do, the period is 0 for all frames */ |
| roywant | 0:ed0152b5c495 | 265 | return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL; |
| roywant | 0:ed0152b5c495 | 266 | } |
| roywant | 0:ed0152b5c495 | 267 | |
| roywant | 0:ed0152b5c495 | 268 | // In case left over from Config Adv Mode |
| roywant | 0:ed0152b5c495 | 269 | ble.gap().clearScanResponse(); |
| roywant | 0:ed0152b5c495 | 270 | |
| roywant | 0:ed0152b5c495 | 271 | operationMode = EDDYSTONE_MODE_BEACON; |
| roywant | 0:ed0152b5c495 | 272 | |
| roywant | 0:ed0152b5c495 | 273 | /* Configure advertisements initially at power of active slot*/ |
| roywant | 0:ed0152b5c495 | 274 | ble.gap().setTxPower(slotRadioTxPowerLevels[activeSlot]); |
| roywant | 0:ed0152b5c495 | 275 | |
| roywant | 0:ed0152b5c495 | 276 | if (remainConnectable) { |
| roywant | 0:ed0152b5c495 | 277 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
| roywant | 0:ed0152b5c495 | 278 | } else { |
| roywant | 0:ed0152b5c495 | 279 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED); |
| roywant | 0:ed0152b5c495 | 280 | } |
| roywant | 0:ed0152b5c495 | 281 | ble.gap().setAdvertisingInterval(ble.gap().getMaxAdvertisingInterval()); |
| roywant | 0:ed0152b5c495 | 282 | |
| roywant | 0:ed0152b5c495 | 283 | /* Make sure the queue is currently empty */ |
| roywant | 0:ed0152b5c495 | 284 | advFrameQueue.reset(); |
| roywant | 0:ed0152b5c495 | 285 | /* Setup callbacks to periodically add frames to be advertised to the queue and |
| roywant | 0:ed0152b5c495 | 286 | * add initial frame so that we have something to advertise on startup */ |
| roywant | 0:ed0152b5c495 | 287 | for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) { |
| roywant | 0:ed0152b5c495 | 288 | uint8_t* frame = slotToFrame(slot); |
| roywant | 0:ed0152b5c495 | 289 | if (slotAdvIntervals[slot] && testValidFrame(frame)) { |
| roywant | 0:ed0152b5c495 | 290 | advFrameQueue.push(slot); |
| roywant | 0:ed0152b5c495 | 291 | slotCallbackHandles[slot] = eventQueue.post_every( |
| roywant | 0:ed0152b5c495 | 292 | &EddystoneService::enqueueFrame, this, slot, |
| roywant | 0:ed0152b5c495 | 293 | slotAdvIntervals[slot] /* ms */ |
| roywant | 0:ed0152b5c495 | 294 | ); |
| roywant | 0:ed0152b5c495 | 295 | } |
| roywant | 0:ed0152b5c495 | 296 | } |
| roywant | 0:ed0152b5c495 | 297 | /* Start advertising */ |
| roywant | 0:ed0152b5c495 | 298 | manageRadio(); |
| roywant | 0:ed0152b5c495 | 299 | |
| roywant | 0:ed0152b5c495 | 300 | return EDDYSTONE_ERROR_NONE; |
| roywant | 0:ed0152b5c495 | 301 | } |
| roywant | 0:ed0152b5c495 | 302 | |
| roywant | 0:ed0152b5c495 | 303 | ble_error_t EddystoneService::setCompleteDeviceName(const char *deviceNameIn) |
| roywant | 0:ed0152b5c495 | 304 | { |
| roywant | 0:ed0152b5c495 | 305 | /* Make sure the device name is safe */ |
| roywant | 0:ed0152b5c495 | 306 | ble_error_t error = ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceNameIn)); |
| roywant | 0:ed0152b5c495 | 307 | if (error == BLE_ERROR_NONE) { |
| roywant | 0:ed0152b5c495 | 308 | deviceName = deviceNameIn; |
| roywant | 0:ed0152b5c495 | 309 | if (operationMode == EDDYSTONE_MODE_CONFIG) { |
| roywant | 0:ed0152b5c495 | 310 | /* Need to update the advertising packets to the new name */ |
| roywant | 0:ed0152b5c495 | 311 | setupEddystoneConfigScanResponse(); |
| roywant | 0:ed0152b5c495 | 312 | } |
| roywant | 0:ed0152b5c495 | 313 | } |
| roywant | 0:ed0152b5c495 | 314 | |
| roywant | 0:ed0152b5c495 | 315 | return error; |
| roywant | 0:ed0152b5c495 | 316 | } |
| roywant | 0:ed0152b5c495 | 317 | |
| roywant | 0:ed0152b5c495 | 318 | /* It is not the responsibility of the Eddystone implementation to store |
| roywant | 0:ed0152b5c495 | 319 | * the configured parameters in persistent storage since this is |
| roywant | 0:ed0152b5c495 | 320 | * platform-specific. So we provide this function that returns the |
| roywant | 0:ed0152b5c495 | 321 | * configured values that need to be stored and the main application |
| roywant | 0:ed0152b5c495 | 322 | * takes care of storing them. |
| roywant | 0:ed0152b5c495 | 323 | */ |
| roywant | 0:ed0152b5c495 | 324 | void EddystoneService::getEddystoneParams(EddystoneParams_t ¶ms) |
| roywant | 0:ed0152b5c495 | 325 | { |
| roywant | 0:ed0152b5c495 | 326 | // Capabilities |
| roywant | 0:ed0152b5c495 | 327 | memcpy(params.capabilities, capabilities, sizeof(Capability_t)); |
| roywant | 0:ed0152b5c495 | 328 | // Active Slot |
| roywant | 0:ed0152b5c495 | 329 | params.activeSlot = activeSlot; |
| roywant | 0:ed0152b5c495 | 330 | // Intervals |
| roywant | 0:ed0152b5c495 | 331 | memcpy(params.slotAdvIntervals, slotAdvIntervals, sizeof(SlotAdvIntervals_t)); |
| roywant | 0:ed0152b5c495 | 332 | // Power Levels |
| roywant | 0:ed0152b5c495 | 333 | memcpy(params.radioTxPowerLevels, radioTxPowerLevels, sizeof(PowerLevels_t)); |
| roywant | 0:ed0152b5c495 | 334 | memcpy(params.advTxPowerLevels, advTxPowerLevels, sizeof(PowerLevels_t)); |
| roywant | 0:ed0152b5c495 | 335 | // Slot Power Levels |
| roywant | 0:ed0152b5c495 | 336 | memcpy(params.slotRadioTxPowerLevels, slotRadioTxPowerLevels, sizeof(MAX_ADV_SLOTS)); |
| roywant | 0:ed0152b5c495 | 337 | memcpy(params.slotAdvTxPowerLevels, slotAdvTxPowerLevels, sizeof(MAX_ADV_SLOTS)); |
| roywant | 0:ed0152b5c495 | 338 | // Lock |
| roywant | 0:ed0152b5c495 | 339 | params.lockState = lockState; |
| roywant | 0:ed0152b5c495 | 340 | memcpy(params.unlockKey, unlockKey, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 341 | memcpy(params.unlockToken, unlockToken, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 342 | memcpy(params.challenge, challenge, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 343 | // Slots |
| roywant | 0:ed0152b5c495 | 344 | memcpy(params.slotFrameTypes, slotFrameTypes, sizeof(SlotFrameTypes_t)); |
| roywant | 0:ed0152b5c495 | 345 | memcpy(params.slotStorage, slotStorage, sizeof(SlotStorage_t)); |
| roywant | 0:ed0152b5c495 | 346 | memcpy(params.slotEidRotationPeriodExps, slotEidRotationPeriodExps, sizeof(SlotEidRotationPeriodExps_t)); |
| roywant | 0:ed0152b5c495 | 347 | memcpy(params.slotEidIdentityKeys, slotEidIdentityKeys, sizeof(SlotEidIdentityKeys_t)); |
| roywant | 0:ed0152b5c495 | 348 | // Testing and Management |
| roywant | 0:ed0152b5c495 | 349 | params.remainConnectable = remainConnectable; |
| roywant | 0:ed0152b5c495 | 350 | } |
| roywant | 0:ed0152b5c495 | 351 | |
| roywant | 0:ed0152b5c495 | 352 | void EddystoneService::swapAdvertisedFrame(int slot) |
| roywant | 0:ed0152b5c495 | 353 | { |
| roywant | 0:ed0152b5c495 | 354 | uint8_t* frame = slotToFrame(slot); |
| roywant | 0:ed0152b5c495 | 355 | uint8_t frameType = slotFrameTypes[slot]; |
| roywant | 0:ed0152b5c495 | 356 | uint32_t timeSecs = timeSinceBootTimer.read_ms() / 1000; |
| roywant | 0:ed0152b5c495 | 357 | switch (frameType) { |
| roywant | 0:ed0152b5c495 | 358 | case EDDYSTONE_FRAME_UID: |
| roywant | 0:ed0152b5c495 | 359 | updateAdvertisementPacket(uidFrame.getAdvFrame(frame), uidFrame.getAdvFrameLength(frame)); |
| roywant | 0:ed0152b5c495 | 360 | break; |
| roywant | 0:ed0152b5c495 | 361 | case EDDYSTONE_FRAME_URL: |
| roywant | 0:ed0152b5c495 | 362 | updateAdvertisementPacket(urlFrame.getAdvFrame(frame), urlFrame.getAdvFrameLength(frame)); |
| roywant | 0:ed0152b5c495 | 363 | break; |
| roywant | 0:ed0152b5c495 | 364 | case EDDYSTONE_FRAME_TLM: |
| roywant | 0:ed0152b5c495 | 365 | updateRawTLMFrame(frame); |
| roywant | 0:ed0152b5c495 | 366 | updateAdvertisementPacket(tlmFrame.getAdvFrame(frame), tlmFrame.getAdvFrameLength(frame)); |
| roywant | 0:ed0152b5c495 | 367 | break; |
| roywant | 0:ed0152b5c495 | 368 | case EDDYSTONE_FRAME_EID: |
| roywant | 0:ed0152b5c495 | 369 | // only update the frame if the rotation period is due |
| roywant | 0:ed0152b5c495 | 370 | if (timeSecs >= slotEidNextRotationTimes[slot]) { |
| roywant | 0:ed0152b5c495 | 371 | eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSecs); |
| roywant | 0:ed0152b5c495 | 372 | slotEidNextRotationTimes[slot] = timeSecs + (1 << slotEidRotationPeriodExps[slot]); |
| roywant | 0:ed0152b5c495 | 373 | setRandomMacAddress(); // selects a new MAC address so the beacon is not trackable |
| roywant | 0:ed0152b5c495 | 374 | LOG(("EID ROTATED: Time=%lu\r\n", timeSecs)); |
| roywant | 0:ed0152b5c495 | 375 | } |
| roywant | 0:ed0152b5c495 | 376 | updateAdvertisementPacket(eidFrame.getAdvFrame(frame), eidFrame.getAdvFrameLength(frame)); |
| roywant | 0:ed0152b5c495 | 377 | break; |
| roywant | 0:ed0152b5c495 | 378 | default: |
| roywant | 0:ed0152b5c495 | 379 | //Some error occurred |
| roywant | 0:ed0152b5c495 | 380 | error("Frame to swap in does not specify a valid type"); |
| roywant | 0:ed0152b5c495 | 381 | break; |
| roywant | 0:ed0152b5c495 | 382 | } |
| roywant | 0:ed0152b5c495 | 383 | ble.gap().setTxPower(slotRadioTxPowerLevels[slot]); |
| roywant | 0:ed0152b5c495 | 384 | } |
| roywant | 0:ed0152b5c495 | 385 | |
| roywant | 0:ed0152b5c495 | 386 | |
| roywant | 0:ed0152b5c495 | 387 | /* Helper function that calls user-defined functions to update Battery Voltage and Temperature (if available), |
| roywant | 0:ed0152b5c495 | 388 | * then updates the raw frame data and finally updates the actual advertised packet. This operation must be |
| roywant | 0:ed0152b5c495 | 389 | * done fairly often because the TLM frame TimeSinceBoot must have a 0.1 secs resolution according to the |
| roywant | 0:ed0152b5c495 | 390 | * Eddystone specification. |
| roywant | 0:ed0152b5c495 | 391 | */ |
| roywant | 0:ed0152b5c495 | 392 | void EddystoneService::updateRawTLMFrame(uint8_t* frame) |
| roywant | 0:ed0152b5c495 | 393 | { |
| roywant | 0:ed0152b5c495 | 394 | if (tlmBeaconTemperatureCallback != NULL) { |
| roywant | 0:ed0152b5c495 | 395 | tlmFrame.updateBeaconTemperature((*tlmBeaconTemperatureCallback)(tlmFrame.getBeaconTemperature())); |
| roywant | 0:ed0152b5c495 | 396 | } |
| roywant | 0:ed0152b5c495 | 397 | if (tlmBatteryVoltageCallback != NULL) { |
| roywant | 0:ed0152b5c495 | 398 | tlmFrame.updateBatteryVoltage((*tlmBatteryVoltageCallback)(tlmFrame.getBatteryVoltage())); |
| roywant | 0:ed0152b5c495 | 399 | } |
| roywant | 0:ed0152b5c495 | 400 | tlmFrame.updateTimeSinceBoot(timeSinceBootTimer.read_ms()); |
| roywant | 0:ed0152b5c495 | 401 | tlmFrame.setData(frame); |
| roywant | 0:ed0152b5c495 | 402 | int slot = getEidSlot(); |
| roywant | 0:ed0152b5c495 | 403 | LOG(("TLMHelper Method slot=%d\r\n", slot)); |
| roywant | 0:ed0152b5c495 | 404 | if (slot != NO_EID_SLOT_SET) { |
| roywant | 0:ed0152b5c495 | 405 | LOG(("TLMHelper: Before Encrypting TLM\r\n")); |
| roywant | 0:ed0152b5c495 | 406 | tlmFrame.encryptData(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000); |
| roywant | 0:ed0152b5c495 | 407 | LOG(("TLMHelper: Before Encrypting TLM\r\n")); |
| roywant | 0:ed0152b5c495 | 408 | } |
| roywant | 0:ed0152b5c495 | 409 | } |
| roywant | 0:ed0152b5c495 | 410 | |
| roywant | 0:ed0152b5c495 | 411 | void EddystoneService::updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength) |
| roywant | 0:ed0152b5c495 | 412 | { |
| roywant | 0:ed0152b5c495 | 413 | ble.gap().clearAdvertisingPayload(); |
| roywant | 0:ed0152b5c495 | 414 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
| roywant | 0:ed0152b5c495 | 415 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID)); |
| roywant | 0:ed0152b5c495 | 416 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, rawFrameLength); |
| roywant | 0:ed0152b5c495 | 417 | } |
| roywant | 0:ed0152b5c495 | 418 | |
| roywant | 0:ed0152b5c495 | 419 | uint8_t* EddystoneService::slotToFrame(int slot) |
| roywant | 0:ed0152b5c495 | 420 | { |
| roywant | 0:ed0152b5c495 | 421 | return reinterpret_cast<uint8_t *>(&slotStorage[slot * sizeof(Slot_t)]); |
| roywant | 0:ed0152b5c495 | 422 | } |
| roywant | 0:ed0152b5c495 | 423 | |
| roywant | 0:ed0152b5c495 | 424 | void EddystoneService::enqueueFrame(int slot) |
| roywant | 0:ed0152b5c495 | 425 | { |
| roywant | 0:ed0152b5c495 | 426 | advFrameQueue.push(slot); |
| roywant | 0:ed0152b5c495 | 427 | if (!radioManagerCallbackHandle) { |
| roywant | 0:ed0152b5c495 | 428 | /* Advertising stopped and there is not callback posted in the event queue. Just |
| roywant | 0:ed0152b5c495 | 429 | * execute the manager to resume advertising */ |
| roywant | 0:ed0152b5c495 | 430 | manageRadio(); |
| roywant | 0:ed0152b5c495 | 431 | } |
| roywant | 0:ed0152b5c495 | 432 | } |
| roywant | 0:ed0152b5c495 | 433 | |
| roywant | 0:ed0152b5c495 | 434 | void EddystoneService::manageRadio(void) |
| roywant | 0:ed0152b5c495 | 435 | { |
| roywant | 0:ed0152b5c495 | 436 | uint8_t slot; |
| roywant | 0:ed0152b5c495 | 437 | uint32_t startTimeManageRadio = timeSinceBootTimer.read_ms(); |
| roywant | 0:ed0152b5c495 | 438 | |
| roywant | 0:ed0152b5c495 | 439 | /* Signal that there is currently no callback posted */ |
| roywant | 0:ed0152b5c495 | 440 | radioManagerCallbackHandle = NULL; |
| roywant | 0:ed0152b5c495 | 441 | |
| roywant | 0:ed0152b5c495 | 442 | if (advFrameQueue.pop(slot)) { |
| roywant | 0:ed0152b5c495 | 443 | /* We have something to advertise */ |
| roywant | 0:ed0152b5c495 | 444 | if (ble.gap().getState().advertising) { |
| roywant | 0:ed0152b5c495 | 445 | ble.gap().stopAdvertising(); |
| roywant | 0:ed0152b5c495 | 446 | } |
| roywant | 0:ed0152b5c495 | 447 | swapAdvertisedFrame(slot); |
| roywant | 0:ed0152b5c495 | 448 | ble.gap().startAdvertising(); |
| roywant | 0:ed0152b5c495 | 449 | |
| roywant | 0:ed0152b5c495 | 450 | /* Increase the advertised packet count in TLM frame */ |
| roywant | 0:ed0152b5c495 | 451 | tlmFrame.updatePduCount(); |
| roywant | 0:ed0152b5c495 | 452 | |
| roywant | 0:ed0152b5c495 | 453 | /* Post a callback to itself to stop the advertisement or pop the next |
| roywant | 0:ed0152b5c495 | 454 | * frame from the queue. However, take into account the time taken to |
| roywant | 0:ed0152b5c495 | 455 | * swap in this frame. */ |
| roywant | 0:ed0152b5c495 | 456 | radioManagerCallbackHandle = eventQueue.post_in( |
| roywant | 0:ed0152b5c495 | 457 | &EddystoneService::manageRadio, this, |
| roywant | 0:ed0152b5c495 | 458 | ble.gap().getMinNonConnectableAdvertisingInterval() - (timeSinceBootTimer.read_ms() - startTimeManageRadio) /* ms */ |
| roywant | 0:ed0152b5c495 | 459 | ); |
| roywant | 0:ed0152b5c495 | 460 | } else if (ble.gap().getState().advertising) { |
| roywant | 0:ed0152b5c495 | 461 | /* Nothing else to advertise, stop advertising and do not schedule any callbacks */ |
| roywant | 0:ed0152b5c495 | 462 | ble.gap().stopAdvertising(); |
| roywant | 0:ed0152b5c495 | 463 | } |
| roywant | 0:ed0152b5c495 | 464 | } |
| roywant | 0:ed0152b5c495 | 465 | |
| roywant | 0:ed0152b5c495 | 466 | void EddystoneService::startEddystoneConfigService(void) |
| roywant | 0:ed0152b5c495 | 467 | { |
| roywant | 0:ed0152b5c495 | 468 | uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]); |
| roywant | 0:ed0152b5c495 | 469 | int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot]; |
| roywant | 0:ed0152b5c495 | 470 | int8_t advTxPower = slotAdvTxPowerLevels[activeSlot]; |
| roywant | 0:ed0152b5c495 | 471 | uint8_t* slotData = slotToFrame(activeSlot) + 1; |
| roywant | 0:ed0152b5c495 | 472 | aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey); |
| roywant | 0:ed0152b5c495 | 473 | |
| roywant | 0:ed0152b5c495 | 474 | capabilitiesChar = new ReadOnlyArrayGattCharacteristic<uint8_t, sizeof(Capability_t)>(UUID_CAPABILITIES_CHAR, capabilities); |
| roywant | 0:ed0152b5c495 | 475 | activeSlotChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_ACTIVE_SLOT_CHAR, &activeSlot); |
| roywant | 0:ed0152b5c495 | 476 | advIntervalChar = new ReadWriteGattCharacteristic<uint16_t>(UUID_ADV_INTERVAL_CHAR, &beAdvInterval); |
| roywant | 0:ed0152b5c495 | 477 | radioTxPowerChar = new ReadWriteGattCharacteristic<int8_t>(UUID_RADIO_TX_POWER_CHAR, &radioTxPower); |
| roywant | 0:ed0152b5c495 | 478 | advTxPowerChar = new ReadWriteGattCharacteristic<int8_t>(UUID_ADV_TX_POWER_CHAR, &advTxPower); |
| roywant | 0:ed0152b5c495 | 479 | lockStateChar = new GattCharacteristic(UUID_LOCK_STATE_CHAR, &lockState, sizeof(uint8_t), sizeof(LockState_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); |
| roywant | 0:ed0152b5c495 | 480 | unlockChar = new ReadWriteArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_UNLOCK_CHAR, unlockToken); |
| roywant | 0:ed0152b5c495 | 481 | publicEcdhKeyChar = new GattCharacteristic(UUID_PUBLIC_ECDH_KEY_CHAR, publicEcdhKey, 0, sizeof(PublicEcdhKey_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); |
| roywant | 0:ed0152b5c495 | 482 | eidIdentityKeyChar = new GattCharacteristic(UUID_EID_IDENTITY_KEY_CHAR, encryptedEidIdentityKey, 0, sizeof(EidIdentityKey_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); |
| roywant | 0:ed0152b5c495 | 483 | advSlotDataChar = new GattCharacteristic(UUID_ADV_SLOT_DATA_CHAR, slotData, 0, 34, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); |
| roywant | 0:ed0152b5c495 | 484 | factoryResetChar = new WriteOnlyGattCharacteristic<uint8_t>(UUID_FACTORY_RESET_CHAR, &factoryReset); |
| roywant | 0:ed0152b5c495 | 485 | remainConnectableChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_REMAIN_CONNECTABLE_CHAR, &remainConnectable); |
| roywant | 0:ed0152b5c495 | 486 | |
| roywant | 0:ed0152b5c495 | 487 | // CHAR-1 capabilities (READ ONLY) |
| roywant | 0:ed0152b5c495 | 488 | capabilitiesChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 489 | // CHAR-2 Active Slot |
| roywant | 0:ed0152b5c495 | 490 | activeSlotChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 491 | activeSlotChar->setWriteAuthorizationCallback(this, &EddystoneService::writeActiveSlotAuthorizationCallback<uint8_t>); |
| roywant | 0:ed0152b5c495 | 492 | // CHAR-3 Adv Interval |
| roywant | 0:ed0152b5c495 | 493 | advIntervalChar->setReadAuthorizationCallback(this, &EddystoneService::readAdvIntervalAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 494 | advIntervalChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint16_t>); |
| roywant | 0:ed0152b5c495 | 495 | // CHAR-4 Radio TX Power |
| roywant | 0:ed0152b5c495 | 496 | radioTxPowerChar->setReadAuthorizationCallback(this, &EddystoneService::readRadioTxPowerAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 497 | radioTxPowerChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint8_t>); |
| roywant | 0:ed0152b5c495 | 498 | // CHAR-5 |
| roywant | 0:ed0152b5c495 | 499 | advTxPowerChar->setReadAuthorizationCallback(this, &EddystoneService::readAdvTxPowerAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 500 | advTxPowerChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint8_t>); |
| roywant | 0:ed0152b5c495 | 501 | // CHAR-6 Lock State |
| roywant | 0:ed0152b5c495 | 502 | lockStateChar->setWriteAuthorizationCallback(this, &EddystoneService::writeLockStateAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 503 | // CHAR-7 Unlock |
| roywant | 0:ed0152b5c495 | 504 | unlockChar->setReadAuthorizationCallback(this, &EddystoneService::readUnlockAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 505 | unlockChar->setWriteAuthorizationCallback(this, &EddystoneService::writeUnlockAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 506 | // CHAR-8 Public Ecdh Key (READ ONLY) |
| roywant | 0:ed0152b5c495 | 507 | publicEcdhKeyChar->setReadAuthorizationCallback(this, &EddystoneService::readPublicEcdhKeyAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 508 | // CHAR-9 EID Identity Key (READ ONLY) |
| roywant | 0:ed0152b5c495 | 509 | eidIdentityKeyChar->setReadAuthorizationCallback(this, &EddystoneService::readEidIdentityAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 510 | // CHAR-10 Adv Slot Data |
| roywant | 0:ed0152b5c495 | 511 | advSlotDataChar->setReadAuthorizationCallback(this, &EddystoneService::readDataAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 512 | advSlotDataChar->setWriteAuthorizationCallback(this, &EddystoneService::writeVarLengthDataAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 513 | // CHAR-11 Factory Reset |
| roywant | 0:ed0152b5c495 | 514 | factoryResetChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 515 | factoryResetChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<bool>); |
| roywant | 0:ed0152b5c495 | 516 | // CHAR-12 Remain Connectable |
| roywant | 0:ed0152b5c495 | 517 | remainConnectableChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback); |
| roywant | 0:ed0152b5c495 | 518 | remainConnectableChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<bool>); |
| roywant | 0:ed0152b5c495 | 519 | |
| roywant | 0:ed0152b5c495 | 520 | // Create pointers to all characteristics in the GATT service |
| roywant | 0:ed0152b5c495 | 521 | charTable[0] = capabilitiesChar; |
| roywant | 0:ed0152b5c495 | 522 | charTable[1] = activeSlotChar; |
| roywant | 0:ed0152b5c495 | 523 | charTable[2] = advIntervalChar; |
| roywant | 0:ed0152b5c495 | 524 | charTable[3] = radioTxPowerChar; |
| roywant | 0:ed0152b5c495 | 525 | charTable[4] = advTxPowerChar; |
| roywant | 0:ed0152b5c495 | 526 | charTable[5] = lockStateChar; |
| roywant | 0:ed0152b5c495 | 527 | charTable[6] = unlockChar; |
| roywant | 0:ed0152b5c495 | 528 | charTable[7] = publicEcdhKeyChar; |
| roywant | 0:ed0152b5c495 | 529 | charTable[8] = eidIdentityKeyChar; |
| roywant | 0:ed0152b5c495 | 530 | charTable[9] = advSlotDataChar; |
| roywant | 0:ed0152b5c495 | 531 | charTable[10] = factoryResetChar; |
| roywant | 0:ed0152b5c495 | 532 | charTable[11] = remainConnectableChar; |
| roywant | 0:ed0152b5c495 | 533 | |
| roywant | 0:ed0152b5c495 | 534 | GattService configService(UUID_ES_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); |
| roywant | 0:ed0152b5c495 | 535 | |
| roywant | 0:ed0152b5c495 | 536 | ble.gattServer().addService(configService); |
| roywant | 0:ed0152b5c495 | 537 | ble.gattServer().onDataWritten(this, &EddystoneService::onDataWrittenCallback); |
| roywant | 0:ed0152b5c495 | 538 | updateCharacteristicValues(); |
| roywant | 0:ed0152b5c495 | 539 | } |
| roywant | 0:ed0152b5c495 | 540 | |
| roywant | 0:ed0152b5c495 | 541 | |
| roywant | 0:ed0152b5c495 | 542 | void EddystoneService::freeConfigCharacteristics(void) |
| roywant | 0:ed0152b5c495 | 543 | { |
| roywant | 0:ed0152b5c495 | 544 | delete capabilitiesChar; |
| roywant | 0:ed0152b5c495 | 545 | delete activeSlotChar; |
| roywant | 0:ed0152b5c495 | 546 | delete advIntervalChar; |
| roywant | 0:ed0152b5c495 | 547 | delete radioTxPowerChar; |
| roywant | 0:ed0152b5c495 | 548 | delete advTxPowerChar; |
| roywant | 0:ed0152b5c495 | 549 | delete lockStateChar; |
| roywant | 0:ed0152b5c495 | 550 | delete unlockChar; |
| roywant | 0:ed0152b5c495 | 551 | delete publicEcdhKeyChar; |
| roywant | 0:ed0152b5c495 | 552 | delete eidIdentityKeyChar; |
| roywant | 0:ed0152b5c495 | 553 | delete advSlotDataChar; |
| roywant | 0:ed0152b5c495 | 554 | delete factoryResetChar; |
| roywant | 0:ed0152b5c495 | 555 | delete remainConnectableChar; |
| roywant | 0:ed0152b5c495 | 556 | } |
| roywant | 0:ed0152b5c495 | 557 | |
| roywant | 0:ed0152b5c495 | 558 | void EddystoneService::stopEddystoneBeaconAdvertisements(void) |
| roywant | 0:ed0152b5c495 | 559 | { |
| roywant | 0:ed0152b5c495 | 560 | /* Unschedule callbacks */ |
| roywant | 0:ed0152b5c495 | 561 | |
| roywant | 0:ed0152b5c495 | 562 | for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) { |
| roywant | 0:ed0152b5c495 | 563 | if (slotCallbackHandles[slot]) { |
| roywant | 0:ed0152b5c495 | 564 | eventQueue.cancel(slotCallbackHandles[slot]); |
| roywant | 0:ed0152b5c495 | 565 | slotCallbackHandles[slot] = NULL; |
| roywant | 0:ed0152b5c495 | 566 | } |
| roywant | 0:ed0152b5c495 | 567 | } |
| roywant | 0:ed0152b5c495 | 568 | |
| roywant | 0:ed0152b5c495 | 569 | if (radioManagerCallbackHandle) { |
| roywant | 0:ed0152b5c495 | 570 | eventQueue.cancel(radioManagerCallbackHandle); |
| roywant | 0:ed0152b5c495 | 571 | radioManagerCallbackHandle = NULL; |
| roywant | 0:ed0152b5c495 | 572 | } |
| roywant | 0:ed0152b5c495 | 573 | |
| roywant | 0:ed0152b5c495 | 574 | /* Stop any current Advs (ES Config or Beacon) */ |
| roywant | 0:ed0152b5c495 | 575 | BLE::Instance().gap().stopAdvertising(); |
| roywant | 0:ed0152b5c495 | 576 | } |
| roywant | 0:ed0152b5c495 | 577 | |
| roywant | 0:ed0152b5c495 | 578 | /* |
| roywant | 0:ed0152b5c495 | 579 | * Internal helper function used to update the GATT database following any |
| roywant | 0:ed0152b5c495 | 580 | * change to the internal state of the service object. |
| roywant | 0:ed0152b5c495 | 581 | */ |
| roywant | 0:ed0152b5c495 | 582 | void EddystoneService::updateCharacteristicValues(void) |
| roywant | 0:ed0152b5c495 | 583 | { |
| roywant | 0:ed0152b5c495 | 584 | // Init variables for update |
| roywant | 0:ed0152b5c495 | 585 | uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]); |
| roywant | 0:ed0152b5c495 | 586 | int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot]; |
| roywant | 0:ed0152b5c495 | 587 | int8_t advTxPower = slotAdvTxPowerLevels[activeSlot]; |
| roywant | 0:ed0152b5c495 | 588 | uint8_t* frame = slotToFrame(activeSlot); |
| roywant | 0:ed0152b5c495 | 589 | uint8_t slotLength = 0; |
| roywant | 0:ed0152b5c495 | 590 | uint8_t* slotData = NULL; |
| roywant | 0:ed0152b5c495 | 591 | memset(encryptedEidIdentityKey, 0, sizeof(encryptedEidIdentityKey)); |
| roywant | 0:ed0152b5c495 | 592 | |
| roywant | 0:ed0152b5c495 | 593 | switch(slotFrameTypes[activeSlot]) { |
| roywant | 0:ed0152b5c495 | 594 | case EDDYSTONE_FRAME_UID: |
| roywant | 0:ed0152b5c495 | 595 | slotLength = uidFrame.getDataLength(frame); |
| roywant | 0:ed0152b5c495 | 596 | slotData = uidFrame.getData(frame); |
| roywant | 0:ed0152b5c495 | 597 | break; |
| roywant | 0:ed0152b5c495 | 598 | case EDDYSTONE_FRAME_URL: |
| roywant | 0:ed0152b5c495 | 599 | slotLength = urlFrame.getDataLength(frame); |
| roywant | 0:ed0152b5c495 | 600 | slotData = urlFrame.getData(frame); |
| roywant | 0:ed0152b5c495 | 601 | break; |
| roywant | 0:ed0152b5c495 | 602 | case EDDYSTONE_FRAME_TLM: |
| roywant | 0:ed0152b5c495 | 603 | updateRawTLMFrame(frame); |
| roywant | 0:ed0152b5c495 | 604 | slotLength = tlmFrame.getDataLength(frame); |
| roywant | 0:ed0152b5c495 | 605 | slotData = tlmFrame.getData(frame); |
| roywant | 0:ed0152b5c495 | 606 | break; |
| roywant | 0:ed0152b5c495 | 607 | case EDDYSTONE_FRAME_EID: |
| roywant | 0:ed0152b5c495 | 608 | slotLength = eidFrame.getDataLength(frame); |
| roywant | 0:ed0152b5c495 | 609 | slotData = eidFrame.getData(frame); |
| roywant | 0:ed0152b5c495 | 610 | aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey); |
| roywant | 0:ed0152b5c495 | 611 | break; |
| roywant | 0:ed0152b5c495 | 612 | } |
| roywant | 0:ed0152b5c495 | 613 | |
| roywant | 0:ed0152b5c495 | 614 | ble.gattServer().write(capabilitiesChar->getValueHandle(), reinterpret_cast<uint8_t *>(capabilities), sizeof(Capability_t)); |
| roywant | 0:ed0152b5c495 | 615 | ble.gattServer().write(activeSlotChar->getValueHandle(), &activeSlot, sizeof(uint8_t)); |
| roywant | 0:ed0152b5c495 | 616 | ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t)); |
| roywant | 0:ed0152b5c495 | 617 | ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t)); |
| roywant | 0:ed0152b5c495 | 618 | ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t)); |
| roywant | 0:ed0152b5c495 | 619 | ble.gattServer().write(lockStateChar->getValueHandle(), &lockState, sizeof(uint8_t)); |
| roywant | 0:ed0152b5c495 | 620 | ble.gattServer().write(unlockChar->getValueHandle(), unlockToken, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 621 | ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(publicEcdhKey), sizeof(PublicEcdhKey_t)); |
| roywant | 0:ed0152b5c495 | 622 | ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(encryptedEidIdentityKey), sizeof(EidIdentityKey_t)); |
| roywant | 0:ed0152b5c495 | 623 | ble.gattServer().write(advSlotDataChar->getValueHandle(), slotData, slotLength); |
| roywant | 0:ed0152b5c495 | 624 | ble.gattServer().write(factoryResetChar->getValueHandle(), &factoryReset, sizeof(uint8_t)); |
| roywant | 0:ed0152b5c495 | 625 | ble.gattServer().write(remainConnectableChar->getValueHandle(), &remainConnectable, sizeof(uint8_t)); |
| roywant | 0:ed0152b5c495 | 626 | } |
| roywant | 0:ed0152b5c495 | 627 | |
| roywant | 0:ed0152b5c495 | 628 | EddystoneService::EddystoneError_t EddystoneService::startEddystoneConfigAdvertisements(void) |
| roywant | 0:ed0152b5c495 | 629 | { |
| roywant | 0:ed0152b5c495 | 630 | stopEddystoneBeaconAdvertisements(); |
| roywant | 0:ed0152b5c495 | 631 | |
| roywant | 0:ed0152b5c495 | 632 | if (advConfigInterval == 0) { |
| roywant | 0:ed0152b5c495 | 633 | // Nothing to do, the advertisement interval is 0 |
| roywant | 0:ed0152b5c495 | 634 | return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL; |
| roywant | 0:ed0152b5c495 | 635 | } |
| roywant | 0:ed0152b5c495 | 636 | |
| roywant | 0:ed0152b5c495 | 637 | operationMode = EDDYSTONE_MODE_CONFIG; |
| roywant | 0:ed0152b5c495 | 638 | |
| roywant | 0:ed0152b5c495 | 639 | ble.gap().clearAdvertisingPayload(); |
| roywant | 0:ed0152b5c495 | 640 | |
| roywant | 0:ed0152b5c495 | 641 | /* Accumulate the new payload */ |
| roywant | 0:ed0152b5c495 | 642 | ble.gap().accumulateAdvertisingPayload( |
| roywant | 0:ed0152b5c495 | 643 | GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE |
| roywant | 0:ed0152b5c495 | 644 | ); |
| roywant | 0:ed0152b5c495 | 645 | /* UUID is in different order in the ADV frame (!) */ |
| roywant | 0:ed0152b5c495 | 646 | uint8_t reversedServiceUUID[sizeof(UUID_ES_BEACON_SERVICE)]; |
| roywant | 0:ed0152b5c495 | 647 | for (size_t i = 0; i < sizeof(UUID_ES_BEACON_SERVICE); i++) { |
| roywant | 0:ed0152b5c495 | 648 | reversedServiceUUID[i] = UUID_ES_BEACON_SERVICE[sizeof(UUID_ES_BEACON_SERVICE) - i - 1]; |
| roywant | 0:ed0152b5c495 | 649 | } |
| roywant | 0:ed0152b5c495 | 650 | ble.gap().accumulateAdvertisingPayload( |
| roywant | 0:ed0152b5c495 | 651 | GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, |
| roywant | 0:ed0152b5c495 | 652 | reversedServiceUUID, |
| roywant | 0:ed0152b5c495 | 653 | sizeof(reversedServiceUUID) |
| roywant | 0:ed0152b5c495 | 654 | ); |
| roywant | 0:ed0152b5c495 | 655 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG); |
| roywant | 0:ed0152b5c495 | 656 | setupEddystoneConfigScanResponse(); |
| roywant | 0:ed0152b5c495 | 657 | |
| roywant | 0:ed0152b5c495 | 658 | ble.gap().setTxPower(radioTxPowerLevels[sizeof(PowerLevels_t)-1]); // Max Power for Config |
| roywant | 0:ed0152b5c495 | 659 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
| roywant | 0:ed0152b5c495 | 660 | ble.gap().setAdvertisingInterval(advConfigInterval); |
| roywant | 0:ed0152b5c495 | 661 | ble.gap().startAdvertising(); |
| roywant | 0:ed0152b5c495 | 662 | |
| roywant | 0:ed0152b5c495 | 663 | return EDDYSTONE_ERROR_NONE; |
| roywant | 0:ed0152b5c495 | 664 | } |
| roywant | 0:ed0152b5c495 | 665 | |
| roywant | 0:ed0152b5c495 | 666 | void EddystoneService::setupEddystoneConfigScanResponse(void) |
| roywant | 0:ed0152b5c495 | 667 | { |
| roywant | 0:ed0152b5c495 | 668 | ble.gap().clearScanResponse(); |
| roywant | 0:ed0152b5c495 | 669 | ble.gap().accumulateScanResponse( |
| roywant | 0:ed0152b5c495 | 670 | GapAdvertisingData::COMPLETE_LOCAL_NAME, |
| roywant | 0:ed0152b5c495 | 671 | reinterpret_cast<const uint8_t *>(deviceName), |
| roywant | 0:ed0152b5c495 | 672 | strlen(deviceName) |
| roywant | 0:ed0152b5c495 | 673 | ); |
| roywant | 0:ed0152b5c495 | 674 | ble.gap().accumulateScanResponse( |
| roywant | 0:ed0152b5c495 | 675 | GapAdvertisingData::TX_POWER_LEVEL, |
| roywant | 0:ed0152b5c495 | 676 | reinterpret_cast<uint8_t *>(&advTxPowerLevels[sizeof(PowerLevels_t)-1]), |
| roywant | 0:ed0152b5c495 | 677 | sizeof(uint8_t) |
| roywant | 0:ed0152b5c495 | 678 | ); |
| roywant | 0:ed0152b5c495 | 679 | } |
| roywant | 0:ed0152b5c495 | 680 | |
| roywant | 0:ed0152b5c495 | 681 | /* WRITE AUTHORIZATION */ |
| roywant | 0:ed0152b5c495 | 682 | |
| roywant | 0:ed0152b5c495 | 683 | void EddystoneService::writeUnlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 684 | { |
| roywant | 0:ed0152b5c495 | 685 | if (lockState == UNLOCKED) { |
| roywant | 0:ed0152b5c495 | 686 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 687 | } else if (authParams->len != sizeof(Lock_t)) { |
| roywant | 0:ed0152b5c495 | 688 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; |
| roywant | 0:ed0152b5c495 | 689 | } else if (authParams->offset != 0) { |
| roywant | 0:ed0152b5c495 | 690 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; |
| roywant | 0:ed0152b5c495 | 691 | } else if (memcmp(authParams->data, unlockToken, sizeof(Lock_t)) != 0) { |
| roywant | 0:ed0152b5c495 | 692 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 693 | } else { |
| roywant | 0:ed0152b5c495 | 694 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 695 | } |
| roywant | 0:ed0152b5c495 | 696 | } |
| roywant | 0:ed0152b5c495 | 697 | |
| roywant | 0:ed0152b5c495 | 698 | void EddystoneService::writeVarLengthDataAuthorizationCallback(GattWriteAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 699 | { |
| roywant | 0:ed0152b5c495 | 700 | if (lockState == LOCKED) { |
| roywant | 0:ed0152b5c495 | 701 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 702 | } else if ((authParams->len > 34) || (authParams->len == 0)) { |
| roywant | 0:ed0152b5c495 | 703 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; |
| roywant | 0:ed0152b5c495 | 704 | } else { |
| roywant | 0:ed0152b5c495 | 705 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 706 | } |
| roywant | 0:ed0152b5c495 | 707 | } |
| roywant | 0:ed0152b5c495 | 708 | |
| roywant | 0:ed0152b5c495 | 709 | |
| roywant | 0:ed0152b5c495 | 710 | void EddystoneService::writeLockStateAuthorizationCallback(GattWriteAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 711 | { |
| roywant | 0:ed0152b5c495 | 712 | if (lockState == LOCKED) { |
| roywant | 0:ed0152b5c495 | 713 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 714 | } else if ((authParams->len != sizeof(uint8_t)) && (authParams->len != (sizeof(uint8_t) + sizeof(Lock_t)))) { |
| roywant | 0:ed0152b5c495 | 715 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; |
| roywant | 0:ed0152b5c495 | 716 | } else if (authParams->offset != 0) { |
| roywant | 0:ed0152b5c495 | 717 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; |
| roywant | 0:ed0152b5c495 | 718 | } else { |
| roywant | 0:ed0152b5c495 | 719 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 720 | } |
| roywant | 0:ed0152b5c495 | 721 | } |
| roywant | 0:ed0152b5c495 | 722 | |
| roywant | 0:ed0152b5c495 | 723 | template <typename T> |
| roywant | 0:ed0152b5c495 | 724 | void EddystoneService::writeBasicAuthorizationCallback(GattWriteAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 725 | { |
| roywant | 0:ed0152b5c495 | 726 | if (lockState == LOCKED) { |
| roywant | 0:ed0152b5c495 | 727 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 728 | } else if (authParams->len != sizeof(T)) { |
| roywant | 0:ed0152b5c495 | 729 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; |
| roywant | 0:ed0152b5c495 | 730 | } else if (authParams->offset != 0) { |
| roywant | 0:ed0152b5c495 | 731 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; |
| roywant | 0:ed0152b5c495 | 732 | } else { |
| roywant | 0:ed0152b5c495 | 733 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 734 | } |
| roywant | 0:ed0152b5c495 | 735 | } |
| roywant | 0:ed0152b5c495 | 736 | |
| roywant | 0:ed0152b5c495 | 737 | template <typename T> |
| roywant | 0:ed0152b5c495 | 738 | void EddystoneService::writeActiveSlotAuthorizationCallback(GattWriteAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 739 | { |
| roywant | 0:ed0152b5c495 | 740 | if (lockState == LOCKED) { |
| roywant | 0:ed0152b5c495 | 741 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 742 | } else if (authParams->len != sizeof(T)) { |
| roywant | 0:ed0152b5c495 | 743 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; |
| roywant | 0:ed0152b5c495 | 744 | } else if (*(authParams->data) > MAX_ADV_SLOTS -1) { |
| roywant | 0:ed0152b5c495 | 745 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; |
| roywant | 0:ed0152b5c495 | 746 | } else if (authParams->offset != 0) { |
| roywant | 0:ed0152b5c495 | 747 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; |
| roywant | 0:ed0152b5c495 | 748 | } else { |
| roywant | 0:ed0152b5c495 | 749 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 750 | } |
| roywant | 0:ed0152b5c495 | 751 | } |
| roywant | 0:ed0152b5c495 | 752 | |
| roywant | 0:ed0152b5c495 | 753 | /* READ AUTHORIZTION */ |
| roywant | 0:ed0152b5c495 | 754 | |
| roywant | 0:ed0152b5c495 | 755 | void EddystoneService::readBasicTestLockAuthorizationCallback(GattReadAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 756 | { |
| roywant | 0:ed0152b5c495 | 757 | LOG(("\r\nDO READ BASIC TEST LOCK slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 758 | if (lockState == LOCKED) { |
| roywant | 0:ed0152b5c495 | 759 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 760 | } else { |
| roywant | 0:ed0152b5c495 | 761 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 762 | } |
| roywant | 0:ed0152b5c495 | 763 | } |
| roywant | 0:ed0152b5c495 | 764 | |
| roywant | 0:ed0152b5c495 | 765 | void EddystoneService::readEidIdentityAuthorizationCallback(GattReadAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 766 | { |
| roywant | 0:ed0152b5c495 | 767 | LOG(("\r\nDO READ EID IDENTITY slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 768 | aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey); |
| roywant | 0:ed0152b5c495 | 769 | int sum = 0; |
| roywant | 0:ed0152b5c495 | 770 | // Test if the IdentityKey is all zeros for this slot |
| roywant | 0:ed0152b5c495 | 771 | for (uint8_t i = 0; i < sizeof(EidIdentityKey_t); i++) { |
| roywant | 0:ed0152b5c495 | 772 | sum = sum + slotEidIdentityKeys[activeSlot][i]; |
| roywant | 0:ed0152b5c495 | 773 | } |
| roywant | 0:ed0152b5c495 | 774 | ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), encryptedEidIdentityKey, sizeof(EidIdentityKey_t)); |
| roywant | 0:ed0152b5c495 | 775 | |
| roywant | 0:ed0152b5c495 | 776 | // When the array is all zeros, the key has not been set, so return fault |
| roywant | 0:ed0152b5c495 | 777 | if ((lockState == LOCKED) || (sum == 0)) { |
| roywant | 0:ed0152b5c495 | 778 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 779 | } else { |
| roywant | 0:ed0152b5c495 | 780 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 781 | } |
| roywant | 0:ed0152b5c495 | 782 | } |
| roywant | 0:ed0152b5c495 | 783 | |
| roywant | 0:ed0152b5c495 | 784 | void EddystoneService::readPublicEcdhKeyAuthorizationCallback(GattReadAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 785 | { |
| roywant | 0:ed0152b5c495 | 786 | LOG(("\r\nDO READ BEACON PUBLIC ECDH KEY (LE) slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 787 | |
| roywant | 0:ed0152b5c495 | 788 | ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), publicEcdhKeyLE, sizeof(PublicEcdhKey_t)); |
| roywant | 0:ed0152b5c495 | 789 | |
| roywant | 0:ed0152b5c495 | 790 | // When the array is all zeros, the key has not been set, so return fault |
| roywant | 0:ed0152b5c495 | 791 | if (lockState == LOCKED) { |
| roywant | 0:ed0152b5c495 | 792 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 793 | } else { |
| roywant | 0:ed0152b5c495 | 794 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 795 | } |
| roywant | 0:ed0152b5c495 | 796 | } |
| roywant | 0:ed0152b5c495 | 797 | |
| roywant | 0:ed0152b5c495 | 798 | void EddystoneService::readDataAuthorizationCallback(GattReadAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 799 | { |
| roywant | 0:ed0152b5c495 | 800 | LOG(("\r\nDO READ ADV-DATA : slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 801 | uint8_t frameType = slotFrameTypes[activeSlot]; |
| roywant | 0:ed0152b5c495 | 802 | uint8_t* frame = slotToFrame(activeSlot); |
| roywant | 0:ed0152b5c495 | 803 | uint8_t slotLength = 1; |
| roywant | 0:ed0152b5c495 | 804 | uint8_t buf[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}; |
| roywant | 0:ed0152b5c495 | 805 | uint8_t* slotData = buf; |
| roywant | 0:ed0152b5c495 | 806 | |
| roywant | 0:ed0152b5c495 | 807 | if (lockState == LOCKED) { |
| roywant | 0:ed0152b5c495 | 808 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 809 | return; |
| roywant | 0:ed0152b5c495 | 810 | } |
| roywant | 0:ed0152b5c495 | 811 | LOG(("IN READ ADV-DATA AFTER LOCK TEST frameType=%d\r\n", frameType)); |
| roywant | 0:ed0152b5c495 | 812 | if (testValidFrame(frame) ) { // Check the frame has valid data before proceeding |
| roywant | 0:ed0152b5c495 | 813 | switch(frameType) { |
| roywant | 0:ed0152b5c495 | 814 | case EDDYSTONE_FRAME_UID: |
| roywant | 0:ed0152b5c495 | 815 | LOG(("READ ADV-DATA UID SLOT DATA slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 816 | slotLength = uidFrame.getDataLength(frame); |
| roywant | 0:ed0152b5c495 | 817 | slotData = uidFrame.getData(frame); |
| roywant | 0:ed0152b5c495 | 818 | break; |
| roywant | 0:ed0152b5c495 | 819 | case EDDYSTONE_FRAME_URL: |
| roywant | 0:ed0152b5c495 | 820 | LOG(("READ ADV-DATA URL SLOT DATA slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 821 | slotLength = urlFrame.getDataLength(frame); |
| roywant | 0:ed0152b5c495 | 822 | slotData = urlFrame.getData(frame); |
| roywant | 0:ed0152b5c495 | 823 | break; |
| roywant | 0:ed0152b5c495 | 824 | case EDDYSTONE_FRAME_TLM: |
| roywant | 0:ed0152b5c495 | 825 | LOG(("READ ADV-DATA TLM SLOT DATA slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 826 | updateRawTLMFrame(frame); |
| roywant | 0:ed0152b5c495 | 827 | slotLength = tlmFrame.getDataLength(frame); |
| roywant | 0:ed0152b5c495 | 828 | slotData = tlmFrame.getData(frame); |
| roywant | 0:ed0152b5c495 | 829 | LOG(("READ ADV-DATA AFTER T/E TLM length=%d\r\n", slotLength)); |
| roywant | 0:ed0152b5c495 | 830 | LOG(("Data=")); logPrintHex(slotData, 18); |
| roywant | 0:ed0152b5c495 | 831 | break; |
| roywant | 0:ed0152b5c495 | 832 | case EDDYSTONE_FRAME_EID: |
| roywant | 0:ed0152b5c495 | 833 | LOG(("READ ADV-DATA EID SLOT DATA slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 834 | slotLength = 14; |
| roywant | 0:ed0152b5c495 | 835 | buf[0] = EIDFrame::FRAME_TYPE_EID; |
| roywant | 0:ed0152b5c495 | 836 | buf[1] = slotEidRotationPeriodExps[activeSlot]; |
| roywant | 0:ed0152b5c495 | 837 | // Add time as a big endian 32 bit number |
| roywant | 0:ed0152b5c495 | 838 | uint32_t timeSecs = timeSinceBootTimer.read_ms() / 1000; |
| roywant | 0:ed0152b5c495 | 839 | buf[2] = (timeSecs >> 24) & 0xff; |
| roywant | 0:ed0152b5c495 | 840 | buf[3] = (timeSecs >> 16) & 0xff; |
| roywant | 0:ed0152b5c495 | 841 | buf[4] = (timeSecs >> 8) & 0xff; |
| roywant | 0:ed0152b5c495 | 842 | buf[5] = timeSecs & 0xff; |
| roywant | 0:ed0152b5c495 | 843 | memcpy(buf + 6, eidFrame.getEid(frame), 8); |
| roywant | 0:ed0152b5c495 | 844 | slotData = buf; |
| roywant | 0:ed0152b5c495 | 845 | break; |
| roywant | 0:ed0152b5c495 | 846 | } |
| roywant | 0:ed0152b5c495 | 847 | } |
| roywant | 0:ed0152b5c495 | 848 | LOG(("IN READ ADV-DATA AFTER FRAME PROCESSING slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 849 | ble.gattServer().write(advSlotDataChar->getValueHandle(), slotData, slotLength); |
| roywant | 0:ed0152b5c495 | 850 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 851 | } |
| roywant | 0:ed0152b5c495 | 852 | |
| roywant | 0:ed0152b5c495 | 853 | bool EddystoneService::testValidFrame(uint8_t* frame) { |
| roywant | 0:ed0152b5c495 | 854 | return (frame[0] != 0 ) ? true : false; |
| roywant | 0:ed0152b5c495 | 855 | } |
| roywant | 0:ed0152b5c495 | 856 | |
| roywant | 0:ed0152b5c495 | 857 | void EddystoneService::readUnlockAuthorizationCallback(GattReadAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 858 | { |
| roywant | 0:ed0152b5c495 | 859 | LOG(("\r\nDO READ UNLOCK slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 860 | if (lockState == UNLOCKED) { |
| roywant | 0:ed0152b5c495 | 861 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 862 | return; |
| roywant | 0:ed0152b5c495 | 863 | } |
| roywant | 0:ed0152b5c495 | 864 | // Update the challenge ready for the characteristic read |
| roywant | 0:ed0152b5c495 | 865 | generateRandom(challenge, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 866 | aes128Encrypt(unlockKey, challenge, unlockToken); |
| roywant | 0:ed0152b5c495 | 867 | ble.gattServer().write(unlockChar->getValueHandle(), reinterpret_cast<uint8_t *>(challenge), sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 868 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 869 | } |
| roywant | 0:ed0152b5c495 | 870 | |
| roywant | 0:ed0152b5c495 | 871 | void EddystoneService::readAdvIntervalAuthorizationCallback(GattReadAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 872 | { |
| roywant | 0:ed0152b5c495 | 873 | LOG(("\r\nDO READ ADV INTERVAL slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 874 | if (lockState == LOCKED) { |
| roywant | 0:ed0152b5c495 | 875 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 876 | return; |
| roywant | 0:ed0152b5c495 | 877 | } |
| roywant | 0:ed0152b5c495 | 878 | uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]); |
| roywant | 0:ed0152b5c495 | 879 | ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t)); |
| roywant | 0:ed0152b5c495 | 880 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 881 | } |
| roywant | 0:ed0152b5c495 | 882 | |
| roywant | 0:ed0152b5c495 | 883 | void EddystoneService::readRadioTxPowerAuthorizationCallback(GattReadAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 884 | { |
| roywant | 0:ed0152b5c495 | 885 | LOG(("\r\nDO READ RADIO TXPOWER slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 886 | if (lockState == LOCKED) { |
| roywant | 0:ed0152b5c495 | 887 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 888 | return; |
| roywant | 0:ed0152b5c495 | 889 | } |
| roywant | 0:ed0152b5c495 | 890 | int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot]; |
| roywant | 0:ed0152b5c495 | 891 | ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t)); |
| roywant | 0:ed0152b5c495 | 892 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 893 | } |
| roywant | 0:ed0152b5c495 | 894 | |
| roywant | 0:ed0152b5c495 | 895 | void EddystoneService::readAdvTxPowerAuthorizationCallback(GattReadAuthCallbackParams *authParams) |
| roywant | 0:ed0152b5c495 | 896 | { |
| roywant | 0:ed0152b5c495 | 897 | LOG(("\r\nDO READ ADV TXPOWER slot=%d\r\n", activeSlot)); |
| roywant | 0:ed0152b5c495 | 898 | if (lockState == LOCKED) { |
| roywant | 0:ed0152b5c495 | 899 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED; |
| roywant | 0:ed0152b5c495 | 900 | return; |
| roywant | 0:ed0152b5c495 | 901 | } |
| roywant | 0:ed0152b5c495 | 902 | int8_t advTxPower = slotAdvTxPowerLevels[activeSlot]; |
| roywant | 0:ed0152b5c495 | 903 | ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t)); |
| roywant | 0:ed0152b5c495 | 904 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| roywant | 0:ed0152b5c495 | 905 | } |
| roywant | 0:ed0152b5c495 | 906 | |
| roywant | 0:ed0152b5c495 | 907 | /* |
| roywant | 0:ed0152b5c495 | 908 | * This callback is invoked when a GATT client attempts to modify any of the |
| roywant | 0:ed0152b5c495 | 909 | * characteristics of this service. Attempts to do so are also applied to |
| roywant | 0:ed0152b5c495 | 910 | * the internal state of this service object. |
| roywant | 0:ed0152b5c495 | 911 | */ |
| roywant | 0:ed0152b5c495 | 912 | void EddystoneService::onDataWrittenCallback(const GattWriteCallbackParams *writeParams) |
| roywant | 0:ed0152b5c495 | 913 | { |
| roywant | 0:ed0152b5c495 | 914 | uint16_t handle = writeParams->handle; |
| roywant | 0:ed0152b5c495 | 915 | LOG(("\r\nDO WRITE: Handle=%d Len=%d\r\n", handle, writeParams->len)); |
| roywant | 0:ed0152b5c495 | 916 | // CHAR-1 CAPABILITIES |
| roywant | 0:ed0152b5c495 | 917 | /* capabilitySlotChar is READ ONLY */ |
| roywant | 0:ed0152b5c495 | 918 | // CHAR-2 ACTIVE SLOT |
| roywant | 0:ed0152b5c495 | 919 | if (handle == activeSlotChar->getValueHandle()) { |
| roywant | 0:ed0152b5c495 | 920 | LOG(("Write: Active Slot Handle=%d\r\n", handle)); |
| roywant | 0:ed0152b5c495 | 921 | uint8_t slot = *(writeParams->data); |
| roywant | 0:ed0152b5c495 | 922 | LOG(("Active Slot=%d\r\n", slot)); |
| roywant | 0:ed0152b5c495 | 923 | // Ensure slot does not exceed limit, or set highest slot |
| roywant | 0:ed0152b5c495 | 924 | if (slot < MAX_ADV_SLOTS) { |
| roywant | 0:ed0152b5c495 | 925 | activeSlot = slot; |
| roywant | 0:ed0152b5c495 | 926 | } |
| roywant | 0:ed0152b5c495 | 927 | ble.gattServer().write(activeSlotChar->getValueHandle(), &activeSlot, sizeof(uint8_t)); |
| roywant | 0:ed0152b5c495 | 928 | // CHAR-3 ADV INTERVAL |
| roywant | 0:ed0152b5c495 | 929 | } else if (handle == advIntervalChar->getValueHandle()) { |
| roywant | 0:ed0152b5c495 | 930 | LOG(("Write: Interval Handle=%d\r\n", handle)); |
| roywant | 0:ed0152b5c495 | 931 | uint16_t interval = correctAdvertisementPeriod(swapEndian(*((uint16_t *)(writeParams->data)))); |
| roywant | 0:ed0152b5c495 | 932 | slotAdvIntervals[activeSlot] = interval; // Store this value for reading |
| roywant | 0:ed0152b5c495 | 933 | uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]); |
| roywant | 0:ed0152b5c495 | 934 | ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t)); |
| roywant | 0:ed0152b5c495 | 935 | // CHAR-4 RADIO TX POWER |
| roywant | 0:ed0152b5c495 | 936 | } else if (handle == radioTxPowerChar->getValueHandle()) { |
| roywant | 0:ed0152b5c495 | 937 | LOG(("Write: RADIO Power Handle=%d\r\n", handle)); |
| roywant | 0:ed0152b5c495 | 938 | int8_t radioTxPower = *(writeParams->data); |
| roywant | 0:ed0152b5c495 | 939 | uint8_t index = radioTxPowerToIndex(radioTxPower); |
| roywant | 0:ed0152b5c495 | 940 | radioTxPower = radioTxPowerLevels[index]; // Power now corrected to nearest allowed power |
| roywant | 0:ed0152b5c495 | 941 | slotRadioTxPowerLevels[activeSlot] = radioTxPower; // Store by slot number |
| roywant | 0:ed0152b5c495 | 942 | int8_t advTxPower = advTxPowerLevels[index]; // Determine adv power equivalent |
| roywant | 0:ed0152b5c495 | 943 | slotAdvTxPowerLevels[activeSlot] = advTxPower; |
| roywant | 0:ed0152b5c495 | 944 | setFrameTxPower(activeSlot, advTxPower); // Set the actual frame radio TxPower for this slot |
| roywant | 0:ed0152b5c495 | 945 | ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t)); |
| roywant | 0:ed0152b5c495 | 946 | // CHAR-5 ADV TX POWER |
| roywant | 0:ed0152b5c495 | 947 | } else if (handle == advTxPowerChar->getValueHandle()) { |
| roywant | 0:ed0152b5c495 | 948 | LOG(("Write: ADV Power Handle=%d\r\n", handle)); |
| roywant | 0:ed0152b5c495 | 949 | int8_t advTxPower = *(writeParams->data); |
| roywant | 0:ed0152b5c495 | 950 | slotAdvTxPowerLevels[activeSlot] = advTxPower; |
| roywant | 0:ed0152b5c495 | 951 | setFrameTxPower(activeSlot, advTxPower); // Update the actual frame Adv TxPower for this slot |
| roywant | 0:ed0152b5c495 | 952 | ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t)); |
| roywant | 0:ed0152b5c495 | 953 | // CHAR-6 LOCK STATE |
| roywant | 0:ed0152b5c495 | 954 | } else if (handle == lockStateChar->getValueHandle()) { |
| roywant | 0:ed0152b5c495 | 955 | LOG(("Write: Lock State Handle=%d\r\n", handle)); |
| roywant | 0:ed0152b5c495 | 956 | uint8_t newLockState = *(writeParams->data); |
| roywant | 0:ed0152b5c495 | 957 | if ((writeParams->len == sizeof(uint8_t)) || (writeParams->len == sizeof(uint8_t) + sizeof(Lock_t))) { |
| roywant | 0:ed0152b5c495 | 958 | if ((newLockState == LOCKED) || (newLockState == UNLOCKED) || (newLockState == UNLOCKED_AUTO_RELOCK_DISABLED)) { |
| roywant | 0:ed0152b5c495 | 959 | lockState = newLockState; |
| roywant | 0:ed0152b5c495 | 960 | } |
| roywant | 0:ed0152b5c495 | 961 | } |
| roywant | 0:ed0152b5c495 | 962 | if ((newLockState == LOCKED) && (writeParams->len == (sizeof(uint8_t) + sizeof(Lock_t))) ) { |
| roywant | 0:ed0152b5c495 | 963 | // And sets the new secret lock code if present |
| roywant | 0:ed0152b5c495 | 964 | uint8_t encryptedNewKey[sizeof(Lock_t)]; |
| roywant | 0:ed0152b5c495 | 965 | uint8_t newKey[sizeof(Lock_t)]; |
| roywant | 0:ed0152b5c495 | 966 | memcpy(encryptedNewKey, (writeParams->data)+1, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 967 | // Decrypt the new key |
| roywant | 0:ed0152b5c495 | 968 | aes128Decrypt(unlockKey, encryptedNewKey, newKey); |
| roywant | 0:ed0152b5c495 | 969 | memcpy(unlockKey, newKey, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 970 | } |
| roywant | 0:ed0152b5c495 | 971 | ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(uint8_t)); |
| roywant | 0:ed0152b5c495 | 972 | // CHAR-7 UNLOCK |
| roywant | 0:ed0152b5c495 | 973 | } else if (handle == unlockChar->getValueHandle()) { |
| roywant | 0:ed0152b5c495 | 974 | LOG(("Write: Unlock Handle=%d\r\n", handle)); |
| roywant | 0:ed0152b5c495 | 975 | // NOTE: Actual comparison with unlock code is done in: |
| roywant | 0:ed0152b5c495 | 976 | // writeUnlockAuthorizationCallback(...) which is executed before this method call. |
| roywant | 0:ed0152b5c495 | 977 | lockState = UNLOCKED; |
| roywant | 0:ed0152b5c495 | 978 | // Regenerate challenge and expected unlockToken for Next unlock operation |
| roywant | 0:ed0152b5c495 | 979 | generateRandom(challenge, sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 980 | aes128Encrypt(unlockKey, challenge, unlockToken); |
| roywant | 0:ed0152b5c495 | 981 | // Update Chars |
| roywant | 0:ed0152b5c495 | 982 | ble.gattServer().write(unlockChar->getValueHandle(), reinterpret_cast<uint8_t *>(challenge), sizeof(Lock_t)); // Update the challenge |
| roywant | 0:ed0152b5c495 | 983 | ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(uint8_t)); // Update the lock |
| roywant | 0:ed0152b5c495 | 984 | // CHAR-8 PUBLIC ECDH KEY |
| roywant | 0:ed0152b5c495 | 985 | /* PublicEchdChar is READ ONLY */ |
| roywant | 0:ed0152b5c495 | 986 | // CHAR-9 EID INDENTITY KEY |
| roywant | 0:ed0152b5c495 | 987 | /* EidIdentityChar is READ ONLY */ |
| roywant | 0:ed0152b5c495 | 988 | // CHAR-10 ADV DATA |
| roywant | 0:ed0152b5c495 | 989 | } else if (handle == advSlotDataChar->getValueHandle()) { |
| roywant | 0:ed0152b5c495 | 990 | LOG(("Write: Adv Slot DATA Handle=%d\r\n", handle)); |
| roywant | 0:ed0152b5c495 | 991 | uint8_t* frame = slotToFrame(activeSlot); |
| roywant | 0:ed0152b5c495 | 992 | int8_t advTxPower = slotAdvTxPowerLevels[activeSlot]; |
| roywant | 0:ed0152b5c495 | 993 | uint8_t writeFrameFormat = *(writeParams->data); |
| roywant | 0:ed0152b5c495 | 994 | uint8_t writeFrameLen = (writeParams->len); |
| roywant | 0:ed0152b5c495 | 995 | uint8_t writeData[34]; |
| roywant | 0:ed0152b5c495 | 996 | uint8_t serverPublicEcdhKey[32]; |
| roywant | 0:ed0152b5c495 | 997 | |
| roywant | 0:ed0152b5c495 | 998 | if (writeFrameLen != 0) { |
| roywant | 0:ed0152b5c495 | 999 | writeFrameLen--; // Remove the Format byte from the count |
| roywant | 0:ed0152b5c495 | 1000 | } else { |
| roywant | 0:ed0152b5c495 | 1001 | writeFrameFormat = UNDEFINED_FRAME_FORMAT; // Undefined format |
| roywant | 0:ed0152b5c495 | 1002 | } |
| roywant | 0:ed0152b5c495 | 1003 | |
| roywant | 0:ed0152b5c495 | 1004 | memcpy(writeData, (writeParams->data) + 1, writeFrameLen); |
| roywant | 0:ed0152b5c495 | 1005 | LOG(("ADV Data Write=%d,%d\r\n", writeFrameFormat, writeFrameLen)); |
| roywant | 0:ed0152b5c495 | 1006 | switch(writeFrameFormat) { |
| roywant | 0:ed0152b5c495 | 1007 | case UIDFrame::FRAME_TYPE_UID: |
| roywant | 0:ed0152b5c495 | 1008 | if (writeFrameLen == 16) { |
| roywant | 0:ed0152b5c495 | 1009 | uidFrame.setData(frame, advTxPower,reinterpret_cast<const uint8_t *>((writeParams->data) + 1)); |
| roywant | 0:ed0152b5c495 | 1010 | slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_UID; |
| roywant | 0:ed0152b5c495 | 1011 | } else if (writeFrameLen == 0) { |
| roywant | 0:ed0152b5c495 | 1012 | uidFrame.clearFrame(frame); |
| roywant | 0:ed0152b5c495 | 1013 | } |
| roywant | 0:ed0152b5c495 | 1014 | break; |
| roywant | 0:ed0152b5c495 | 1015 | case URLFrame::FRAME_TYPE_URL: |
| roywant | 0:ed0152b5c495 | 1016 | if (writeFrameLen <= 18) { |
| roywant | 0:ed0152b5c495 | 1017 | urlFrame.setData(frame, advTxPower, reinterpret_cast<const uint8_t*>((writeParams->data) + 1), writeFrameLen ); |
| roywant | 0:ed0152b5c495 | 1018 | slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_URL; |
| roywant | 0:ed0152b5c495 | 1019 | } else if (writeFrameLen == 0) { |
| roywant | 0:ed0152b5c495 | 1020 | urlFrame.clearFrame(frame); |
| roywant | 0:ed0152b5c495 | 1021 | } |
| roywant | 0:ed0152b5c495 | 1022 | break; |
| roywant | 0:ed0152b5c495 | 1023 | case TLMFrame::FRAME_TYPE_TLM: |
| roywant | 0:ed0152b5c495 | 1024 | if (writeFrameLen == 0) { |
| roywant | 0:ed0152b5c495 | 1025 | updateRawTLMFrame(frame); |
| roywant | 0:ed0152b5c495 | 1026 | tlmFrame.setData(frame); |
| roywant | 0:ed0152b5c495 | 1027 | int slot = getEidSlot(); |
| roywant | 0:ed0152b5c495 | 1028 | LOG(("WRITE: Testing if TLM or ETLM=%d\r\n", slot)); |
| roywant | 0:ed0152b5c495 | 1029 | if (slot != NO_EID_SLOT_SET) { |
| roywant | 0:ed0152b5c495 | 1030 | LOG(("WRITE: Configuring ETLM Slot time=%d\r\n", timeSinceBootTimer.read_ms() / 1000)); |
| roywant | 0:ed0152b5c495 | 1031 | tlmFrame.encryptData(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000); |
| roywant | 0:ed0152b5c495 | 1032 | } |
| roywant | 0:ed0152b5c495 | 1033 | slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_TLM; |
| roywant | 0:ed0152b5c495 | 1034 | } |
| roywant | 0:ed0152b5c495 | 1035 | break; |
| roywant | 0:ed0152b5c495 | 1036 | case EIDFrame::FRAME_TYPE_EID: |
| roywant | 0:ed0152b5c495 | 1037 | LOG(("EID Len=%d\r\n", writeFrameLen)); |
| roywant | 0:ed0152b5c495 | 1038 | if (writeFrameLen == 17) { |
| roywant | 0:ed0152b5c495 | 1039 | // Least secure |
| roywant | 0:ed0152b5c495 | 1040 | LOG(("EID Insecure branch\r\n")); |
| roywant | 0:ed0152b5c495 | 1041 | aes128Decrypt(unlockKey, writeData, slotEidIdentityKeys[activeSlot]); |
| roywant | 0:ed0152b5c495 | 1042 | slotEidRotationPeriodExps[activeSlot] = writeData[16]; // index 16 is the exponent |
| roywant | 0:ed0152b5c495 | 1043 | ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&writeData), sizeof(EidIdentityKey_t)); |
| roywant | 0:ed0152b5c495 | 1044 | } else if (writeFrameLen == 33 ) { |
| roywant | 0:ed0152b5c495 | 1045 | // Most secure |
| roywant | 0:ed0152b5c495 | 1046 | memcpy(serverPublicEcdhKey, writeData, 32); |
| roywant | 0:ed0152b5c495 | 1047 | ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&serverPublicEcdhKey), sizeof(PublicEcdhKey_t)); |
| roywant | 0:ed0152b5c495 | 1048 | LOG(("ServerPublicEcdhKey=")); logPrintHex(serverPublicEcdhKey, 32); |
| roywant | 0:ed0152b5c495 | 1049 | slotEidRotationPeriodExps[activeSlot] = writeData[32]; // index 32 is the exponent |
| roywant | 0:ed0152b5c495 | 1050 | LOG(("Exponent=%i\r\n", writeData[32])); |
| roywant | 0:ed0152b5c495 | 1051 | LOG(("genBeaconKeyRC=%x\r\n", genBeaconKeyRC)); |
| roywant | 0:ed0152b5c495 | 1052 | LOG(("BeaconPrivateEcdhKey=")); logPrintHex(privateEcdhKey, 32); |
| roywant | 0:ed0152b5c495 | 1053 | LOG(("BeaconPublicEcdhKey=")); logPrintHex(publicEcdhKey, 32); |
| roywant | 0:ed0152b5c495 | 1054 | LOG(("genECDHShareKey\r\n")); |
| roywant | 0:ed0152b5c495 | 1055 | int rc = eidFrame.genEcdhSharedKey(privateEcdhKey, publicEcdhKey, serverPublicEcdhKey, slotEidIdentityKeys[activeSlot]); |
| roywant | 0:ed0152b5c495 | 1056 | LOG(("Gen Keys RC = %x\r\n", rc)); |
| roywant | 0:ed0152b5c495 | 1057 | LOG(("Generated eidIdentityKey=")); logPrintHex(slotEidIdentityKeys[activeSlot], 16); |
| roywant | 0:ed0152b5c495 | 1058 | aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey); |
| roywant | 0:ed0152b5c495 | 1059 | LOG(("encryptedEidIdentityKey=")); logPrintHex(encryptedEidIdentityKey, 16); |
| roywant | 0:ed0152b5c495 | 1060 | ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&encryptedEidIdentityKey), sizeof(EidIdentityKey_t)); |
| roywant | 0:ed0152b5c495 | 1061 | } else if (writeFrameLen == 0) { |
| roywant | 0:ed0152b5c495 | 1062 | // Reset eidFrame |
| roywant | 0:ed0152b5c495 | 1063 | eidFrame.clearFrame(frame); |
| roywant | 0:ed0152b5c495 | 1064 | break; |
| roywant | 0:ed0152b5c495 | 1065 | } else { |
| roywant | 0:ed0152b5c495 | 1066 | break; // Do nothing, this is not a recognized Frame length |
| roywant | 0:ed0152b5c495 | 1067 | } |
| roywant | 0:ed0152b5c495 | 1068 | // Establish the new frame type |
| roywant | 0:ed0152b5c495 | 1069 | slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_EID; |
| roywant | 0:ed0152b5c495 | 1070 | nextEidSlot = activeSlot; // This was the last one updated |
| roywant | 0:ed0152b5c495 | 1071 | LOG(("update Eid Frame\r\n")); |
| roywant | 0:ed0152b5c495 | 1072 | // Generate ADV frame packet from EidIdentity Key |
| roywant | 0:ed0152b5c495 | 1073 | eidFrame.update(frame, slotEidIdentityKeys[activeSlot], slotEidRotationPeriodExps[activeSlot], timeSinceBootTimer.read_ms() / 1000); |
| roywant | 0:ed0152b5c495 | 1074 | LOG(("END update Eid Frame\r\n")); |
| roywant | 0:ed0152b5c495 | 1075 | break; |
| roywant | 0:ed0152b5c495 | 1076 | default: |
| roywant | 0:ed0152b5c495 | 1077 | frame[0] = 0; // Frame format unknown so clear the entire frame by writing 0 to its length |
| roywant | 0:ed0152b5c495 | 1078 | break; |
| roywant | 0:ed0152b5c495 | 1079 | } |
| roywant | 0:ed0152b5c495 | 1080 | // Read takes care of setting the Characteristic Value |
| roywant | 0:ed0152b5c495 | 1081 | // CHAR-11 FACTORY RESET |
| roywant | 0:ed0152b5c495 | 1082 | } else if (handle == factoryResetChar->getValueHandle() && (*((uint8_t *)writeParams->data) != 0)) { |
| roywant | 0:ed0152b5c495 | 1083 | LOG(("Write: Factory Reset: Handle=%d\r\n", handle)); |
| roywant | 0:ed0152b5c495 | 1084 | // Reset params to default values |
| roywant | 0:ed0152b5c495 | 1085 | doFactoryReset(); |
| roywant | 0:ed0152b5c495 | 1086 | // Update all characteristics based on params |
| roywant | 0:ed0152b5c495 | 1087 | updateCharacteristicValues(); |
| roywant | 0:ed0152b5c495 | 1088 | // CHAR-12 REMAIN CONNECTABLE |
| roywant | 0:ed0152b5c495 | 1089 | } else if (handle == remainConnectableChar->getValueHandle()) { |
| roywant | 0:ed0152b5c495 | 1090 | LOG(("Write: Remain Connectable Handle=%d\r\n", handle)); |
| roywant | 0:ed0152b5c495 | 1091 | remainConnectable = *(writeParams->data); |
| roywant | 0:ed0152b5c495 | 1092 | ble.gattServer().write(remainConnectableChar->getValueHandle(), &remainConnectable, sizeof(uint8_t)); |
| roywant | 0:ed0152b5c495 | 1093 | } |
| roywant | 0:ed0152b5c495 | 1094 | |
| roywant | 0:ed0152b5c495 | 1095 | } |
| roywant | 0:ed0152b5c495 | 1096 | |
| roywant | 0:ed0152b5c495 | 1097 | void EddystoneService::setFrameTxPower(uint8_t slot, int8_t advTxPower) { |
| roywant | 0:ed0152b5c495 | 1098 | uint8_t* frame = slotToFrame(slot); |
| roywant | 0:ed0152b5c495 | 1099 | uint8_t frameType = slotFrameTypes[slot]; |
| roywant | 0:ed0152b5c495 | 1100 | switch (frameType) { |
| roywant | 0:ed0152b5c495 | 1101 | case UIDFrame::FRAME_TYPE_UID: |
| roywant | 0:ed0152b5c495 | 1102 | uidFrame.setAdvTxPower(frame, advTxPower); |
| roywant | 0:ed0152b5c495 | 1103 | break; |
| roywant | 0:ed0152b5c495 | 1104 | case URLFrame::FRAME_TYPE_URL: |
| roywant | 0:ed0152b5c495 | 1105 | urlFrame.setAdvTxPower(frame, advTxPower); |
| roywant | 0:ed0152b5c495 | 1106 | break; |
| roywant | 0:ed0152b5c495 | 1107 | case EIDFrame::FRAME_TYPE_EID: |
| roywant | 0:ed0152b5c495 | 1108 | eidFrame.setAdvTxPower(frame, advTxPower); |
| roywant | 0:ed0152b5c495 | 1109 | break; |
| roywant | 0:ed0152b5c495 | 1110 | } |
| roywant | 0:ed0152b5c495 | 1111 | } |
| roywant | 0:ed0152b5c495 | 1112 | |
| roywant | 0:ed0152b5c495 | 1113 | uint8_t EddystoneService::radioTxPowerToIndex(int8_t txPower) { |
| roywant | 0:ed0152b5c495 | 1114 | // NOTE: txPower is an 8-bit signed number |
| roywant | 0:ed0152b5c495 | 1115 | uint8_t size = sizeof(PowerLevels_t); |
| roywant | 0:ed0152b5c495 | 1116 | // Look for the value in range (or next biggest value) |
| roywant | 0:ed0152b5c495 | 1117 | for (uint8_t i = 0; i < size; i++) { |
| roywant | 0:ed0152b5c495 | 1118 | if (txPower <= radioTxPowerLevels[i]) { |
| roywant | 0:ed0152b5c495 | 1119 | return i; |
| roywant | 0:ed0152b5c495 | 1120 | } |
| roywant | 0:ed0152b5c495 | 1121 | } |
| roywant | 0:ed0152b5c495 | 1122 | return size - 1; |
| roywant | 0:ed0152b5c495 | 1123 | } |
| roywant | 0:ed0152b5c495 | 1124 | |
| roywant | 0:ed0152b5c495 | 1125 | /** AES128 encrypts a 16-byte input array with a key, resulting in a 16-byte output array */ |
| roywant | 0:ed0152b5c495 | 1126 | void EddystoneService::aes128Encrypt(uint8_t key[], uint8_t input[], uint8_t output[]) { |
| roywant | 0:ed0152b5c495 | 1127 | mbedtls_aes_context ctx; |
| roywant | 0:ed0152b5c495 | 1128 | mbedtls_aes_init(&ctx); |
| roywant | 0:ed0152b5c495 | 1129 | mbedtls_aes_setkey_enc(&ctx, key, 8 * sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 1130 | mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, input, output); |
| roywant | 0:ed0152b5c495 | 1131 | mbedtls_aes_free(&ctx); |
| roywant | 0:ed0152b5c495 | 1132 | } |
| roywant | 0:ed0152b5c495 | 1133 | |
| roywant | 0:ed0152b5c495 | 1134 | /** AES128 decrypts a 16-byte input array with a key, resulting in a 16-byte output array */ |
| roywant | 0:ed0152b5c495 | 1135 | void EddystoneService::aes128Decrypt(uint8_t key[], uint8_t input[], uint8_t output[]) { |
| roywant | 0:ed0152b5c495 | 1136 | mbedtls_aes_context ctx; |
| roywant | 0:ed0152b5c495 | 1137 | mbedtls_aes_init(&ctx); |
| roywant | 0:ed0152b5c495 | 1138 | mbedtls_aes_setkey_dec(&ctx, key, 8 * sizeof(Lock_t)); |
| roywant | 0:ed0152b5c495 | 1139 | mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_DECRYPT, input, output); |
| roywant | 0:ed0152b5c495 | 1140 | mbedtls_aes_free(&ctx); |
| roywant | 0:ed0152b5c495 | 1141 | } |
| roywant | 0:ed0152b5c495 | 1142 | |
| roywant | 0:ed0152b5c495 | 1143 | |
| roywant | 0:ed0152b5c495 | 1144 | |
| roywant | 0:ed0152b5c495 | 1145 | #ifdef HARDWARE_RANDOM_NUM_GENERATOR |
| roywant | 0:ed0152b5c495 | 1146 | // Generates a set of random values in byte array[size] based on hardware source |
| roywant | 0:ed0152b5c495 | 1147 | void EddystoneService::generateRandom(uint8_t ain[], int size) { |
| roywant | 0:ed0152b5c495 | 1148 | mbedtls_entropy_context entropy; |
| roywant | 0:ed0152b5c495 | 1149 | mbedtls_entropy_init(&entropy); |
| roywant | 0:ed0152b5c495 | 1150 | // init entropy source |
| roywant | 0:ed0152b5c495 | 1151 | eddystoneRegisterEntropySource(&entropy); |
| roywant | 0:ed0152b5c495 | 1152 | mbedtls_ctr_drbg_context ctr_drbg; |
| roywant | 0:ed0152b5c495 | 1153 | mbedtls_ctr_drbg_init(&ctr_drbg); |
| roywant | 0:ed0152b5c495 | 1154 | mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); |
| roywant | 0:ed0152b5c495 | 1155 | mbedtls_ctr_drbg_random(&ctr_drbg, ain, size); |
| roywant | 0:ed0152b5c495 | 1156 | mbedtls_ctr_drbg_free(&ctr_drbg); |
| roywant | 0:ed0152b5c495 | 1157 | mbedtls_entropy_free(&entropy); |
| roywant | 0:ed0152b5c495 | 1158 | return; |
| roywant | 0:ed0152b5c495 | 1159 | } |
| roywant | 0:ed0152b5c495 | 1160 | #else |
| roywant | 0:ed0152b5c495 | 1161 | // Generates a set of random values in byte array[size] seeded by the clock(ms) |
| roywant | 0:ed0152b5c495 | 1162 | void EddystoneService::generateRandom(uint8_t ain[], int size) { |
| roywant | 0:ed0152b5c495 | 1163 | int i; |
| roywant | 0:ed0152b5c495 | 1164 | // Random seed based on boot time in milliseconds |
| roywant | 0:ed0152b5c495 | 1165 | srand(timeSinceBootTimer.read_ms()); |
| roywant | 0:ed0152b5c495 | 1166 | for (i = 0; i < size; i++) { |
| roywant | 0:ed0152b5c495 | 1167 | ain[i] = rand() % 256; |
| roywant | 0:ed0152b5c495 | 1168 | } |
| roywant | 0:ed0152b5c495 | 1169 | return; |
| roywant | 0:ed0152b5c495 | 1170 | } |
| roywant | 0:ed0152b5c495 | 1171 | #endif |
| roywant | 0:ed0152b5c495 | 1172 | |
| roywant | 0:ed0152b5c495 | 1173 | /* |
| roywant | 0:ed0152b5c495 | 1174 | // ALTERNATE Better Random number generator (but has Memory usage issues) |
| roywant | 0:ed0152b5c495 | 1175 | // Generates a set of random values in byte array[size] |
| roywant | 0:ed0152b5c495 | 1176 | |
| roywant | 0:ed0152b5c495 | 1177 | */ |
| roywant | 0:ed0152b5c495 | 1178 | |
| roywant | 0:ed0152b5c495 | 1179 | /** Reverse Even sized Array endianess: Big to Little or Little to Big */ |
| roywant | 0:ed0152b5c495 | 1180 | void EddystoneService::swapEndianArray(uint8_t ptrIn[], uint8_t ptrOut[], int size) { |
| roywant | 0:ed0152b5c495 | 1181 | int i; |
| roywant | 0:ed0152b5c495 | 1182 | for (i = 0; i < size; i++) { |
| roywant | 0:ed0152b5c495 | 1183 | ptrOut[i] = ptrIn[size - i - 1]; |
| roywant | 0:ed0152b5c495 | 1184 | } |
| roywant | 0:ed0152b5c495 | 1185 | return; |
| roywant | 0:ed0152b5c495 | 1186 | } |
| roywant | 0:ed0152b5c495 | 1187 | |
| roywant | 0:ed0152b5c495 | 1188 | /** Reverse endianess: Big to Little or Little to Big */ |
| roywant | 0:ed0152b5c495 | 1189 | uint16_t EddystoneService::swapEndian(uint16_t arg) { |
| roywant | 0:ed0152b5c495 | 1190 | return (arg / 256) + (arg % 256) * 256; |
| roywant | 0:ed0152b5c495 | 1191 | } |
| roywant | 0:ed0152b5c495 | 1192 | |
| roywant | 0:ed0152b5c495 | 1193 | uint16_t EddystoneService::correctAdvertisementPeriod(uint16_t beaconPeriodIn) const |
| roywant | 0:ed0152b5c495 | 1194 | { |
| roywant | 0:ed0152b5c495 | 1195 | /* Re-map beaconPeriod to within permissible bounds if necessary. */ |
| roywant | 0:ed0152b5c495 | 1196 | if (beaconPeriodIn != 0) { |
| roywant | 0:ed0152b5c495 | 1197 | if (beaconPeriodIn < ble.gap().getMinNonConnectableAdvertisingInterval()) { |
| roywant | 0:ed0152b5c495 | 1198 | return ble.gap().getMinNonConnectableAdvertisingInterval(); |
| roywant | 0:ed0152b5c495 | 1199 | } else if (beaconPeriodIn > ble.gap().getMaxAdvertisingInterval()) { |
| roywant | 0:ed0152b5c495 | 1200 | return ble.gap().getMaxAdvertisingInterval(); |
| roywant | 0:ed0152b5c495 | 1201 | } |
| roywant | 0:ed0152b5c495 | 1202 | } |
| roywant | 0:ed0152b5c495 | 1203 | return beaconPeriodIn; |
| roywant | 0:ed0152b5c495 | 1204 | } |
| roywant | 0:ed0152b5c495 | 1205 | |
| roywant | 0:ed0152b5c495 | 1206 | void EddystoneService::logPrintHex(uint8_t* a, int len) { |
| roywant | 0:ed0152b5c495 | 1207 | for (int i = 0; i < len; i++) { |
| roywant | 0:ed0152b5c495 | 1208 | LOG(("%x%x", a[i] >> 4, a[i] & 0x0f )); |
| roywant | 0:ed0152b5c495 | 1209 | } |
| roywant | 0:ed0152b5c495 | 1210 | LOG(("\r\n")); |
| roywant | 0:ed0152b5c495 | 1211 | } |
| roywant | 0:ed0152b5c495 | 1212 | |
| roywant | 0:ed0152b5c495 | 1213 | void EddystoneService::setRandomMacAddress(void) { |
| roywant | 0:ed0152b5c495 | 1214 | #ifdef EID_RANDOM_MAC |
| roywant | 0:ed0152b5c495 | 1215 | uint8_t macAddress[6]; // 48 bit Mac Address |
| roywant | 0:ed0152b5c495 | 1216 | generateRandom(macAddress, 6); |
| roywant | 0:ed0152b5c495 | 1217 | macAddress[5] |= 0xc0; // Ensure upper two bits are 11's for Random Add |
| roywant | 0:ed0152b5c495 | 1218 | ble.setAddress(BLEProtocol::AddressType::RANDOM_STATIC, macAddress); |
| roywant | 0:ed0152b5c495 | 1219 | #endif |
| roywant | 0:ed0152b5c495 | 1220 | } |
| roywant | 0:ed0152b5c495 | 1221 | |
| roywant | 0:ed0152b5c495 | 1222 | int EddystoneService::getEidSlot(void) { |
| roywant | 0:ed0152b5c495 | 1223 | int eidSlot = NO_EID_SLOT_SET; // by default; |
| roywant | 0:ed0152b5c495 | 1224 | for (int i = 0; i < MAX_ADV_SLOTS; i++) { |
| roywant | 0:ed0152b5c495 | 1225 | if (slotFrameTypes[nextEidSlot] == EDDYSTONE_FRAME_EID) { |
| roywant | 0:ed0152b5c495 | 1226 | eidSlot = nextEidSlot; |
| roywant | 0:ed0152b5c495 | 1227 | nextEidSlot = (nextEidSlot-1) % MAX_ADV_SLOTS; |
| roywant | 0:ed0152b5c495 | 1228 | break; |
| roywant | 0:ed0152b5c495 | 1229 | } |
| roywant | 0:ed0152b5c495 | 1230 | nextEidSlot = (nextEidSlot-1) % MAX_ADV_SLOTS; // ensure the slot numbers wrap |
| roywant | 0:ed0152b5c495 | 1231 | } |
| roywant | 0:ed0152b5c495 | 1232 | return eidSlot; |
| roywant | 0:ed0152b5c495 | 1233 | } |
| roywant | 0:ed0152b5c495 | 1234 | |
| roywant | 0:ed0152b5c495 | 1235 | |
| roywant | 0:ed0152b5c495 | 1236 | const uint8_t EddystoneService::slotDefaultUids[MAX_ADV_SLOTS][16] = EDDYSTONE_DEFAULT_SLOT_UIDS; |
| roywant | 0:ed0152b5c495 | 1237 | |
| roywant | 0:ed0152b5c495 | 1238 | const uint8_t EddystoneService::slotDefaultEidIdentityKeys[MAX_ADV_SLOTS][16] = EDDYSTONE_DEFAULT_SLOT_EID_IDENTITY_KEYS; |
| roywant | 0:ed0152b5c495 | 1239 | |
| roywant | 0:ed0152b5c495 | 1240 | const uint8_t EddystoneService::allSlotsDefaultEid[8] = {0,0,0,0,0,0,0,0}; |