The Uniform Resource Identifier Beacon (UriBeacon) defines Bluetooth 4.0 Advertisement Data that contain Web URIs. URIBeacon provides a way for Bluetooth Low Energy devices to discover nearby URIs, for example, provides a way for a user to discover a short URL and then download it on their smartphone.
Dependencies: BLE_API mbed nRF51822
Fork of BLE_PhysicalWeb by
This example demonstrates how to set up and initialize a basic URI Beacon. For a more advanced example of using a URI beacon please see the BLE_PhysicalWeb project. The Google Github Page also gives a great description of what UriBeacons are and how to use them.
Basic Details
URI Beacons are a standard way of providing a URI link in a BLE advertising packet. Website URL's are the most popular URI's. The goal of URI Beacons are to act as a bridge between the physical and digital worlds. Since the URI can be any web capable link the possibilities for use are really endless. The UriBeacons can be thought of as a natural extension of and more useful version of QR codes.
Smartphone App Links
Walkthrough of Physical Web application
Size of URI
The UriBeacon has one purpose, to advertise a web link. Because of the nature of BLE these web links have to be small. In order to provide a nice balance of small and useful the UriBeacon specification has abstracted out the representation of the URI prefix('http://www.', 'https://www.' , ...etc) and suffix ('.com','.org','.edu','.gov' ...etc) to a single byte each. Of the 27 bytes available for a usual BLE payload the UriBeacon has 19 bytes available for the URI. Of these 19bytes one byte must be given to the prefix. That leaves 18 bytes to fit the address and the suffix into. If the suffix used is not one of the standard ones in the UriBeacon specification then each letter will take up 1 byte instead of the entire suffix being abbreviated into a single byte.
Here are the acceptable abbreviations currently available
PreFix | Suffix |
---|---|
http://www. | .com/ |
https://www. | .org/ |
http:// | .edu/ |
https:// | .net/ |
urn:uuid: | .info/ |
.biz/ | |
.gov/ | |
.com | |
.org | |
.edu | |
.net | |
.info | |
.biz | |
.gov |
So for example the address "http://www.google.com" would take up 8 bytes. Both "http://www." and ".com" are supported abbreviations so each will be shortened to 1 byte.
Number of Bytes | Data |
---|---|
1 | "http://www." abbreviated |
6 | "google", 1 byte for each letter |
1 | ".com/" abbreviated |
An address like "http://www.bit.ly/xyz" would take up 11 bytes. Notice that ".ly" is not a supported suffix, so each letter takes up 1 byte.
Number of Bytes | Data |
---|---|
1 | "http://www." abbreviated |
10 | "bit.ly/xyz" |
Even addresses with the suffix in the middle are abbreviated, ie "http://www.youtube.com/XYZ". Notice here that the '.com' is in the middle of an address, it is still shortened to just 1 byte.
Number of Bytes | Data |
---|---|
1 | "http://www." abbreviated |
7 | "youtube" |
1 | ".com/" abbreviated |
4 | "XYZ" |
Using UriBeacons with mbed BLE API
Using the UriBeacon with the mbed API is rather simple. Just like any other BLE project you must first initialize the BLE baselayer by creating a ble object.
Initialize_bl_object
BLEDevice ble;
Then you pass the BLE object to a UriBeacon config service.
Configure_UriBeacon_Service
URIBeaconConfigService *uriBeaconConfig; uriBeaconConfig = new URIBeaconConfigService(ble, "http://www.mbed.org");
Optionally you can then adjust settings of the UriBeacon such as transmission power levels, grabbing verbose debug information and other handy dandy services.
Optional_UriBeacon_configuration
/* Adjust the TX Power Level */ const int8_t powerLevels[] = {-20, -4, 0, 10}; uriBeaconConfig->setTxPowerLevels(powerLevels); // Set TX power levels, Lowest(-20), Low(-4), Medium(0), High(10) uriBeaconConfig->setTxPowerMode(URIBeaconConfigService::TX_POWER_MODE_LOW); // Set transmission in Low power mode /* Adjust Beacon Period*/ setBeaconPeriod(1000); // Set beacon to advertise every 1000ms
Other UriBeacon Services
This is just the bare basics of how URI beacons work. There is also a configuration service that allows URI beacons to be updated, locked, and provides other management feature. That is beyond the scope of this example but details can be found in the Technical Details section below.
Technical Details
For more details on how URI beacons work please see these websites:
UriBeacon Github Project : the github home for all things UriBeacon (maintained by google)
UriBeacon Specification : lots of good technical details
UriBeacon configuration service - This is a service that pairs with the UriBeacon that allows changing the URI's, locking them, and some other cool features. This service is not detailed in this example application.
Android App : smartphone application to view nearby UriBeacons.
iOS App : Sample code for using UriBeacons with iOS.
The PhysicalWeb Project : a project that the UriBeacon is central to.
In case you're really interested here is a diagram that nicely sums up how the 27bytes of advertising data payload are used.
main.cpp@13:b82d8db73633, 2015-02-13 (annotated)
- 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?
User | Revision | Line number | New 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 | } |