Import from github.com/google/uribeacon/beacons/mbed
Dependencies: BLE_API mbed nRF51822
main.cpp@0:9033b372f4fe, 2015-02-25 (annotated)
- Committer:
- schilit
- Date:
- Wed Feb 25 21:44:47 2015 +0000
- Revision:
- 0:9033b372f4fe
Import from github
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
schilit | 0:9033b372f4fe | 1 | /* |
schilit | 0:9033b372f4fe | 2 | * Copyright 2014-2015 Google Inc. All rights reserved. |
schilit | 0:9033b372f4fe | 3 | * |
schilit | 0:9033b372f4fe | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
schilit | 0:9033b372f4fe | 5 | * you may not use this file except in compliance with the License. |
schilit | 0:9033b372f4fe | 6 | * You may obtain a copy of the License at |
schilit | 0:9033b372f4fe | 7 | * |
schilit | 0:9033b372f4fe | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
schilit | 0:9033b372f4fe | 9 | * |
schilit | 0:9033b372f4fe | 10 | * Unless required by applicable law or agreed to in writing, software |
schilit | 0:9033b372f4fe | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
schilit | 0:9033b372f4fe | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
schilit | 0:9033b372f4fe | 13 | * See the License for the specific language governing permissions and |
schilit | 0:9033b372f4fe | 14 | * limitations under the License. |
schilit | 0:9033b372f4fe | 15 | */ |
schilit | 0:9033b372f4fe | 16 | |
schilit | 0:9033b372f4fe | 17 | #include <stdint.h> |
schilit | 0:9033b372f4fe | 18 | #include <stddef.h> |
schilit | 0:9033b372f4fe | 19 | #include <nrf_error.h> |
schilit | 0:9033b372f4fe | 20 | #include "mbed.h" |
schilit | 0:9033b372f4fe | 21 | #include "BLEDevice.h" |
schilit | 0:9033b372f4fe | 22 | #include "URIBeaconConfigService.h" |
schilit | 0:9033b372f4fe | 23 | #include "DFUService.h" |
schilit | 0:9033b372f4fe | 24 | #include "pstorage.h" |
schilit | 0:9033b372f4fe | 25 | #include "DeviceInformationService.h" |
schilit | 0:9033b372f4fe | 26 | |
schilit | 0:9033b372f4fe | 27 | // Struct to hold persistent data across power cycles |
schilit | 0:9033b372f4fe | 28 | struct PersistentData_t { |
schilit | 0:9033b372f4fe | 29 | uint32_t magic; |
schilit | 0:9033b372f4fe | 30 | URIBeaconConfigService::Params_t params; |
schilit | 0:9033b372f4fe | 31 | uint8_t pad[4]; |
schilit | 0:9033b372f4fe | 32 | } __attribute__ ((aligned (4))); |
schilit | 0:9033b372f4fe | 33 | |
schilit | 0:9033b372f4fe | 34 | static const int PERSISTENT_DATA_ALIGNED_SIZE = sizeof(PersistentData_t); |
schilit | 0:9033b372f4fe | 35 | // Seconds after power-on that config service is available. |
schilit | 0:9033b372f4fe | 36 | static const int ADVERTISING_TIMEOUT_SECONDS = 60; |
schilit | 0:9033b372f4fe | 37 | // Advertising interval for config service. |
schilit | 0:9033b372f4fe | 38 | static const int ADVERTISING_INTERVAL_MSEC = 1000; |
schilit | 0:9033b372f4fe | 39 | // Maximum size of service data in ADV packets |
schilit | 0:9033b372f4fe | 40 | static const int SERVICE_DATA_MAX = 31; |
schilit | 0:9033b372f4fe | 41 | // Magic that identifies persistent storage |
schilit | 0:9033b372f4fe | 42 | static const uint32_t MAGIC = 0x1BEAC000; |
schilit | 0:9033b372f4fe | 43 | // Values for ADV packets related to firmware levels |
schilit | 0:9033b372f4fe | 44 | static URIBeaconConfigService::PowerLevels_t defaultAdvPowerLevels = {-20, -4, 0, 10}; |
schilit | 0:9033b372f4fe | 45 | // Values for setTxPower() indexed by power mode. |
schilit | 0:9033b372f4fe | 46 | static const int8_t firmwarePowerLevels[] = {-20, -4, 0, 10}; |
schilit | 0:9033b372f4fe | 47 | |
schilit | 0:9033b372f4fe | 48 | BLEDevice ble; |
schilit | 0:9033b372f4fe | 49 | URIBeaconConfigService *uriBeaconConfig; |
schilit | 0:9033b372f4fe | 50 | pstorage_handle_t pstorageHandle; |
schilit | 0:9033b372f4fe | 51 | PersistentData_t persistentData; |
schilit | 0:9033b372f4fe | 52 | |
schilit | 0:9033b372f4fe | 53 | /* LEDs for indication */ |
schilit | 0:9033b372f4fe | 54 | DigitalOut connectionStateLed(LED1); |
schilit | 0:9033b372f4fe | 55 | DigitalOut advertisingStateLed(LED2); |
schilit | 0:9033b372f4fe | 56 | |
schilit | 0:9033b372f4fe | 57 | void blink(int count) { |
schilit | 0:9033b372f4fe | 58 | for (int i = 0; i <= count; i++) { |
schilit | 0:9033b372f4fe | 59 | advertisingStateLed = !advertisingStateLed; |
schilit | 0:9033b372f4fe | 60 | wait(0.2); |
schilit | 0:9033b372f4fe | 61 | advertisingStateLed = !advertisingStateLed; |
schilit | 0:9033b372f4fe | 62 | wait(0.2); |
schilit | 0:9033b372f4fe | 63 | } |
schilit | 0:9033b372f4fe | 64 | } |
schilit | 0:9033b372f4fe | 65 | |
schilit | 0:9033b372f4fe | 66 | /* Dummy callback handler needed by Nordic's pstorage module. */ |
schilit | 0:9033b372f4fe | 67 | void pstorageNotificationCallback(pstorage_handle_t *p_handle, |
schilit | 0:9033b372f4fe | 68 | uint8_t op_code, |
schilit | 0:9033b372f4fe | 69 | uint32_t result, |
schilit | 0:9033b372f4fe | 70 | uint8_t * p_data, |
schilit | 0:9033b372f4fe | 71 | uint32_t data_len) { |
schilit | 0:9033b372f4fe | 72 | /* APP_ERROR_CHECK(result); */ |
schilit | 0:9033b372f4fe | 73 | } |
schilit | 0:9033b372f4fe | 74 | |
schilit | 0:9033b372f4fe | 75 | void pstorageLoad() { |
schilit | 0:9033b372f4fe | 76 | pstorage_init(); |
schilit | 0:9033b372f4fe | 77 | pstorage_module_param_t pstorageParams = { |
schilit | 0:9033b372f4fe | 78 | .cb = pstorageNotificationCallback, |
schilit | 0:9033b372f4fe | 79 | .block_size = PERSISTENT_DATA_ALIGNED_SIZE, |
schilit | 0:9033b372f4fe | 80 | .block_count = 1 |
schilit | 0:9033b372f4fe | 81 | }; |
schilit | 0:9033b372f4fe | 82 | pstorage_register(&pstorageParams, &pstorageHandle); |
schilit | 0:9033b372f4fe | 83 | if (pstorage_load(reinterpret_cast<uint8_t *>(&persistentData), |
schilit | 0:9033b372f4fe | 84 | &pstorageHandle, PERSISTENT_DATA_ALIGNED_SIZE, 0) != NRF_SUCCESS) { |
schilit | 0:9033b372f4fe | 85 | // On failure zero out and let the service reset to defaults |
schilit | 0:9033b372f4fe | 86 | memset(&persistentData, 0, sizeof(PersistentData_t)); |
schilit | 0:9033b372f4fe | 87 | } |
schilit | 0:9033b372f4fe | 88 | } |
schilit | 0:9033b372f4fe | 89 | |
schilit | 0:9033b372f4fe | 90 | |
schilit | 0:9033b372f4fe | 91 | void pstorageSave() { |
schilit | 0:9033b372f4fe | 92 | if (persistentData.magic != MAGIC) { |
schilit | 0:9033b372f4fe | 93 | persistentData.magic = MAGIC; |
schilit | 0:9033b372f4fe | 94 | pstorage_store(&pstorageHandle, |
schilit | 0:9033b372f4fe | 95 | reinterpret_cast<uint8_t *>(&persistentData), |
schilit | 0:9033b372f4fe | 96 | sizeof(PersistentData_t), |
schilit | 0:9033b372f4fe | 97 | 0 /* offset */); |
schilit | 0:9033b372f4fe | 98 | } else { |
schilit | 0:9033b372f4fe | 99 | pstorage_update(&pstorageHandle, |
schilit | 0:9033b372f4fe | 100 | reinterpret_cast<uint8_t *>(&persistentData), |
schilit | 0:9033b372f4fe | 101 | sizeof(PersistentData_t), |
schilit | 0:9033b372f4fe | 102 | 0 /* offset */); |
schilit | 0:9033b372f4fe | 103 | } |
schilit | 0:9033b372f4fe | 104 | } |
schilit | 0:9033b372f4fe | 105 | |
schilit | 0:9033b372f4fe | 106 | void startAdvertisingUriBeaconConfig() { |
schilit | 0:9033b372f4fe | 107 | char DEVICE_NAME[] = "mUriBeacon Config"; |
schilit | 0:9033b372f4fe | 108 | |
schilit | 0:9033b372f4fe | 109 | ble.clearAdvertisingPayload(); |
schilit | 0:9033b372f4fe | 110 | |
schilit | 0:9033b372f4fe | 111 | // Stops advertising the UriBeacon Config Service after a delay |
schilit | 0:9033b372f4fe | 112 | ble.setAdvertisingTimeout(ADVERTISING_TIMEOUT_SECONDS); |
schilit | 0:9033b372f4fe | 113 | |
schilit | 0:9033b372f4fe | 114 | ble.accumulateAdvertisingPayload( |
schilit | 0:9033b372f4fe | 115 | GapAdvertisingData::BREDR_NOT_SUPPORTED | |
schilit | 0:9033b372f4fe | 116 | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
schilit | 0:9033b372f4fe | 117 | |
schilit | 0:9033b372f4fe | 118 | // UUID is in different order in the ADV frame (!) |
schilit | 0:9033b372f4fe | 119 | uint8_t reversedServiceUUID[sizeof(UUID_URI_BEACON_SERVICE)]; |
schilit | 0:9033b372f4fe | 120 | for (unsigned int i = 0; i < sizeof(UUID_URI_BEACON_SERVICE); i++) { |
schilit | 0:9033b372f4fe | 121 | reversedServiceUUID[i] = |
schilit | 0:9033b372f4fe | 122 | UUID_URI_BEACON_SERVICE[sizeof(UUID_URI_BEACON_SERVICE) - i - 1]; |
schilit | 0:9033b372f4fe | 123 | } |
schilit | 0:9033b372f4fe | 124 | ble.accumulateAdvertisingPayload( |
schilit | 0:9033b372f4fe | 125 | GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, |
schilit | 0:9033b372f4fe | 126 | reversedServiceUUID, |
schilit | 0:9033b372f4fe | 127 | sizeof(reversedServiceUUID)); |
schilit | 0:9033b372f4fe | 128 | |
schilit | 0:9033b372f4fe | 129 | ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG); |
schilit | 0:9033b372f4fe | 130 | ble.accumulateScanResponse( |
schilit | 0:9033b372f4fe | 131 | GapAdvertisingData::COMPLETE_LOCAL_NAME, |
schilit | 0:9033b372f4fe | 132 | reinterpret_cast<uint8_t *>(&DEVICE_NAME), |
schilit | 0:9033b372f4fe | 133 | sizeof(DEVICE_NAME)); |
schilit | 0:9033b372f4fe | 134 | ble.accumulateScanResponse( |
schilit | 0:9033b372f4fe | 135 | GapAdvertisingData::TX_POWER_LEVEL, |
schilit | 0:9033b372f4fe | 136 | reinterpret_cast<uint8_t *>( |
schilit | 0:9033b372f4fe | 137 | &defaultAdvPowerLevels[URIBeaconConfigService::TX_POWER_MODE_LOW]), |
schilit | 0:9033b372f4fe | 138 | sizeof(uint8_t)); |
schilit | 0:9033b372f4fe | 139 | |
schilit | 0:9033b372f4fe | 140 | ble.setTxPower( |
schilit | 0:9033b372f4fe | 141 | firmwarePowerLevels[URIBeaconConfigService::TX_POWER_MODE_LOW]); |
schilit | 0:9033b372f4fe | 142 | |
schilit | 0:9033b372f4fe | 143 | ble.setDeviceName(reinterpret_cast<uint8_t *>(&DEVICE_NAME)); |
schilit | 0:9033b372f4fe | 144 | ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
schilit | 0:9033b372f4fe | 145 | ble.setAdvertisingInterval( |
schilit | 0:9033b372f4fe | 146 | Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(ADVERTISING_INTERVAL_MSEC)); |
schilit | 0:9033b372f4fe | 147 | ble.startAdvertising(); |
schilit | 0:9033b372f4fe | 148 | } |
schilit | 0:9033b372f4fe | 149 | |
schilit | 0:9033b372f4fe | 150 | |
schilit | 0:9033b372f4fe | 151 | void startAdvertisingUriBeacon() { |
schilit | 0:9033b372f4fe | 152 | uint8_t serviceData[SERVICE_DATA_MAX]; |
schilit | 0:9033b372f4fe | 153 | int serviceDataLen = 0; |
schilit | 0:9033b372f4fe | 154 | |
schilit | 0:9033b372f4fe | 155 | advertisingStateLed = 1; |
schilit | 0:9033b372f4fe | 156 | connectionStateLed = 1; |
schilit | 0:9033b372f4fe | 157 | |
schilit | 0:9033b372f4fe | 158 | ble.shutdown(); |
schilit | 0:9033b372f4fe | 159 | ble.init(); |
schilit | 0:9033b372f4fe | 160 | |
schilit | 0:9033b372f4fe | 161 | // Fields from the Service |
schilit | 0:9033b372f4fe | 162 | int beaconPeriod = persistentData.params.beaconPeriod; |
schilit | 0:9033b372f4fe | 163 | int txPowerMode = persistentData.params.txPowerMode; |
schilit | 0:9033b372f4fe | 164 | int uriDataLength = persistentData.params.uriDataLength; |
schilit | 0:9033b372f4fe | 165 | URIBeaconConfigService::UriData_t &uriData = persistentData.params.uriData; |
schilit | 0:9033b372f4fe | 166 | URIBeaconConfigService::PowerLevels_t &advPowerLevels = |
schilit | 0:9033b372f4fe | 167 | persistentData.params.advPowerLevels; |
schilit | 0:9033b372f4fe | 168 | uint8_t flags = persistentData.params.flags; |
schilit | 0:9033b372f4fe | 169 | |
schilit | 0:9033b372f4fe | 170 | pstorageSave(); |
schilit | 0:9033b372f4fe | 171 | |
schilit | 0:9033b372f4fe | 172 | delete uriBeaconConfig; |
schilit | 0:9033b372f4fe | 173 | uriBeaconConfig = NULL; |
schilit | 0:9033b372f4fe | 174 | |
schilit | 0:9033b372f4fe | 175 | ble.clearAdvertisingPayload(); |
schilit | 0:9033b372f4fe | 176 | ble.setTxPower(firmwarePowerLevels[txPowerMode]); |
schilit | 0:9033b372f4fe | 177 | |
schilit | 0:9033b372f4fe | 178 | ble.setAdvertisingType( |
schilit | 0:9033b372f4fe | 179 | GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED); |
schilit | 0:9033b372f4fe | 180 | |
schilit | 0:9033b372f4fe | 181 | ble.setAdvertisingInterval( |
schilit | 0:9033b372f4fe | 182 | Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(beaconPeriod)); |
schilit | 0:9033b372f4fe | 183 | |
schilit | 0:9033b372f4fe | 184 | ble.accumulateAdvertisingPayload( |
schilit | 0:9033b372f4fe | 185 | GapAdvertisingData::BREDR_NOT_SUPPORTED | |
schilit | 0:9033b372f4fe | 186 | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
schilit | 0:9033b372f4fe | 187 | |
schilit | 0:9033b372f4fe | 188 | ble.accumulateAdvertisingPayload( |
schilit | 0:9033b372f4fe | 189 | GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, |
schilit | 0:9033b372f4fe | 190 | sizeof(BEACON_UUID)); |
schilit | 0:9033b372f4fe | 191 | |
schilit | 0:9033b372f4fe | 192 | serviceData[serviceDataLen++] = BEACON_UUID[0]; |
schilit | 0:9033b372f4fe | 193 | serviceData[serviceDataLen++] = BEACON_UUID[1]; |
schilit | 0:9033b372f4fe | 194 | serviceData[serviceDataLen++] = flags; |
schilit | 0:9033b372f4fe | 195 | serviceData[serviceDataLen++] = advPowerLevels[txPowerMode]; |
schilit | 0:9033b372f4fe | 196 | for (int j=0; j < uriDataLength; j++) { |
schilit | 0:9033b372f4fe | 197 | serviceData[serviceDataLen++] = uriData[j]; |
schilit | 0:9033b372f4fe | 198 | } |
schilit | 0:9033b372f4fe | 199 | |
schilit | 0:9033b372f4fe | 200 | ble.accumulateAdvertisingPayload( |
schilit | 0:9033b372f4fe | 201 | GapAdvertisingData::SERVICE_DATA, |
schilit | 0:9033b372f4fe | 202 | serviceData, serviceDataLen); |
schilit | 0:9033b372f4fe | 203 | |
schilit | 0:9033b372f4fe | 204 | ble.startAdvertising(); |
schilit | 0:9033b372f4fe | 205 | } |
schilit | 0:9033b372f4fe | 206 | |
schilit | 0:9033b372f4fe | 207 | // After advertising timeout, stop config and switch to UriBeacon |
schilit | 0:9033b372f4fe | 208 | void timeout(void) { |
schilit | 0:9033b372f4fe | 209 | Gap::GapState_t state; |
schilit | 0:9033b372f4fe | 210 | state = ble.getGapState(); |
schilit | 0:9033b372f4fe | 211 | if (!state.connected) { |
schilit | 0:9033b372f4fe | 212 | startAdvertisingUriBeacon(); |
schilit | 0:9033b372f4fe | 213 | } |
schilit | 0:9033b372f4fe | 214 | } |
schilit | 0:9033b372f4fe | 215 | |
schilit | 0:9033b372f4fe | 216 | // When connected to config service, change the LEDs |
schilit | 0:9033b372f4fe | 217 | void connectionCallback(Gap::Handle_t handle, |
schilit | 0:9033b372f4fe | 218 | Gap::addr_type_t peerAddrType, |
schilit | 0:9033b372f4fe | 219 | const Gap::address_t peerAddr, |
schilit | 0:9033b372f4fe | 220 | const Gap::ConnectionParams_t *params) { |
schilit | 0:9033b372f4fe | 221 | advertisingStateLed = 1; |
schilit | 0:9033b372f4fe | 222 | connectionStateLed = 0; |
schilit | 0:9033b372f4fe | 223 | } |
schilit | 0:9033b372f4fe | 224 | |
schilit | 0:9033b372f4fe | 225 | // When disconnected from config service, start advertising UriBeacon |
schilit | 0:9033b372f4fe | 226 | void disconnectionCallback(Gap::Handle_t handle, |
schilit | 0:9033b372f4fe | 227 | Gap::DisconnectionReason_t reason) { |
schilit | 0:9033b372f4fe | 228 | advertisingStateLed = 0; // on |
schilit | 0:9033b372f4fe | 229 | connectionStateLed = 1; // off |
schilit | 0:9033b372f4fe | 230 | startAdvertisingUriBeacon(); |
schilit | 0:9033b372f4fe | 231 | } |
schilit | 0:9033b372f4fe | 232 | |
schilit | 0:9033b372f4fe | 233 | int main(void) { |
schilit | 0:9033b372f4fe | 234 | URIBeaconConfigService::UriData_t uriData = { |
schilit | 0:9033b372f4fe | 235 | // http://uribeacon.org |
schilit | 0:9033b372f4fe | 236 | 0x02, 'u', 'r', 'i', 'b', 'e', 'a', 'c', 'o', 'n', 0x08 |
schilit | 0:9033b372f4fe | 237 | }; |
schilit | 0:9033b372f4fe | 238 | int uriDataLength = 11; |
schilit | 0:9033b372f4fe | 239 | |
schilit | 0:9033b372f4fe | 240 | advertisingStateLed = 0; // on |
schilit | 0:9033b372f4fe | 241 | connectionStateLed = 1; // off |
schilit | 0:9033b372f4fe | 242 | |
schilit | 0:9033b372f4fe | 243 | ble.init(); |
schilit | 0:9033b372f4fe | 244 | ble.onDisconnection(disconnectionCallback); |
schilit | 0:9033b372f4fe | 245 | ble.onConnection(connectionCallback); |
schilit | 0:9033b372f4fe | 246 | // Advertising timeout |
schilit | 0:9033b372f4fe | 247 | ble.onTimeout(timeout); |
schilit | 0:9033b372f4fe | 248 | |
schilit | 0:9033b372f4fe | 249 | pstorageLoad(); |
schilit | 0:9033b372f4fe | 250 | bool resetToDefaults = persistentData.magic != MAGIC; |
schilit | 0:9033b372f4fe | 251 | uriBeaconConfig = new URIBeaconConfigService( |
schilit | 0:9033b372f4fe | 252 | ble, persistentData.params, resetToDefaults, |
schilit | 0:9033b372f4fe | 253 | uriData, uriDataLength, defaultAdvPowerLevels); |
schilit | 0:9033b372f4fe | 254 | if (!uriBeaconConfig->configuredSuccessfully()) { |
schilit | 0:9033b372f4fe | 255 | error("failed to accommodate URI"); |
schilit | 0:9033b372f4fe | 256 | } |
schilit | 0:9033b372f4fe | 257 | |
schilit | 0:9033b372f4fe | 258 | // Setup auxiliary services to allow over-the-air firmware updates, etc |
schilit | 0:9033b372f4fe | 259 | DFUService dfu(ble); |
schilit | 0:9033b372f4fe | 260 | DeviceInformationService deviceInfo( |
schilit | 0:9033b372f4fe | 261 | ble, "ARM", "UriBeacon", "SN1", "hw-rev1", "fw-rev1", "soft-rev1"); |
schilit | 0:9033b372f4fe | 262 | |
schilit | 0:9033b372f4fe | 263 | |
schilit | 0:9033b372f4fe | 264 | startAdvertisingUriBeaconConfig(); |
schilit | 0:9033b372f4fe | 265 | |
schilit | 0:9033b372f4fe | 266 | while (true) { |
schilit | 0:9033b372f4fe | 267 | ble.waitForEvent(); |
schilit | 0:9033b372f4fe | 268 | } |
schilit | 0:9033b372f4fe | 269 | } |