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

Files at this revision

API Documentation at this revision

Comitter:
rgrover1
Date:
Fri Mar 13 10:13:18 2015 +0000
Parent:
16:1daa78939a3b
Child:
18:569257e0af4e
Commit message:
updated to the latest from BLE_API.; Moving persistence checking into the Nordic specific flash adaptor code.

Changed in this revision

BLE_API.lib Show annotated file Show diff for this revision Revisions of this file
ConfigParamsPersistence.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
nrfConfigParamsPersistence.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/BLE_API.lib	Mon Mar 09 16:35:03 2015 +0000
+++ b/BLE_API.lib	Fri Mar 13 10:13:18 2015 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#193908f2b13b
+http://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#d87182a62c1b
--- a/ConfigParamsPersistence.h	Mon Mar 09 16:35:03 2015 +0000
+++ b/ConfigParamsPersistence.h	Fri Mar 13 10:13:18 2015 +0000
@@ -19,7 +19,6 @@
 
 #include "URIBeaconConfigService.h"
 
-
 /**
  * Generic API to load the URIBeacon configuration parameters from persistent
  * storage. If persistent storage isn't available, the persistenceSignature
@@ -27,19 +26,28 @@
  * a reset to default values.
  *
  * @param[out] paramsP
- *                 The parameters to be filled in from persistence storage.
+ *                 The parameters to be filled in from persistence storage. This
+                   argument can be NULL if the caller is only interested in
+                   discovering the persistence status of params.
+
+ * @return true if params were loaded from persistent storage and have usefully
+ *         initialized fields.
  */
-void loadURIBeaconConfigParams(URIBeaconConfigService::Params_t *paramsP);
+bool loadURIBeaconConfigParams(URIBeaconConfigService::Params_t *paramsP);
 
 /**
  * Generic API to store the URIBeacon configuration parameters to persistent
  * storage. It typically initializes the persistenceSignature member of the
  * params to the MAGIC value to indicate persistence.
  *
+ * @note: the save operation may be asynchronous. It may be a short while before
+ * the request takes affect. Reading back saved configParams may not yield
+ * correct behaviour if attempted soon after a store.
+ *
  * @param[in/out] paramsP
  *                    The params to be saved; persistenceSignature member gets
  *                    updated if persistence is successful.
  */
-void saveURIBeaconConfigParams(URIBeaconConfigService::Params_t *paramsP);
+void saveURIBeaconConfigParams(const URIBeaconConfigService::Params_t *paramsP);
 
 #endif /* #ifndef __BLE_CONFIG_PARAMS_PERSISTENCE_H__*/
--- a/main.cpp	Mon Mar 09 16:35:03 2015 +0000
+++ b/main.cpp	Fri Mar 13 10:13:18 2015 +0000
@@ -24,6 +24,36 @@
 BLEDevice ble;
 URIBeaconConfigService *uriBeaconConfig;
 
+/**
+ * URIBeaconConfig service can operate in two modes: a configuration mode which
+ * allows a user to update settings over a connection; and normal URIBeacon mode
+ * which involves advertising a URI. Constructing an object from URIBeaconConfig
+ * service sets up advertisements for the configuration mode. It is then up to
+ * the application to switch to URIBeacon mode based on some timeout.
+ *
+ * The following help with this switch.
+ */
+static const int CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS = 60;  // Duration after power-on that config service is available.
+Ticker configAdvertisementTimeoutTicker;
+
+/**
+ * Stop advertising the UriBeaconConfig Service after a delay; and switch to normal URIBeacon.
+ */
+void timeout(void)
+{
+    Gap::GapState_t state;
+    state = ble.getGapState();
+    if (!state.connected) { /* don't switch if we're in a connected state. */
+        uriBeaconConfig->setupURIBeaconAdvertisements();
+        ble.startAdvertising();
+
+        configAdvertisementTimeoutTicker.detach(); /* disable the callback from the timeout Ticker. */
+    }
+}
+
+/**
+ * Callback triggered upon a disconnection event. Needs to re-enable advertisements.
+ */
 void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
 {
     ble.startAdvertising();
@@ -42,14 +72,15 @@
      * operation.
      */
     URIBeaconConfigService::Params_t params;
-    loadURIBeaconConfigParams(&params);
+    bool fetchedFromPersistentStorage = loadURIBeaconConfigParams(&params);
 
     /* Initialize a URIBeaconConfig service providing config params, default URI, and power levels. */
     static URIBeaconConfigService::PowerLevels_t defaultAdvPowerLevels = {-20, -4, 0, 10}; // Values for ADV packets related to firmware levels
-    uriBeaconConfig = new URIBeaconConfigService(ble, params, "http://uribeacon.org", defaultAdvPowerLevels);
+    uriBeaconConfig = new URIBeaconConfigService(ble, params, !fetchedFromPersistentStorage, "http://uribeacon.org", defaultAdvPowerLevels);
     if (!uriBeaconConfig->configuredSuccessfully()) {
         error("failed to accommodate URI");
     }
+    configAdvertisementTimeoutTicker.attach(timeout, CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS);
 
     // Setup auxiliary services to allow over-the-air firmware updates, etc
     DFUService dfu(ble);
--- a/nrfConfigParamsPersistence.cpp	Mon Mar 09 16:35:03 2015 +0000
+++ b/nrfConfigParamsPersistence.cpp	Fri Mar 13 10:13:18 2015 +0000
@@ -18,50 +18,86 @@
 #include "nrf_error.h"
 #include "ConfigParamsPersistence.h"
 
+/**
+ * Nordic specific structure used to store params persistently.
+ * It extends URIBeaconConfigService::Params_t with a persistence signature.
+ */
+struct PersistentParams_t {
+    URIBeaconConfigService::Params_t params;
+    uint32_t                         persistenceSignature; /* This isn't really a parameter, but having the expected
+                                                            * magic value in this field indicates persistence. */
+
+    static const uint32_t MAGIC = 0x1BEAC000;              /* Magic that identifies persistence */
+};
+
+/**
+ * The following is a module-local variable to hold configuration parameters for
+ * short periods during flash access. This is necessary because the pstorage
+ * APIs don't copy in the memory provided as data source. The memory cannot be
+ * freed or reused by the application until this flash access is complete. The
+ * load and store operations in this module initialize persistentParams and then
+ * pass it on to the 'pstorage' APIs.
+ */
+static PersistentParams_t persistentParams;
+
 static pstorage_handle_t pstorageHandle;
 
-/* Dummy callback handler needed by Nordic's pstorage module. */
+/**
+ * Dummy callback handler needed by Nordic's pstorage module. This is called
+ * after every flash access.
+ */
 static void pstorageNotificationCallback(pstorage_handle_t *p_handle,
-                                  uint8_t            op_code,
-                                  uint32_t           result,
-                                  uint8_t *          p_data,
-                                  uint32_t           data_len)
+                                         uint8_t            op_code,
+                                         uint32_t           result,
+                                         uint8_t           *p_data,
+                                         uint32_t           data_len)
 {
     /* APP_ERROR_CHECK(result); */
 }
 
 /* Platform-specific implementation for persistence on the nRF5x. Based on the
  * pstorage module provided by the Nordic SDK. */
-void loadURIBeaconConfigParams(URIBeaconConfigService::Params_t *paramsP)
+bool loadURIBeaconConfigParams(URIBeaconConfigService::Params_t *paramsP)
 {
-    pstorage_init();
-    static pstorage_module_param_t pstorageParams = {
-        .cb          = pstorageNotificationCallback,
-        .block_size  = sizeof(URIBeaconConfigService::Params_t),
-        .block_count = 1
-    };
+    static bool pstorageInitied = false;
+    if (!pstorageInitied) {
+        pstorage_init();
 
-    pstorage_register(&pstorageParams, &pstorageHandle);
-    if (pstorage_load(reinterpret_cast<uint8_t *>(paramsP), &pstorageHandle, sizeof(URIBeaconConfigService::Params_t), 0) != NRF_SUCCESS) {
+        static pstorage_module_param_t pstorageParams = {
+            .cb          = pstorageNotificationCallback,
+            .block_size  = sizeof(PersistentParams_t),
+            .block_count = 1
+        };
+        pstorage_register(&pstorageParams, &pstorageHandle);
+        pstorageInitied = true;
+    }
+
+    if ((pstorage_load(reinterpret_cast<uint8_t *>(&persistentParams), &pstorageHandle, sizeof(PersistentParams_t), 0) != NRF_SUCCESS) ||
+        (persistentParams.persistenceSignature != PersistentParams_t::MAGIC)) {
         // On failure zero out and let the service reset to defaults
         memset(paramsP, 0, sizeof(URIBeaconConfigService::Params_t));
+        return false;
     }
+
+    memcpy(paramsP, &persistentParams.params, sizeof(URIBeaconConfigService::Params_t));
+    return true;
 }
 
 /* Platform-specific implementation for persistence on the nRF5x. Based on the
  * pstorage module provided by the Nordic SDK. */
-void saveURIBeaconConfigParams(URIBeaconConfigService::Params_t *paramsP)
+void saveURIBeaconConfigParams(const URIBeaconConfigService::Params_t *paramsP)
 {
-    if (paramsP->persistenceSignature != URIBeaconConfigService::Params_t::MAGIC) {
-        paramsP->persistenceSignature = URIBeaconConfigService::Params_t::MAGIC;
+    memcpy(&persistentParams.params, paramsP, sizeof(URIBeaconConfigService::Params_t));
+    if (persistentParams.persistenceSignature != PersistentParams_t::MAGIC) {
+        persistentParams.persistenceSignature = PersistentParams_t::MAGIC;
         pstorage_store(&pstorageHandle,
-                       reinterpret_cast<uint8_t *>(paramsP),
-                       sizeof(URIBeaconConfigService::Params_t),
+                       reinterpret_cast<uint8_t *>(&persistentParams),
+                       sizeof(PersistentParams_t),
                        0 /* offset */);
     } else {
         pstorage_update(&pstorageHandle,
-                        reinterpret_cast<uint8_t *>(paramsP),
-                        sizeof(URIBeaconConfigService::Params_t),
+                        reinterpret_cast<uint8_t *>(&persistentParams),
+                        sizeof(PersistentParams_t),
                         0 /* offset */);
     }
 }