Run on TY51822r3 board with ACC sensor (LIS3DH or BMC050)
Dependencies: BLE_API LIS3DH mbed nRF51822 BMC050 nRF51_LowPwr nRF51_Vdd
Fork of BLE_EddystoneBeacon_Service by
EddystoneService.cpp@37:ea459e6c6a35, 2016-06-11 (annotated)
- Committer:
- kenjiArai
- Date:
- Sat Jun 11 01:51:59 2016 +0000
- Revision:
- 37:ea459e6c6a35
- Parent:
- 34:f6d4a699a1ea
Added low power mode (use nRF51_LowPwr library) for reduce Idle Current
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
andresag | 34:f6d4a699a1ea | 1 | /* mbed Microcontroller Library |
andresag | 34:f6d4a699a1ea | 2 | * Copyright (c) 2006-2015 ARM Limited |
andresag | 34:f6d4a699a1ea | 3 | * |
andresag | 34:f6d4a699a1ea | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
andresag | 34:f6d4a699a1ea | 5 | * you may not use this file except in compliance with the License. |
andresag | 34:f6d4a699a1ea | 6 | * You may obtain a copy of the License at |
andresag | 34:f6d4a699a1ea | 7 | * |
andresag | 34:f6d4a699a1ea | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
andresag | 34:f6d4a699a1ea | 9 | * |
andresag | 34:f6d4a699a1ea | 10 | * Unless required by applicable law or agreed to in writing, software |
andresag | 34:f6d4a699a1ea | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
andresag | 34:f6d4a699a1ea | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
andresag | 34:f6d4a699a1ea | 13 | * See the License for the specific language governing permissions and |
andresag | 34:f6d4a699a1ea | 14 | * limitations under the License. |
andresag | 34:f6d4a699a1ea | 15 | */ |
andresag | 34:f6d4a699a1ea | 16 | |
andresag | 34:f6d4a699a1ea | 17 | #include "EddystoneService.h" |
andresag | 34:f6d4a699a1ea | 18 | |
andresag | 34:f6d4a699a1ea | 19 | /* Initialise the EddystoneService using parameters from persistent storage */ |
andresag | 34:f6d4a699a1ea | 20 | EddystoneService::EddystoneService(BLE &bleIn, |
andresag | 34:f6d4a699a1ea | 21 | EddystoneParams_t ¶msIn, |
andresag | 34:f6d4a699a1ea | 22 | const PowerLevels_t &advPowerLevelsIn, |
andresag | 34:f6d4a699a1ea | 23 | const PowerLevels_t &radioPowerLevelsIn, |
andresag | 34:f6d4a699a1ea | 24 | uint32_t advConfigIntervalIn) : |
andresag | 34:f6d4a699a1ea | 25 | ble(bleIn), |
andresag | 34:f6d4a699a1ea | 26 | operationMode(EDDYSTONE_MODE_NONE), |
andresag | 34:f6d4a699a1ea | 27 | urlFrame(paramsIn.urlData, paramsIn.urlDataLength), |
andresag | 34:f6d4a699a1ea | 28 | uidFrame(paramsIn.uidNamespaceID, paramsIn.uidInstanceID), |
andresag | 34:f6d4a699a1ea | 29 | tlmFrame(paramsIn.tlmVersion), |
andresag | 34:f6d4a699a1ea | 30 | resetFlag(false), |
andresag | 34:f6d4a699a1ea | 31 | tlmBatteryVoltageCallback(NULL), |
andresag | 34:f6d4a699a1ea | 32 | tlmBeaconTemperatureCallback(NULL) |
andresag | 34:f6d4a699a1ea | 33 | { |
andresag | 34:f6d4a699a1ea | 34 | lockState = paramsIn.lockState; |
andresag | 34:f6d4a699a1ea | 35 | flags = paramsIn.flags; |
andresag | 34:f6d4a699a1ea | 36 | txPowerMode = paramsIn.txPowerMode; |
andresag | 34:f6d4a699a1ea | 37 | beaconPeriod = correctAdvertisementPeriod(paramsIn.beaconPeriod); |
andresag | 34:f6d4a699a1ea | 38 | |
andresag | 34:f6d4a699a1ea | 39 | memcpy(lock, paramsIn.lock, sizeof(Lock_t)); |
andresag | 34:f6d4a699a1ea | 40 | memcpy(unlock, paramsIn.unlock, sizeof(Lock_t)); |
andresag | 34:f6d4a699a1ea | 41 | |
andresag | 34:f6d4a699a1ea | 42 | eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn); |
andresag | 34:f6d4a699a1ea | 43 | } |
andresag | 34:f6d4a699a1ea | 44 | |
andresag | 34:f6d4a699a1ea | 45 | /* When using this constructor we need to call setURLData, |
andresag | 34:f6d4a699a1ea | 46 | * setTMLData and setUIDData to initialise values manually |
andresag | 34:f6d4a699a1ea | 47 | */ |
andresag | 34:f6d4a699a1ea | 48 | EddystoneService::EddystoneService(BLE &bleIn, |
andresag | 34:f6d4a699a1ea | 49 | const PowerLevels_t &advPowerLevelsIn, |
andresag | 34:f6d4a699a1ea | 50 | const PowerLevels_t &radioPowerLevelsIn, |
andresag | 34:f6d4a699a1ea | 51 | uint32_t advConfigIntervalIn) : |
andresag | 34:f6d4a699a1ea | 52 | ble(bleIn), |
andresag | 34:f6d4a699a1ea | 53 | operationMode(EDDYSTONE_MODE_NONE), |
andresag | 34:f6d4a699a1ea | 54 | urlFrame(), |
andresag | 34:f6d4a699a1ea | 55 | uidFrame(), |
andresag | 34:f6d4a699a1ea | 56 | tlmFrame(), |
andresag | 34:f6d4a699a1ea | 57 | lockState(false), |
andresag | 34:f6d4a699a1ea | 58 | resetFlag(false), |
andresag | 34:f6d4a699a1ea | 59 | lock(), |
andresag | 34:f6d4a699a1ea | 60 | unlock(), |
andresag | 34:f6d4a699a1ea | 61 | flags(0), |
andresag | 34:f6d4a699a1ea | 62 | txPowerMode(0), |
andresag | 34:f6d4a699a1ea | 63 | beaconPeriod(DEFAULT_BEACON_PERIOD_MSEC), |
andresag | 34:f6d4a699a1ea | 64 | tlmBatteryVoltageCallback(NULL), |
andresag | 34:f6d4a699a1ea | 65 | tlmBeaconTemperatureCallback(NULL) |
andresag | 34:f6d4a699a1ea | 66 | { |
andresag | 34:f6d4a699a1ea | 67 | eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn); |
andresag | 34:f6d4a699a1ea | 68 | } |
andresag | 34:f6d4a699a1ea | 69 | |
andresag | 34:f6d4a699a1ea | 70 | /* Setup callback to update BatteryVoltage in TLM frame */ |
andresag | 34:f6d4a699a1ea | 71 | void EddystoneService::onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn) |
andresag | 34:f6d4a699a1ea | 72 | { |
andresag | 34:f6d4a699a1ea | 73 | tlmBatteryVoltageCallback = tlmBatteryVoltageCallbackIn; |
andresag | 34:f6d4a699a1ea | 74 | } |
andresag | 34:f6d4a699a1ea | 75 | |
andresag | 34:f6d4a699a1ea | 76 | /* Setup callback to update BeaconTemperature in TLM frame */ |
andresag | 34:f6d4a699a1ea | 77 | void EddystoneService::onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn) |
andresag | 34:f6d4a699a1ea | 78 | { |
andresag | 34:f6d4a699a1ea | 79 | tlmBeaconTemperatureCallback = tlmBeaconTemperatureCallbackIn; |
andresag | 34:f6d4a699a1ea | 80 | } |
andresag | 34:f6d4a699a1ea | 81 | |
andresag | 34:f6d4a699a1ea | 82 | void EddystoneService::setTLMData(uint8_t tlmVersionIn) |
andresag | 34:f6d4a699a1ea | 83 | { |
andresag | 34:f6d4a699a1ea | 84 | tlmFrame.setTLMData(tlmVersionIn); |
andresag | 34:f6d4a699a1ea | 85 | } |
andresag | 34:f6d4a699a1ea | 86 | |
andresag | 34:f6d4a699a1ea | 87 | void EddystoneService::setURLData(const char *urlDataIn) |
andresag | 34:f6d4a699a1ea | 88 | { |
andresag | 34:f6d4a699a1ea | 89 | urlFrame.setURLData(urlDataIn); |
andresag | 34:f6d4a699a1ea | 90 | } |
andresag | 34:f6d4a699a1ea | 91 | |
andresag | 34:f6d4a699a1ea | 92 | void EddystoneService::setUIDData(const UIDNamespaceID_t *uidNamespaceIDIn, const UIDInstanceID_t *uidInstanceIDIn) |
andresag | 34:f6d4a699a1ea | 93 | { |
andresag | 34:f6d4a699a1ea | 94 | uidFrame.setUIDData(uidNamespaceIDIn, uidInstanceIDIn); |
andresag | 34:f6d4a699a1ea | 95 | } |
andresag | 34:f6d4a699a1ea | 96 | |
andresag | 34:f6d4a699a1ea | 97 | EddystoneService::EddystoneError_t EddystoneService::startConfigService(void) |
andresag | 34:f6d4a699a1ea | 98 | { |
andresag | 34:f6d4a699a1ea | 99 | if (operationMode == EDDYSTONE_MODE_CONFIG) { |
andresag | 34:f6d4a699a1ea | 100 | /* Nothing to do, we are already in config mode */ |
andresag | 34:f6d4a699a1ea | 101 | return EDDYSTONE_ERROR_NONE; |
andresag | 34:f6d4a699a1ea | 102 | } else if (advConfigInterval == 0) { |
andresag | 34:f6d4a699a1ea | 103 | /* Nothing to do, the advertisement interval is 0 */ |
andresag | 34:f6d4a699a1ea | 104 | return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL; |
andresag | 34:f6d4a699a1ea | 105 | } |
andresag | 34:f6d4a699a1ea | 106 | |
andresag | 34:f6d4a699a1ea | 107 | if (operationMode == EDDYSTONE_MODE_BEACON) { |
andresag | 34:f6d4a699a1ea | 108 | ble.shutdown(); |
andresag | 34:f6d4a699a1ea | 109 | /* Free unused memory */ |
andresag | 34:f6d4a699a1ea | 110 | freeBeaconFrames(); |
andresag | 34:f6d4a699a1ea | 111 | operationMode = EDDYSTONE_MODE_CONFIG; |
andresag | 34:f6d4a699a1ea | 112 | ble.init(this, &EddystoneService::bleInitComplete); |
andresag | 34:f6d4a699a1ea | 113 | return EDDYSTONE_ERROR_NONE; |
andresag | 34:f6d4a699a1ea | 114 | } |
andresag | 34:f6d4a699a1ea | 115 | |
andresag | 34:f6d4a699a1ea | 116 | operationMode = EDDYSTONE_MODE_CONFIG; |
andresag | 34:f6d4a699a1ea | 117 | setupConfigService(); |
andresag | 34:f6d4a699a1ea | 118 | return EDDYSTONE_ERROR_NONE; |
andresag | 34:f6d4a699a1ea | 119 | } |
andresag | 34:f6d4a699a1ea | 120 | |
andresag | 34:f6d4a699a1ea | 121 | EddystoneService::EddystoneError_t EddystoneService::startBeaconService(uint16_t consecUrlFramesIn, uint16_t consecUidFramesIn, uint16_t consecTlmFramesIn) |
andresag | 34:f6d4a699a1ea | 122 | { |
andresag | 34:f6d4a699a1ea | 123 | if (operationMode == EDDYSTONE_MODE_BEACON) { |
andresag | 34:f6d4a699a1ea | 124 | /* Nothing to do, we are already in beacon mode */ |
andresag | 34:f6d4a699a1ea | 125 | return EDDYSTONE_ERROR_NONE; |
andresag | 34:f6d4a699a1ea | 126 | } else if (!consecUrlFramesIn && !consecUidFramesIn && !consecTlmFramesIn) { |
andresag | 34:f6d4a699a1ea | 127 | /* Nothing to do, the user wants 0 consecutive frames of everything */ |
andresag | 34:f6d4a699a1ea | 128 | return EDDYSTONE_ERROR_INVALID_CONSEC_FRAMES; |
andresag | 34:f6d4a699a1ea | 129 | } else if (!beaconPeriod) { |
andresag | 34:f6d4a699a1ea | 130 | /* Nothing to do, the period is 0 for all frames */ |
andresag | 34:f6d4a699a1ea | 131 | return EDDYSTONE_ERROR_INVALID_BEACON_PERIOD; |
andresag | 34:f6d4a699a1ea | 132 | } |
andresag | 34:f6d4a699a1ea | 133 | |
andresag | 34:f6d4a699a1ea | 134 | /* Setup tracking of the current advertised frame. Note that this will |
andresag | 34:f6d4a699a1ea | 135 | * cause URL or UID frames to be advertised first! |
andresag | 34:f6d4a699a1ea | 136 | */ |
andresag | 34:f6d4a699a1ea | 137 | currentAdvertisedFrame = EDDYSTONE_FRAME_TLM; |
andresag | 34:f6d4a699a1ea | 138 | consecFrames[EDDYSTONE_FRAME_URL] = consecUrlFramesIn; |
andresag | 34:f6d4a699a1ea | 139 | consecFrames[EDDYSTONE_FRAME_UID] = consecUidFramesIn; |
andresag | 34:f6d4a699a1ea | 140 | consecFrames[EDDYSTONE_FRAME_TLM] = consecTlmFramesIn; |
andresag | 34:f6d4a699a1ea | 141 | |
andresag | 34:f6d4a699a1ea | 142 | memset(currentConsecFrames, 0, sizeof(uint16_t) * NUM_EDDYSTONE_FRAMES); |
andresag | 34:f6d4a699a1ea | 143 | |
andresag | 34:f6d4a699a1ea | 144 | if (operationMode == EDDYSTONE_MODE_CONFIG) { |
andresag | 34:f6d4a699a1ea | 145 | ble.shutdown(); |
andresag | 34:f6d4a699a1ea | 146 | /* Free unused memory */ |
andresag | 34:f6d4a699a1ea | 147 | freeConfigCharacteristics(); |
andresag | 34:f6d4a699a1ea | 148 | operationMode = EDDYSTONE_MODE_BEACON; |
andresag | 34:f6d4a699a1ea | 149 | ble.init(this, &EddystoneService::bleInitComplete); |
andresag | 34:f6d4a699a1ea | 150 | return EDDYSTONE_ERROR_NONE; |
andresag | 34:f6d4a699a1ea | 151 | } |
andresag | 34:f6d4a699a1ea | 152 | |
andresag | 34:f6d4a699a1ea | 153 | operationMode = EDDYSTONE_MODE_BEACON; |
andresag | 34:f6d4a699a1ea | 154 | setupBeaconService(); |
andresag | 34:f6d4a699a1ea | 155 | return EDDYSTONE_ERROR_NONE; |
andresag | 34:f6d4a699a1ea | 156 | } |
andresag | 34:f6d4a699a1ea | 157 | |
andresag | 34:f6d4a699a1ea | 158 | /* It is not the responsibility of the Eddystone implementation to store |
andresag | 34:f6d4a699a1ea | 159 | * the configured parameters in persistent storage since this is |
andresag | 34:f6d4a699a1ea | 160 | * platform-specific. So we provide this function that returns the |
andresag | 34:f6d4a699a1ea | 161 | * configured values that need to be stored and the main application |
andresag | 34:f6d4a699a1ea | 162 | * takes care of storing them. |
andresag | 34:f6d4a699a1ea | 163 | */ |
andresag | 34:f6d4a699a1ea | 164 | void EddystoneService::getEddystoneParams(EddystoneParams_t *params) |
andresag | 34:f6d4a699a1ea | 165 | { |
andresag | 34:f6d4a699a1ea | 166 | params->lockState = lockState; |
andresag | 34:f6d4a699a1ea | 167 | params->flags = flags; |
andresag | 34:f6d4a699a1ea | 168 | params->txPowerMode = txPowerMode; |
andresag | 34:f6d4a699a1ea | 169 | params->beaconPeriod = beaconPeriod; |
andresag | 34:f6d4a699a1ea | 170 | params->tlmVersion = tlmFrame.getTLMVersion(); |
andresag | 34:f6d4a699a1ea | 171 | params->urlDataLength = urlFrame.getEncodedURLDataLength(); |
andresag | 34:f6d4a699a1ea | 172 | |
andresag | 34:f6d4a699a1ea | 173 | memcpy(params->advPowerLevels, advPowerLevels, sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 174 | memcpy(params->lock, lock, sizeof(Lock_t)); |
andresag | 34:f6d4a699a1ea | 175 | memcpy(params->unlock, unlock, sizeof(Lock_t)); |
andresag | 34:f6d4a699a1ea | 176 | memcpy(params->urlData, urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength()); |
andresag | 34:f6d4a699a1ea | 177 | memcpy(params->uidNamespaceID, uidFrame.getUIDNamespaceID(), sizeof(UIDNamespaceID_t)); |
andresag | 34:f6d4a699a1ea | 178 | memcpy(params->uidInstanceID, uidFrame.getUIDInstanceID(), sizeof(UIDInstanceID_t)); |
andresag | 34:f6d4a699a1ea | 179 | } |
andresag | 34:f6d4a699a1ea | 180 | |
andresag | 34:f6d4a699a1ea | 181 | /* Helper function used only once during constructing the object to avoid |
andresag | 34:f6d4a699a1ea | 182 | * duplicated code. |
andresag | 34:f6d4a699a1ea | 183 | */ |
andresag | 34:f6d4a699a1ea | 184 | void EddystoneService::eddystoneConstructorHelper(const PowerLevels_t &advPowerLevelsIn, |
andresag | 34:f6d4a699a1ea | 185 | const PowerLevels_t &radioPowerLevelsIn, |
andresag | 34:f6d4a699a1ea | 186 | uint32_t advConfigIntervalIn) |
andresag | 34:f6d4a699a1ea | 187 | { |
andresag | 34:f6d4a699a1ea | 188 | advConfigInterval = (advConfigIntervalIn > 0) ? correctAdvertisementPeriod(advConfigIntervalIn) : 0; |
andresag | 34:f6d4a699a1ea | 189 | |
andresag | 34:f6d4a699a1ea | 190 | memcpy(radioPowerLevels, radioPowerLevelsIn, sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 191 | memcpy(advPowerLevels, advPowerLevelsIn, sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 192 | |
andresag | 34:f6d4a699a1ea | 193 | /* TODO: Note that this timer is started from the time EddystoneService |
andresag | 34:f6d4a699a1ea | 194 | * is initialised and NOT from when the device is booted. So app needs |
andresag | 34:f6d4a699a1ea | 195 | * to take care that EddystoneService is one of the first things to be |
andresag | 34:f6d4a699a1ea | 196 | * started! |
andresag | 34:f6d4a699a1ea | 197 | */ |
andresag | 34:f6d4a699a1ea | 198 | timeSinceBootTimer.start(); |
andresag | 34:f6d4a699a1ea | 199 | } |
andresag | 34:f6d4a699a1ea | 200 | |
andresag | 34:f6d4a699a1ea | 201 | /* When changing modes, we shutdown and init the BLE instance, so |
andresag | 34:f6d4a699a1ea | 202 | * this is needed to complete the initialisation task. |
andresag | 34:f6d4a699a1ea | 203 | */ |
andresag | 34:f6d4a699a1ea | 204 | void EddystoneService::bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext) |
andresag | 34:f6d4a699a1ea | 205 | { |
andresag | 34:f6d4a699a1ea | 206 | if (initContext->error != BLE_ERROR_NONE) { |
andresag | 34:f6d4a699a1ea | 207 | /* Initialisation failed */ |
andresag | 34:f6d4a699a1ea | 208 | return; |
andresag | 34:f6d4a699a1ea | 209 | } |
andresag | 34:f6d4a699a1ea | 210 | |
andresag | 34:f6d4a699a1ea | 211 | switch (operationMode) { |
andresag | 34:f6d4a699a1ea | 212 | case EDDYSTONE_MODE_CONFIG: |
andresag | 34:f6d4a699a1ea | 213 | setupConfigService(); |
andresag | 34:f6d4a699a1ea | 214 | break; |
andresag | 34:f6d4a699a1ea | 215 | case EDDYSTONE_MODE_BEACON: |
andresag | 34:f6d4a699a1ea | 216 | setupBeaconService(); |
andresag | 34:f6d4a699a1ea | 217 | break; |
andresag | 34:f6d4a699a1ea | 218 | default: |
andresag | 34:f6d4a699a1ea | 219 | /* Some error occurred */ |
andresag | 34:f6d4a699a1ea | 220 | break; |
andresag | 34:f6d4a699a1ea | 221 | } |
andresag | 34:f6d4a699a1ea | 222 | } |
andresag | 34:f6d4a699a1ea | 223 | |
andresag | 34:f6d4a699a1ea | 224 | void EddystoneService::swapAdvertisedFrame(void) |
andresag | 34:f6d4a699a1ea | 225 | { |
andresag | 34:f6d4a699a1ea | 226 | /* This essentially works out which is the next frame to be swapped in |
andresag | 34:f6d4a699a1ea | 227 | * and updated the advertised packets. It will eventually terminate |
andresag | 34:f6d4a699a1ea | 228 | * and in the worst case the frame swapped in is the current advertised |
andresag | 34:f6d4a699a1ea | 229 | * frame. |
andresag | 34:f6d4a699a1ea | 230 | */ |
andresag | 34:f6d4a699a1ea | 231 | while (true) { |
andresag | 34:f6d4a699a1ea | 232 | currentAdvertisedFrame = (currentAdvertisedFrame + 1) % NUM_EDDYSTONE_FRAMES; |
andresag | 34:f6d4a699a1ea | 233 | |
andresag | 34:f6d4a699a1ea | 234 | if (currentAdvertisedFrame == EDDYSTONE_FRAME_URL && consecFrames[EDDYSTONE_FRAME_URL] > 0) { |
andresag | 34:f6d4a699a1ea | 235 | updateAdvertisementPacket(rawUrlFrame, urlFrame.getRawFrameSize()); |
andresag | 34:f6d4a699a1ea | 236 | return; |
andresag | 34:f6d4a699a1ea | 237 | } else if (currentAdvertisedFrame == EDDYSTONE_FRAME_UID && consecFrames[EDDYSTONE_FRAME_UID] > 0) { |
andresag | 34:f6d4a699a1ea | 238 | updateAdvertisementPacket(rawUidFrame, uidFrame.getRawFrameSize()); |
andresag | 34:f6d4a699a1ea | 239 | return; |
andresag | 34:f6d4a699a1ea | 240 | } else if (currentAdvertisedFrame == EDDYSTONE_FRAME_TLM && consecFrames[EDDYSTONE_FRAME_UID] > 0) { |
andresag | 34:f6d4a699a1ea | 241 | updateRawTLMFrame(); |
andresag | 34:f6d4a699a1ea | 242 | updateAdvertisementPacket(rawTlmFrame, tlmFrame.getRawFrameSize()); |
andresag | 34:f6d4a699a1ea | 243 | return; |
andresag | 34:f6d4a699a1ea | 244 | } |
andresag | 34:f6d4a699a1ea | 245 | } |
andresag | 34:f6d4a699a1ea | 246 | } |
andresag | 34:f6d4a699a1ea | 247 | |
andresag | 34:f6d4a699a1ea | 248 | /* Helper function that calls user-defined functions to update Battery Voltage and Temperature (if available), |
andresag | 34:f6d4a699a1ea | 249 | * then updates the raw frame data and finally updates the actual advertised packet. This operation must be |
andresag | 34:f6d4a699a1ea | 250 | * done fairly often because the TLM frame TimeSinceBoot must have a 0.1 secs resolution according to the |
andresag | 34:f6d4a699a1ea | 251 | * Eddystone specification. |
andresag | 34:f6d4a699a1ea | 252 | */ |
andresag | 34:f6d4a699a1ea | 253 | void EddystoneService::updateRawTLMFrame(void) |
andresag | 34:f6d4a699a1ea | 254 | { |
andresag | 34:f6d4a699a1ea | 255 | if (tlmBeaconTemperatureCallback != NULL) { |
andresag | 34:f6d4a699a1ea | 256 | tlmFrame.updateBeaconTemperature((*tlmBeaconTemperatureCallback)(tlmFrame.getBeaconTemperature())); |
andresag | 34:f6d4a699a1ea | 257 | } |
andresag | 34:f6d4a699a1ea | 258 | if (tlmBatteryVoltageCallback != NULL) { |
andresag | 34:f6d4a699a1ea | 259 | tlmFrame.updateBatteryVoltage((*tlmBatteryVoltageCallback)(tlmFrame.getBatteryVoltage())); |
andresag | 34:f6d4a699a1ea | 260 | } |
andresag | 34:f6d4a699a1ea | 261 | tlmFrame.updateTimeSinceBoot(timeSinceBootTimer.read_ms()); |
andresag | 34:f6d4a699a1ea | 262 | tlmFrame.constructTLMFrame(rawTlmFrame); |
andresag | 34:f6d4a699a1ea | 263 | } |
andresag | 34:f6d4a699a1ea | 264 | |
andresag | 34:f6d4a699a1ea | 265 | void EddystoneService::updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength) |
andresag | 34:f6d4a699a1ea | 266 | { |
andresag | 34:f6d4a699a1ea | 267 | ble.gap().clearAdvertisingPayload(); |
andresag | 34:f6d4a699a1ea | 268 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
andresag | 34:f6d4a699a1ea | 269 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID)); |
andresag | 34:f6d4a699a1ea | 270 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, rawFrameLength); |
andresag | 34:f6d4a699a1ea | 271 | } |
andresag | 34:f6d4a699a1ea | 272 | |
andresag | 34:f6d4a699a1ea | 273 | void EddystoneService::setupBeaconService(void) |
andresag | 34:f6d4a699a1ea | 274 | { |
andresag | 34:f6d4a699a1ea | 275 | /* Initialise arrays to hold constructed raw frames */ |
andresag | 34:f6d4a699a1ea | 276 | if (consecFrames[EDDYSTONE_FRAME_URL] > 0) { |
andresag | 34:f6d4a699a1ea | 277 | rawUrlFrame = new uint8_t[urlFrame.getRawFrameSize()]; |
andresag | 34:f6d4a699a1ea | 278 | urlFrame.constructURLFrame(rawUrlFrame, advPowerLevels[txPowerMode]); |
andresag | 34:f6d4a699a1ea | 279 | } |
andresag | 34:f6d4a699a1ea | 280 | |
andresag | 34:f6d4a699a1ea | 281 | if (consecFrames[EDDYSTONE_FRAME_UID] > 0) { |
andresag | 34:f6d4a699a1ea | 282 | rawUidFrame = new uint8_t[uidFrame.getRawFrameSize()]; |
andresag | 34:f6d4a699a1ea | 283 | uidFrame.constructUIDFrame(rawUidFrame, advPowerLevels[txPowerMode]); |
andresag | 34:f6d4a699a1ea | 284 | } |
andresag | 34:f6d4a699a1ea | 285 | |
andresag | 34:f6d4a699a1ea | 286 | if (consecFrames[EDDYSTONE_FRAME_TLM] > 0) { |
andresag | 34:f6d4a699a1ea | 287 | rawTlmFrame = new uint8_t[tlmFrame.getRawFrameSize()]; |
andresag | 34:f6d4a699a1ea | 288 | /* Do not initialise because we have to reconstruct every 0.1 secs */ |
andresag | 34:f6d4a699a1ea | 289 | } |
andresag | 34:f6d4a699a1ea | 290 | |
andresag | 34:f6d4a699a1ea | 291 | /* Configure advertisements */ |
andresag | 34:f6d4a699a1ea | 292 | ble.gap().setTxPower(radioPowerLevels[txPowerMode]); |
andresag | 34:f6d4a699a1ea | 293 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED); |
andresag | 34:f6d4a699a1ea | 294 | ble.gap().setAdvertisingInterval(beaconPeriod); |
andresag | 34:f6d4a699a1ea | 295 | ble.gap().onRadioNotification(this, &EddystoneService::radioNotificationCallback); |
andresag | 34:f6d4a699a1ea | 296 | ble.gap().initRadioNotification(); |
andresag | 34:f6d4a699a1ea | 297 | |
andresag | 34:f6d4a699a1ea | 298 | /* Set advertisement packet payload */ |
andresag | 34:f6d4a699a1ea | 299 | swapAdvertisedFrame(); |
andresag | 34:f6d4a699a1ea | 300 | |
andresag | 34:f6d4a699a1ea | 301 | /* Start advertising */ |
andresag | 34:f6d4a699a1ea | 302 | ble.gap().startAdvertising(); |
andresag | 34:f6d4a699a1ea | 303 | } |
andresag | 34:f6d4a699a1ea | 304 | |
andresag | 34:f6d4a699a1ea | 305 | void EddystoneService::setupConfigService(void) |
andresag | 34:f6d4a699a1ea | 306 | { |
andresag | 34:f6d4a699a1ea | 307 | lockStateChar = new ReadOnlyGattCharacteristic<bool>(UUID_LOCK_STATE_CHAR, &lockState); |
andresag | 34:f6d4a699a1ea | 308 | lockChar = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_LOCK_CHAR, lock); |
andresag | 34:f6d4a699a1ea | 309 | unlockChar = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_UNLOCK_CHAR, unlock); |
andresag | 34:f6d4a699a1ea | 310 | urlDataChar = new GattCharacteristic(UUID_URL_DATA_CHAR, urlFrame.getEncodedURLData(), 0, URL_DATA_MAX, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); |
andresag | 34:f6d4a699a1ea | 311 | flagsChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_FLAGS_CHAR, &flags); |
andresag | 34:f6d4a699a1ea | 312 | advPowerLevelsChar = new ReadWriteArrayGattCharacteristic<int8_t, sizeof(PowerLevels_t)>(UUID_ADV_POWER_LEVELS_CHAR, advPowerLevels); |
andresag | 34:f6d4a699a1ea | 313 | txPowerModeChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_TX_POWER_MODE_CHAR, &txPowerMode); |
andresag | 34:f6d4a699a1ea | 314 | beaconPeriodChar = new ReadWriteGattCharacteristic<uint16_t>(UUID_BEACON_PERIOD_CHAR, &beaconPeriod); |
andresag | 34:f6d4a699a1ea | 315 | resetChar = new WriteOnlyGattCharacteristic<bool>(UUID_RESET_CHAR, &resetFlag); |
andresag | 34:f6d4a699a1ea | 316 | |
andresag | 34:f6d4a699a1ea | 317 | lockChar->setWriteAuthorizationCallback(this, &EddystoneService::lockAuthorizationCallback); |
andresag | 34:f6d4a699a1ea | 318 | unlockChar->setWriteAuthorizationCallback(this, &EddystoneService::unlockAuthorizationCallback); |
andresag | 34:f6d4a699a1ea | 319 | urlDataChar->setWriteAuthorizationCallback(this, &EddystoneService::urlDataWriteAuthorizationCallback); |
andresag | 34:f6d4a699a1ea | 320 | flagsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint8_t>); |
andresag | 34:f6d4a699a1ea | 321 | advPowerLevelsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<PowerLevels_t>); |
andresag | 34:f6d4a699a1ea | 322 | txPowerModeChar->setWriteAuthorizationCallback(this, &EddystoneService::powerModeAuthorizationCallback); |
andresag | 34:f6d4a699a1ea | 323 | beaconPeriodChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint16_t>); |
andresag | 34:f6d4a699a1ea | 324 | resetChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<bool>); |
andresag | 34:f6d4a699a1ea | 325 | |
andresag | 34:f6d4a699a1ea | 326 | charTable[0] = lockStateChar; |
andresag | 34:f6d4a699a1ea | 327 | charTable[1] = lockChar; |
andresag | 34:f6d4a699a1ea | 328 | charTable[2] = unlockChar; |
andresag | 34:f6d4a699a1ea | 329 | charTable[3] = urlDataChar; |
andresag | 34:f6d4a699a1ea | 330 | charTable[4] = flagsChar; |
andresag | 34:f6d4a699a1ea | 331 | charTable[5] = advPowerLevelsChar; |
andresag | 34:f6d4a699a1ea | 332 | charTable[6] = txPowerModeChar; |
andresag | 34:f6d4a699a1ea | 333 | charTable[7] = beaconPeriodChar; |
andresag | 34:f6d4a699a1ea | 334 | charTable[8] = resetChar; |
andresag | 34:f6d4a699a1ea | 335 | |
andresag | 34:f6d4a699a1ea | 336 | GattService configService(UUID_URL_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); |
andresag | 34:f6d4a699a1ea | 337 | |
andresag | 34:f6d4a699a1ea | 338 | ble.gattServer().addService(configService); |
andresag | 34:f6d4a699a1ea | 339 | ble.gattServer().onDataWritten(this, &EddystoneService::onDataWrittenCallback); |
andresag | 34:f6d4a699a1ea | 340 | updateCharacteristicValues(); |
andresag | 34:f6d4a699a1ea | 341 | setupEddystoneConfigAdvertisements(); |
andresag | 34:f6d4a699a1ea | 342 | } |
andresag | 34:f6d4a699a1ea | 343 | |
andresag | 34:f6d4a699a1ea | 344 | void EddystoneService::freeConfigCharacteristics(void) |
andresag | 34:f6d4a699a1ea | 345 | { |
andresag | 34:f6d4a699a1ea | 346 | delete lockStateChar; |
andresag | 34:f6d4a699a1ea | 347 | delete lockChar; |
andresag | 34:f6d4a699a1ea | 348 | delete unlockChar; |
andresag | 34:f6d4a699a1ea | 349 | delete urlDataChar; |
andresag | 34:f6d4a699a1ea | 350 | delete flagsChar; |
andresag | 34:f6d4a699a1ea | 351 | delete advPowerLevelsChar; |
andresag | 34:f6d4a699a1ea | 352 | delete txPowerModeChar; |
andresag | 34:f6d4a699a1ea | 353 | delete beaconPeriodChar; |
andresag | 34:f6d4a699a1ea | 354 | delete resetChar; |
andresag | 34:f6d4a699a1ea | 355 | } |
andresag | 34:f6d4a699a1ea | 356 | |
andresag | 34:f6d4a699a1ea | 357 | void EddystoneService::freeBeaconFrames(void) |
andresag | 34:f6d4a699a1ea | 358 | { |
andresag | 34:f6d4a699a1ea | 359 | delete[] rawUrlFrame; |
andresag | 34:f6d4a699a1ea | 360 | delete[] rawUidFrame; |
andresag | 34:f6d4a699a1ea | 361 | delete[] rawTlmFrame; |
andresag | 34:f6d4a699a1ea | 362 | } |
andresag | 34:f6d4a699a1ea | 363 | |
andresag | 34:f6d4a699a1ea | 364 | void EddystoneService::radioNotificationCallback(bool radioActive) |
andresag | 34:f6d4a699a1ea | 365 | { |
andresag | 34:f6d4a699a1ea | 366 | if (radioActive) { |
andresag | 34:f6d4a699a1ea | 367 | /* Do nothing */ |
andresag | 34:f6d4a699a1ea | 368 | return; |
andresag | 34:f6d4a699a1ea | 369 | } |
andresag | 34:f6d4a699a1ea | 370 | |
andresag | 34:f6d4a699a1ea | 371 | tlmFrame.updatePduCount(); |
andresag | 34:f6d4a699a1ea | 372 | currentConsecFrames[currentAdvertisedFrame]++; |
andresag | 34:f6d4a699a1ea | 373 | |
andresag | 34:f6d4a699a1ea | 374 | if (consecFrames[currentAdvertisedFrame] > currentConsecFrames[currentAdvertisedFrame]) { |
andresag | 34:f6d4a699a1ea | 375 | if (currentAdvertisedFrame == EDDYSTONE_FRAME_TLM) { |
andresag | 34:f6d4a699a1ea | 376 | /* Update the TLM frame otherwise we will not meet the 0.1 secs resolution of |
andresag | 34:f6d4a699a1ea | 377 | * the Eddystone specification. |
andresag | 34:f6d4a699a1ea | 378 | */ |
andresag | 34:f6d4a699a1ea | 379 | updateRawTLMFrame(); |
andresag | 34:f6d4a699a1ea | 380 | updateAdvertisementPacket(rawTlmFrame, tlmFrame.getRawFrameSize()); |
andresag | 34:f6d4a699a1ea | 381 | } |
andresag | 34:f6d4a699a1ea | 382 | /* Keep advertising the same frame */ |
andresag | 34:f6d4a699a1ea | 383 | return; |
andresag | 34:f6d4a699a1ea | 384 | } |
andresag | 34:f6d4a699a1ea | 385 | |
andresag | 34:f6d4a699a1ea | 386 | currentConsecFrames[currentAdvertisedFrame] = 0; |
andresag | 34:f6d4a699a1ea | 387 | |
andresag | 34:f6d4a699a1ea | 388 | #ifdef YOTTA_CFG_MBED_OS |
andresag | 34:f6d4a699a1ea | 389 | minar::Scheduler::postCallback(this, &EddystoneService::swapAdvertisedFrame); |
andresag | 34:f6d4a699a1ea | 390 | #else |
andresag | 34:f6d4a699a1ea | 391 | swapAdvertisedFrameTimeout.attach_us(this, &EddystoneService::swapAdvertisedFrame, 1); |
andresag | 34:f6d4a699a1ea | 392 | #endif |
andresag | 34:f6d4a699a1ea | 393 | } |
andresag | 34:f6d4a699a1ea | 394 | |
andresag | 34:f6d4a699a1ea | 395 | /* |
andresag | 34:f6d4a699a1ea | 396 | * Internal helper function used to update the GATT database following any |
andresag | 34:f6d4a699a1ea | 397 | * change to the internal state of the service object. |
andresag | 34:f6d4a699a1ea | 398 | */ |
andresag | 34:f6d4a699a1ea | 399 | void EddystoneService::updateCharacteristicValues(void) |
andresag | 34:f6d4a699a1ea | 400 | { |
andresag | 34:f6d4a699a1ea | 401 | ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool)); |
andresag | 34:f6d4a699a1ea | 402 | ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength()); |
andresag | 34:f6d4a699a1ea | 403 | ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t)); |
andresag | 34:f6d4a699a1ea | 404 | ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t)); |
andresag | 34:f6d4a699a1ea | 405 | ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t)); |
andresag | 34:f6d4a699a1ea | 406 | ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 407 | ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 408 | ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 409 | } |
andresag | 34:f6d4a699a1ea | 410 | |
andresag | 34:f6d4a699a1ea | 411 | void EddystoneService::setupEddystoneConfigAdvertisements(void) |
andresag | 34:f6d4a699a1ea | 412 | { |
andresag | 34:f6d4a699a1ea | 413 | ble.gap().clearAdvertisingPayload(); |
andresag | 34:f6d4a699a1ea | 414 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
andresag | 34:f6d4a699a1ea | 415 | |
andresag | 34:f6d4a699a1ea | 416 | /* UUID is in different order in the ADV frame (!) */ |
andresag | 34:f6d4a699a1ea | 417 | uint8_t reversedServiceUUID[sizeof(UUID_URL_BEACON_SERVICE)]; |
andresag | 34:f6d4a699a1ea | 418 | for (size_t i = 0; i < sizeof(UUID_URL_BEACON_SERVICE); i++) { |
andresag | 34:f6d4a699a1ea | 419 | reversedServiceUUID[i] = UUID_URL_BEACON_SERVICE[sizeof(UUID_URL_BEACON_SERVICE) - i - 1]; |
andresag | 34:f6d4a699a1ea | 420 | } |
andresag | 34:f6d4a699a1ea | 421 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID)); |
andresag | 34:f6d4a699a1ea | 422 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG); |
andresag | 34:f6d4a699a1ea | 423 | ble.gap().accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME)); |
andresag | 34:f6d4a699a1ea | 424 | ble.gap().accumulateScanResponse( |
andresag | 34:f6d4a699a1ea | 425 | GapAdvertisingData::TX_POWER_LEVEL, |
andresag | 34:f6d4a699a1ea | 426 | reinterpret_cast<uint8_t *>(&advPowerLevels[TX_POWER_MODE_LOW]), |
andresag | 34:f6d4a699a1ea | 427 | sizeof(uint8_t)); |
andresag | 34:f6d4a699a1ea | 428 | |
andresag | 34:f6d4a699a1ea | 429 | ble.gap().setTxPower(radioPowerLevels[txPowerMode]); |
andresag | 34:f6d4a699a1ea | 430 | ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME)); |
andresag | 34:f6d4a699a1ea | 431 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
andresag | 34:f6d4a699a1ea | 432 | ble.gap().setAdvertisingInterval(advConfigInterval); |
andresag | 34:f6d4a699a1ea | 433 | ble.gap().startAdvertising(); |
andresag | 34:f6d4a699a1ea | 434 | } |
andresag | 34:f6d4a699a1ea | 435 | |
andresag | 34:f6d4a699a1ea | 436 | void EddystoneService::lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) |
andresag | 34:f6d4a699a1ea | 437 | { |
andresag | 34:f6d4a699a1ea | 438 | if (lockState) { |
andresag | 34:f6d4a699a1ea | 439 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; |
andresag | 34:f6d4a699a1ea | 440 | } else if (authParams->len != sizeof(Lock_t)) { |
andresag | 34:f6d4a699a1ea | 441 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; |
andresag | 34:f6d4a699a1ea | 442 | } else if (authParams->offset != 0) { |
andresag | 34:f6d4a699a1ea | 443 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; |
andresag | 34:f6d4a699a1ea | 444 | } else { |
andresag | 34:f6d4a699a1ea | 445 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
andresag | 34:f6d4a699a1ea | 446 | } |
andresag | 34:f6d4a699a1ea | 447 | } |
andresag | 34:f6d4a699a1ea | 448 | |
andresag | 34:f6d4a699a1ea | 449 | void EddystoneService::unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) |
andresag | 34:f6d4a699a1ea | 450 | { |
andresag | 34:f6d4a699a1ea | 451 | if (!lockState && (authParams->len == sizeof(Lock_t))) { |
andresag | 34:f6d4a699a1ea | 452 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
andresag | 34:f6d4a699a1ea | 453 | } else if (authParams->len != sizeof(Lock_t)) { |
andresag | 34:f6d4a699a1ea | 454 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; |
andresag | 34:f6d4a699a1ea | 455 | } else if (authParams->offset != 0) { |
andresag | 34:f6d4a699a1ea | 456 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; |
andresag | 34:f6d4a699a1ea | 457 | } else if (memcmp(authParams->data, lock, sizeof(Lock_t)) != 0) { |
andresag | 34:f6d4a699a1ea | 458 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; |
andresag | 34:f6d4a699a1ea | 459 | } else { |
andresag | 34:f6d4a699a1ea | 460 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
andresag | 34:f6d4a699a1ea | 461 | } |
andresag | 34:f6d4a699a1ea | 462 | } |
andresag | 34:f6d4a699a1ea | 463 | |
andresag | 34:f6d4a699a1ea | 464 | void EddystoneService::urlDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams) |
andresag | 34:f6d4a699a1ea | 465 | { |
andresag | 34:f6d4a699a1ea | 466 | if (lockState) { |
andresag | 34:f6d4a699a1ea | 467 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; |
andresag | 34:f6d4a699a1ea | 468 | } else if (authParams->offset != 0) { |
andresag | 34:f6d4a699a1ea | 469 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; |
andresag | 34:f6d4a699a1ea | 470 | } else { |
andresag | 34:f6d4a699a1ea | 471 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
andresag | 34:f6d4a699a1ea | 472 | } |
andresag | 34:f6d4a699a1ea | 473 | } |
andresag | 34:f6d4a699a1ea | 474 | |
andresag | 34:f6d4a699a1ea | 475 | void EddystoneService::powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams) |
andresag | 34:f6d4a699a1ea | 476 | { |
andresag | 34:f6d4a699a1ea | 477 | if (lockState) { |
andresag | 34:f6d4a699a1ea | 478 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; |
andresag | 34:f6d4a699a1ea | 479 | } else if (authParams->len != sizeof(uint8_t)) { |
andresag | 34:f6d4a699a1ea | 480 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; |
andresag | 34:f6d4a699a1ea | 481 | } else if (authParams->offset != 0) { |
andresag | 34:f6d4a699a1ea | 482 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; |
andresag | 34:f6d4a699a1ea | 483 | } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) { |
andresag | 34:f6d4a699a1ea | 484 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED; |
andresag | 34:f6d4a699a1ea | 485 | } else { |
andresag | 34:f6d4a699a1ea | 486 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
andresag | 34:f6d4a699a1ea | 487 | } |
andresag | 34:f6d4a699a1ea | 488 | } |
andresag | 34:f6d4a699a1ea | 489 | |
andresag | 34:f6d4a699a1ea | 490 | template <typename T> |
andresag | 34:f6d4a699a1ea | 491 | void EddystoneService::basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams) |
andresag | 34:f6d4a699a1ea | 492 | { |
andresag | 34:f6d4a699a1ea | 493 | if (lockState) { |
andresag | 34:f6d4a699a1ea | 494 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; |
andresag | 34:f6d4a699a1ea | 495 | } else if (authParams->len != sizeof(T)) { |
andresag | 34:f6d4a699a1ea | 496 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; |
andresag | 34:f6d4a699a1ea | 497 | } else if (authParams->offset != 0) { |
andresag | 34:f6d4a699a1ea | 498 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; |
andresag | 34:f6d4a699a1ea | 499 | } else { |
andresag | 34:f6d4a699a1ea | 500 | authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
andresag | 34:f6d4a699a1ea | 501 | } |
andresag | 34:f6d4a699a1ea | 502 | } |
andresag | 34:f6d4a699a1ea | 503 | |
andresag | 34:f6d4a699a1ea | 504 | /* |
andresag | 34:f6d4a699a1ea | 505 | * This callback is invoked when a GATT client attempts to modify any of the |
andresag | 34:f6d4a699a1ea | 506 | * characteristics of this service. Attempts to do so are also applied to |
andresag | 34:f6d4a699a1ea | 507 | * the internal state of this service object. |
andresag | 34:f6d4a699a1ea | 508 | */ |
andresag | 34:f6d4a699a1ea | 509 | void EddystoneService::onDataWrittenCallback(const GattWriteCallbackParams *writeParams) |
andresag | 34:f6d4a699a1ea | 510 | { |
andresag | 34:f6d4a699a1ea | 511 | uint16_t handle = writeParams->handle; |
andresag | 34:f6d4a699a1ea | 512 | |
andresag | 34:f6d4a699a1ea | 513 | if (handle == lockChar->getValueHandle()) { |
andresag | 34:f6d4a699a1ea | 514 | memcpy(lock, writeParams->data, sizeof(Lock_t)); |
andresag | 34:f6d4a699a1ea | 515 | /* Set the state to be locked by the lock code (note: zeros are a valid lock) */ |
andresag | 34:f6d4a699a1ea | 516 | lockState = true; |
andresag | 34:f6d4a699a1ea | 517 | ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 518 | ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool)); |
andresag | 34:f6d4a699a1ea | 519 | } else if (handle == unlockChar->getValueHandle()) { |
andresag | 34:f6d4a699a1ea | 520 | /* Validated earlier */ |
andresag | 34:f6d4a699a1ea | 521 | lockState = false; |
andresag | 34:f6d4a699a1ea | 522 | ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 523 | ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool)); |
andresag | 34:f6d4a699a1ea | 524 | } else if (handle == urlDataChar->getValueHandle()) { |
andresag | 34:f6d4a699a1ea | 525 | urlFrame.setEncodedURLData(writeParams->data, writeParams->len); |
andresag | 34:f6d4a699a1ea | 526 | ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength()); |
andresag | 34:f6d4a699a1ea | 527 | } else if (handle == flagsChar->getValueHandle()) { |
andresag | 34:f6d4a699a1ea | 528 | flags = *(writeParams->data); |
andresag | 34:f6d4a699a1ea | 529 | ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t)); |
andresag | 34:f6d4a699a1ea | 530 | } else if (handle == advPowerLevelsChar->getValueHandle()) { |
andresag | 34:f6d4a699a1ea | 531 | memcpy(advPowerLevels, writeParams->data, sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 532 | ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 533 | } else if (handle == txPowerModeChar->getValueHandle()) { |
andresag | 34:f6d4a699a1ea | 534 | txPowerMode = *(writeParams->data); |
andresag | 34:f6d4a699a1ea | 535 | ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t)); |
andresag | 34:f6d4a699a1ea | 536 | } else if (handle == beaconPeriodChar->getValueHandle()) { |
andresag | 34:f6d4a699a1ea | 537 | uint16_t tmpBeaconPeriod = correctAdvertisementPeriod(*((uint16_t *)(writeParams->data))); |
andresag | 34:f6d4a699a1ea | 538 | if (tmpBeaconPeriod != beaconPeriod) { |
andresag | 34:f6d4a699a1ea | 539 | beaconPeriod = tmpBeaconPeriod; |
andresag | 34:f6d4a699a1ea | 540 | ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t)); |
andresag | 34:f6d4a699a1ea | 541 | } |
andresag | 34:f6d4a699a1ea | 542 | } else if (handle == resetChar->getValueHandle() && (*((uint8_t *)writeParams->data) != 0)) { |
andresag | 34:f6d4a699a1ea | 543 | /* Reset characteristics to default values */ |
andresag | 34:f6d4a699a1ea | 544 | flags = 0; |
andresag | 34:f6d4a699a1ea | 545 | txPowerMode = TX_POWER_MODE_LOW; |
andresag | 34:f6d4a699a1ea | 546 | beaconPeriod = DEFAULT_BEACON_PERIOD_MSEC; |
andresag | 34:f6d4a699a1ea | 547 | |
andresag | 34:f6d4a699a1ea | 548 | urlFrame.setURLData(DEFAULT_URL); |
andresag | 34:f6d4a699a1ea | 549 | memset(lock, 0, sizeof(Lock_t)); |
andresag | 34:f6d4a699a1ea | 550 | |
andresag | 34:f6d4a699a1ea | 551 | ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength()); |
andresag | 34:f6d4a699a1ea | 552 | ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t)); |
andresag | 34:f6d4a699a1ea | 553 | ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t)); |
andresag | 34:f6d4a699a1ea | 554 | ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t)); |
andresag | 34:f6d4a699a1ea | 555 | ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t)); |
andresag | 34:f6d4a699a1ea | 556 | } |
andresag | 34:f6d4a699a1ea | 557 | } |
andresag | 34:f6d4a699a1ea | 558 | |
andresag | 34:f6d4a699a1ea | 559 | uint16_t EddystoneService::correctAdvertisementPeriod(uint16_t beaconPeriodIn) const |
andresag | 34:f6d4a699a1ea | 560 | { |
andresag | 34:f6d4a699a1ea | 561 | /* Re-map beaconPeriod to within permissible bounds if necessary. */ |
andresag | 34:f6d4a699a1ea | 562 | if (beaconPeriodIn != 0) { |
andresag | 34:f6d4a699a1ea | 563 | if (beaconPeriodIn < ble.gap().getMinAdvertisingInterval()) { |
andresag | 34:f6d4a699a1ea | 564 | return ble.gap().getMinAdvertisingInterval(); |
andresag | 34:f6d4a699a1ea | 565 | } else if (beaconPeriodIn > ble.gap().getMaxAdvertisingInterval()) { |
andresag | 34:f6d4a699a1ea | 566 | return ble.gap().getMaxAdvertisingInterval(); |
andresag | 34:f6d4a699a1ea | 567 | } |
andresag | 34:f6d4a699a1ea | 568 | } |
andresag | 34:f6d4a699a1ea | 569 | return beaconPeriodIn; |
andresag | 34:f6d4a699a1ea | 570 | } |