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 Bluetooth Low Energy

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

iPhone Physical Web app

Android App

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

PreFixSuffix
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 BytesData
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 BytesData
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 BytesData
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. https://github.com/google/uribeacon/raw/master/specification/uribeacon-figure.png

Committer:
rgrover1
Date:
Fri Nov 06 13:25:07 2015 +0000
Revision:
28:061605602ef1
Parent:
26:a2396234b4a8
Child:
29:5a1112254406
updating to the latest of the underlying libraries.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
rgrover1 14:868a1207022d 1 /* mbed Microcontroller Library
rgrover1 14:868a1207022d 2 * Copyright (c) 2006-2013 ARM Limited
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 0:790a27ffc99b 17 #include "mbed.h"
rgrover1 26:a2396234b4a8 18 #include "ble/BLE.h"
rgrover1 26:a2396234b4a8 19 #include "ble/services/URIBeaconConfigService.h"
rgrover1 26:a2396234b4a8 20 #include "ble/services/DFUService.h"
rgrover1 26:a2396234b4a8 21 #include "ble/services/DeviceInformationService.h"
rgrover1 16:1daa78939a3b 22 #include "ConfigParamsPersistence.h"
rgrover1 13:b82d8db73633 23
rgrover1 13:b82d8db73633 24 URIBeaconConfigService *uriBeaconConfig;
rgrover1 13:b82d8db73633 25
rgrover1 17:e2c0a1696e39 26 /**
rgrover1 17:e2c0a1696e39 27 * URIBeaconConfig service can operate in two modes: a configuration mode which
rgrover1 17:e2c0a1696e39 28 * allows a user to update settings over a connection; and normal URIBeacon mode
rgrover1 17:e2c0a1696e39 29 * which involves advertising a URI. Constructing an object from URIBeaconConfig
rgrover1 17:e2c0a1696e39 30 * service sets up advertisements for the configuration mode. It is then up to
rgrover1 17:e2c0a1696e39 31 * the application to switch to URIBeacon mode based on some timeout.
rgrover1 17:e2c0a1696e39 32 *
rgrover1 17:e2c0a1696e39 33 * The following help with this switch.
rgrover1 17:e2c0a1696e39 34 */
rgrover1 17:e2c0a1696e39 35 static const int CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS = 60; // Duration after power-on that config service is available.
rgrover1 17:e2c0a1696e39 36 Ticker configAdvertisementTimeoutTicker;
rgrover1 17:e2c0a1696e39 37
rgrover1 17:e2c0a1696e39 38 /**
rgrover1 17:e2c0a1696e39 39 * Stop advertising the UriBeaconConfig Service after a delay; and switch to normal URIBeacon.
rgrover1 17:e2c0a1696e39 40 */
rgrover1 17:e2c0a1696e39 41 void timeout(void)
rgrover1 17:e2c0a1696e39 42 {
rgrover1 17:e2c0a1696e39 43 Gap::GapState_t state;
rgrover1 28:061605602ef1 44 BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
rgrover1 28:061605602ef1 45
rgrover1 17:e2c0a1696e39 46 state = ble.getGapState();
rgrover1 17:e2c0a1696e39 47 if (!state.connected) { /* don't switch if we're in a connected state. */
rgrover1 17:e2c0a1696e39 48 uriBeaconConfig->setupURIBeaconAdvertisements();
rgrover1 26:a2396234b4a8 49 ble.startAdvertising();
rgrover1 17:e2c0a1696e39 50
rgrover1 17:e2c0a1696e39 51 configAdvertisementTimeoutTicker.detach(); /* disable the callback from the timeout Ticker. */
rgrover1 17:e2c0a1696e39 52 }
rgrover1 17:e2c0a1696e39 53 }
rgrover1 17:e2c0a1696e39 54
rgrover1 17:e2c0a1696e39 55 /**
rgrover1 17:e2c0a1696e39 56 * Callback triggered upon a disconnection event. Needs to re-enable advertisements.
rgrover1 17:e2c0a1696e39 57 */
rgrover1 28:061605602ef1 58 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *)
rgrover1 16:1daa78939a3b 59 {
rgrover1 28:061605602ef1 60 BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
rgrover1 26:a2396234b4a8 61 ble.startAdvertising();
rgrover1 6:31b65d4ea67d 62 }
rgrover1 0:790a27ffc99b 63
rgrover1 28:061605602ef1 64 void bleInitComplete(BLE::InitializationCompleteCallbackContext *context)
rgrover1 0:790a27ffc99b 65 {
rgrover1 28:061605602ef1 66 BLE &ble = context->ble;
rgrover1 28:061605602ef1 67
rgrover1 28:061605602ef1 68 if (context->error != BLE_ERROR_NONE) {
rgrover1 28:061605602ef1 69 return;
rgrover1 28:061605602ef1 70 }
rgrover1 28:061605602ef1 71
rgrover1 26:a2396234b4a8 72 ble.onDisconnection(disconnectionCallback);
rgrover1 13:b82d8db73633 73
rgrover1 16:1daa78939a3b 74 /*
rgrover1 16:1daa78939a3b 75 * Load parameters from (platform specific) persistent storage. Parameters
rgrover1 16:1daa78939a3b 76 * can be set to non-default values while the URIBeacon is in configuration
rgrover1 16:1daa78939a3b 77 * mode (within the first 60 seconds of power-up). Thereafter, parameters
rgrover1 16:1daa78939a3b 78 * get copied out to persistent storage before switching to normal URIBeacon
rgrover1 16:1daa78939a3b 79 * operation.
rgrover1 16:1daa78939a3b 80 */
rgrover1 16:1daa78939a3b 81 URIBeaconConfigService::Params_t params;
rgrover1 17:e2c0a1696e39 82 bool fetchedFromPersistentStorage = loadURIBeaconConfigParams(&params);
rgrover1 13:b82d8db73633 83
rgrover1 16:1daa78939a3b 84 /* Initialize a URIBeaconConfig service providing config params, default URI, and power levels. */
rgrover1 16:1daa78939a3b 85 static URIBeaconConfigService::PowerLevels_t defaultAdvPowerLevels = {-20, -4, 0, 10}; // Values for ADV packets related to firmware levels
rgrover1 17:e2c0a1696e39 86 uriBeaconConfig = new URIBeaconConfigService(ble, params, !fetchedFromPersistentStorage, "http://uribeacon.org", defaultAdvPowerLevels);
rgrover1 13:b82d8db73633 87 if (!uriBeaconConfig->configuredSuccessfully()) {
rgrover1 6:31b65d4ea67d 88 error("failed to accommodate URI");
rgrover1 6:31b65d4ea67d 89 }
rgrover1 17:e2c0a1696e39 90 configAdvertisementTimeoutTicker.attach(timeout, CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS);
rgrover1 0:790a27ffc99b 91
rgrover1 13:b82d8db73633 92 // Setup auxiliary services to allow over-the-air firmware updates, etc
rgrover1 13:b82d8db73633 93 DFUService dfu(ble);
rgrover1 16:1daa78939a3b 94 DeviceInformationService deviceInfo(ble, "ARM", "UriBeacon", "SN1", "hw-rev1", "fw-rev1", "soft-rev1");
rgrover1 11:c77cc2b74101 95
rgrover1 26:a2396234b4a8 96 ble.startAdvertising(); /* Set the whole thing in motion. After this call a GAP central can scan the URIBeaconConfig
rgrover1 26:a2396234b4a8 97 * service. This can then be switched to the normal URIBeacon functionality after a timeout. */
rgrover1 28:061605602ef1 98 }
rgrover1 28:061605602ef1 99
rgrover1 28:061605602ef1 100 int main(void)
rgrover1 28:061605602ef1 101 {
rgrover1 28:061605602ef1 102 BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
rgrover1 28:061605602ef1 103 ble.init(bleInitComplete);
rgrover1 28:061605602ef1 104
rgrover1 28:061605602ef1 105 /* SpinWait for initialization to complete. This is necessary because the
rgrover1 28:061605602ef1 106 * BLE object is used in the main loop below. */
rgrover1 28:061605602ef1 107 while (ble.hasInitialized()) { /* spin loop */ }
rgrover1 0:790a27ffc99b 108
rgrover1 0:790a27ffc99b 109 while (true) {
rgrover1 0:790a27ffc99b 110 ble.waitForEvent();
rgrover1 0:790a27ffc99b 111 }
rgrover1 0:790a27ffc99b 112 }