project for eddystone

Dependencies:   BLE_API mbed nRF51822

Fork of BLE_URIBeacon by Bluetooth Low Energy

Committer:
rgrover1
Date:
Fri Feb 13 13:05:49 2015 +0000
Revision:
13:b82d8db73633
Parent:
11:c77cc2b74101
Child:
14:868a1207022d
Updating to Bill's version of the URIBeacon app

Who changed what in which revision?

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