Import from github.com/google/uribeacon/beacons/mbed

Dependencies:   BLE_API mbed nRF51822

Committer:
schilit
Date:
Wed Feb 25 21:44:47 2015 +0000
Revision:
0:9033b372f4fe
Import from github

Who changed what in which revision?

UserRevisionLine numberNew 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 }