Example program for the Eddystone Beacon service.

Dependencies:   BLE_API mbed nRF51822 X_NUCLEO_IDB0XA1

Fork of BLE_EddystoneBeacon by URIBeacon

This example demonstrates how to set up and initialize a Eddystone Beacon. For more details on the Eddystone specification please see the Eddystone Github Page.

The Basics

An Eddystone Beacon is a Bluetooth Low Energy beacon, that means it does all of its data transfer in GAP advertising packets. Eddystone beacons, unlike other beacons, broadcast multiple types of data. Currently Eddystone beacons have 3 frame types (UID, URL, TLM) that will get swapped out periodically. This swapping of frame data allows Eddystone beacons to send many different types of data, thus increasing their usefulness over traditional beacons. Note that the UID frame type provides the same 16Bytes of UUID namespace that iBeacons do and the URL frame type provides the same functionality as a URIBeacon.

For more details see the Eddystone Specification.

Smartphone Apps

nRF Master Control Panel - this program recognizes Eddystone beacons and will display the data for all frame types.

iPhone Physical Web app

Android App

Walkthrough of Physical Web application

Technical Details

The Eddystone Specification looks like the following image. Please note that this may change over time and for up to date information the official spec should be referenced. /media/uploads/mbedAustin/scratch-1-.png

The Eddystone Frames get swapped in and out depending on what frames you have enabled. The only required frame type is the TLM frame, all others are optional and you can have any number enabled. To disable the UID or URL frames give their values a 'NULL' in the Eddystone constructor. The Eddystone spec recommends broadcasting 10 frames a second.

Note

  • The current Eddystone mbed example does not allow for combining the eddystone service with other services, this will be changes in a future update.
  • There is an Eddystone Config service that allows for updating beacons in the field. We are working on an example for this, so keep your eyes pealed for a future update.
Revision:
22:160766614338
Parent:
21:f4646308f363
diff -r f4646308f363 -r 160766614338 ZipBeaconConfigService.h
--- a/ZipBeaconConfigService.h	Thu Jul 23 06:48:50 2015 +0000
+++ b/ZipBeaconConfigService.h	Thu Jul 23 18:00:59 2015 +0000
@@ -39,7 +39,7 @@
 static const uint8_t BEACON_EDDYSTONE[] = {0xAA, 0xFE};
 
 //Debug is disabled by default
-#if 1
+#if 0
 #define DBG(x, ...)  printf("[EddyStone: DBG]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
 #define WARN(x, ...) printf("[EddyStone: WARN]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
 #define ERR(x, ...)  printf("[EddyStone: ERR]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
@@ -228,8 +228,12 @@
     *
     */
     void setUIDFrameData(int8_t power, UIDNamespaceID_t namespaceID, UIDInstanceID_t instanceID, uint16_t RFU = 0x0000) {
-        if(power > 20){power = 20;}
-        if(power < -100){power = -100;}
+        if(power > 20) {
+            power = 20;
+        }
+        if(power < -100) {
+            power = -100;
+        }
         defaultUidPower = power;
         memcpy(defaultUidNamespaceID, namespaceID, UID_NAMESPACEID_SIZE);
         memcpy(defaultUidInstanceID,  instanceID,  UID_INSTANCEID_SIZE);
@@ -247,8 +251,12 @@
 
         int index = 0;
         Data[index++] = FRAME_TYPE_UID;                     // 1B  Type
-        if(defaultUidPower > 20){defaultUidPower = 20;}     // enforce range of vaild values.
-        if(defaultUidPower < -100){defaultUidPower = -100;}
+        if(defaultUidPower > 20) {
+            defaultUidPower = 20;   // enforce range of vaild values.
+        }
+        if(defaultUidPower < -100) {
+            defaultUidPower = -100;
+        }
         Data[index++] = defaultUidPower;                    // 1B  Power @ 0meter
         for(int x = 0; x < UID_NAMESPACEID_SIZE; x++) {     // 10B Namespce ID
             Data[index++] = defaultUidNamespaceID[x];
@@ -393,11 +401,11 @@
     bool updateAdvPacket(uint8_t serviceData[], unsigned serviceDataLen) {
         // Fields from the Service
         DBG("Updating AdvFrame: %d", serviceDataLen);
-        printf("\r\n");
-        for(int x = 0; x<serviceDataLen; x++) {
-            printf("%2.2x:",serviceData[x]);
-        }
-        printf("\r\n");
+//        printf("\r\n");
+//        for(int x = 0; x<serviceDataLen; x++) {
+//            printf("%2.2x:",serviceData[x]);
+//        }
+//        printf("\r\n");
         ble.clearAdvertisingPayload();
         ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
         ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_EDDYSTONE, sizeof(BEACON_EDDYSTONE));
@@ -407,59 +415,87 @@
     }
 
     /*
-    *  Callback from onRadioNotification(), used to update the PDUCounter and maybe other stuff eventually?
-    *
-    *
+    *   State machine for switching out frames.
+    *   This function is called by the radioNotificationCallback when a frame needs to get swapped out.
+    *   This function exists because of time constraints in the radioNotificationCallback, so it is effectively
+    *   broken up into two functions.
     */
-    void radioNotificationCallback(bool radioActive) {
-        DBG("RadioNotificationCallback : %d, %d, %d, %d",radioActive,frameIndex,TlmPduCount,TlmTimeSinceBoot);
-        // Update Time and PDUCount
-        // True just before an frame is sent, fale just after a frame is sent
-
-        TlmPduCount++;
-        frameIndex = frameIndex % EDDYSTONE_MAX_FRAMETYPE;
+    void swapOutFrames(void) {
         uint8_t serviceData[SERVICE_DATA_MAX];
         unsigned serviceDataLen = 0;
         //hard code in the eddystone UUID
         serviceData[serviceDataLen++] = BEACON_EDDYSTONE[0];
         serviceData[serviceDataLen++] = BEACON_EDDYSTONE[1];
+
+        switch(frameIndex) {
+            case 0:
+            // TLM frame
+                serviceDataLen += constructTLMFrame(serviceData+serviceDataLen,20);
+                DBG("\t Swapping in TLM Frame: len=%d",serviceDataLen);
+                updateAdvPacket(serviceData,serviceDataLen);
+                frameIndex++;
+                break;
+            case 1:
+                // URL Frame
+                serviceDataLen += constructURLFrame(serviceData+serviceDataLen,20);
+                DBG("\t Swapping in URL Frame: len=%d ",serviceDataLen);
+                updateAdvPacket(serviceData,serviceDataLen);
+                switchFlag = false;
+                frameIndex++;
+                break;
+            case 2:
+                // UID Frame
+                serviceDataLen += constructUIDFrame(serviceData+serviceDataLen,20);
+                DBG("\t Swapping in UID Frame: len=%d",serviceDataLen);
+                updateAdvPacket(serviceData,serviceDataLen);
+                switchFlag = false;
+                frameIndex++;
+                break;
+        }
+    }
+
+    /*
+    *  Callback from onRadioNotification(), used to update the PDUCounter and maybe other stuff eventually?
+    *
+    *
+    */
+    #define EDDYSTONE_SWAPFRAME_DELAYMS 1
+    void radioNotificationCallback(bool radioActive) {
+        //DBG("RadioNotificationCallback : %d, %d, %d, %d",radioActive,frameIndex,TlmPduCount,TlmTimeSinceBoot);
+        // Update PDUCount
+        TlmPduCount++;
+        frameIndex = frameIndex % EDDYSTONE_MAX_FRAMETYPE;
+
+
+        // True just before an frame is sent, fale just after a frame is sent
         if(radioActive) {
             // Do Nothing
         } else {
             // state machine to control which packet is being sent
             switch(frameIndex) {
                 case 0:
-                    serviceDataLen += constructTLMFrame(serviceData+serviceDataLen,20);
-                    DBG("\tconstructing TLM Frame: len= %d",serviceDataLen);
-                    updateAdvPacket(serviceData,serviceDataLen);
-                    frameIndex++;
+                    switchFrame.attach_us(this, &ZipBeaconConfigService::swapOutFrames, EDDYSTONE_SWAPFRAME_DELAYMS);
                     switchFlag = true;
                     break;
                 case 1:
                     // switch out packets
                     if(switchFlag) {
-                        serviceDataLen += constructURLFrame(serviceData+serviceDataLen,20);
-                        DBG("\tconstructing URL Frame: len = %d ",serviceDataLen);
-                        updateAdvPacket(serviceData,serviceDataLen);
+                        switchFrame.attach_us(this, &ZipBeaconConfigService::swapOutFrames, EDDYSTONE_SWAPFRAME_DELAYMS);
                         switchFlag = false;
                     } else {
                         if((TlmPduCount % 10) == 0) { // every 10 adv packets switch the frame
                             switchFlag = true;
-                            frameIndex++;
                         }
                     }
                     break;
                 case 2:
                     // switch out packets
                     if(switchFlag) {
-                        serviceDataLen += constructUIDFrame(serviceData+serviceDataLen,20);
-                        DBG("\tconstructing UID Frame: len=%d",serviceDataLen);
-                        updateAdvPacket(serviceData,serviceDataLen);
+                        switchFrame.attach_us(this, &ZipBeaconConfigService::swapOutFrames, EDDYSTONE_SWAPFRAME_DELAYMS);
                         switchFlag = false;
                     } else {
                         if((TlmPduCount % 10) == 0) { // every 10 adv packets switch the frame
                             switchFlag = true;
-                            frameIndex++;
                         }
                     }
                     break;
@@ -507,7 +543,7 @@
 
         updateAdvPacket(serviceData, serviceDataLen);
         ble.gap().startAdvertising();
-        //ble.gap().onRadioNotification(this,&ZipBeaconConfigService::radioNotificationCallback);
+        ble.gap().onRadioNotification(this,&ZipBeaconConfigService::radioNotificationCallback);
         timeSinceBootTick.attach(this,&ZipBeaconConfigService::tsbCallback,0.1); // incriment the TimeSinceBoot ticker every 0.1s
     }
 
@@ -667,8 +703,9 @@
 
     BLEDevice           &ble;
     Params_t            &params;
-    Ticker callbackTick;
-    Ticker timeSinceBootTick;
+    Ticker              callbackTick;
+    Ticker              timeSinceBootTick;
+    Timeout             switchFrame;
 // Default value that is restored on reset
     size_t              defaultUriDataLength;
     UriData_t           defaultUriData;