A Deep Dive Into Eddystone Beacons (nRF52 + Zephyr use case)

As a continuation of our series on Bluetooth beacons (part 1 here, part 2 here, and part 3 here), today we’ll be covering Google’s Eddystone standard in more detail along with implementation.

The Eddystone standard was released as an alternative and competitor to Apple’s iBeacon standard. It provides similar functionality to iBeacon, but also goes a step beyond and adds some unique features.

With Eddystone, you can still build similar functionality to include two levels of hierarchy. This includes a 16-byte Beacon ID which is composed of a 10-byte namespace and a 6-byte instance.

Eddystone provides four frame types:

  • Eddystone-UID: this type is similar to iBeacon’s UUID + Major + Minor setup. It defines a 16-byte Beacon ID composed of a 10-byte namespace and a 6-byte instance. For example, the namespace could define a nationwide retailer’s ID for its stores and the instance would refer to each store uniquely.
  • Eddystone-URL: this type provides a way to broadcast a URL to be discovered by a BLE scanner device, which in turn could navigate to the URL and present the webpage to the user.
  • Eddystone-TLM: this type provides telemetry information about the device such as battery level, temperature and a running counter of advertised packets.
  • Eddystone-EID: this type provides a framework for attaining a certain level of security and privacy of a device. EID stands for Ephemeral ID, which is an identifier that changes periodically at a rate determined during the deployment phase by registering it with a trusted web service.

NOTE: Even though Google has pretty much abandoned the Eddystone standard on smartphones (specifically Android and Chrome), it can still serve as a good format for applications where you control both sides (broadcaster devices + scanner devices) and that do not depend on smartphone apps. It also serves as a great exercise for learning more about how Bluetooth advertisements work and how to utilize them for your own application.

In this tutorial, we’ll focus on Eddystone-UID and Eddystone-URL frame types.

Here’s what we’ll cover today:

Eddystone Format

In order to better understand how Eddystone broadcasting devices work, we need to look at the details of the advertising packets involved. Here’s what the format of an Eddystone advertisement packet looks like:

Eddystone Advertisement Packet Format

The part shown in the figure above is constant for all Eddystone advertisement packets, except for the Service Data portion.

Advertisement data follows the format Length-Type-Value (similar to the popular TLV format). The Length value indicates the length of the data that includes both the Type and the Payload.

The Eddystone standard utilizes the Service Data Advertisement data type to send the data to the BLE scanner devices. iBeacon, on the other hand, utilizes the Manufacturer Data Advertisement data type.

Now, let’s look at the Service Data portion which includes the information we’re interested in parsing at the receiver end.

Service Data

The Service Data portion format depends on the frame type used (Eddystone-UID, Eddystone-URL, Eddystone-TLM, or Eddystone-EID).

The first byte of the Service Data field always includes the frame type, which is defined as following:

Eddystone Frame Type Value (1 byte)
Source: Eddystone Specification

Eddystone-UID

As we mentioned earlier, this frame type is used in a similar way as Apple’s iBeacon. It includes the following:

Eddystone-UID Service Data Format
Source: Eddystone Specification

When using this frame type, we can utilize the 10-byte namespace for a unique ID of our choice (e.g. to indicate a specific store identifier), and then use the 6-byte as an identification of a subset of the namespace (e.g. to identify an aisle within the store).

Notes:

  • This frame type utilizes the maximum 31 bytes of advertisement data available in a BLE (primary) advertisement packet.
  • The Ranging Data field is the Tx power measured at 0 meters from the transmitter (-100 dBm to +20 dBm).
    Per the Eddystone specification, the best way to determine the value to include in this field is to measure the actual RSSI at 1 meter and then add 41 dBm to the value (based on the assumption that 41 dBm is the signal loss that occurs over 1 meter).
  • For more information and recommendations on the generation of the namespace ID and instance ID, refer to the Eddystone specification here.

Important Note: The Tx power value uses a two’s complement format to represent signed values. For more information on this calculation, refer to the section of the Wikipedia article on Two’s Complement: https://en.wikipedia.org/wiki/Two%27s_complement#Converting_to_two’s_complement_representation

Eddystone-URL

The Eddystone-URL frame type is unique and different from the use of the Eddystone-UID or Apple’s iBeacon format. It is used to advertise a URL for the scanner to discover and parse, then potentially point a web browser to.

This frame type is primarily used in “Physical Web” applications and implementations.

Since the maximum BLE advertisement data length is 31 bytes, this limits the URLs that can be broadcast using this frame type. However, the format provides some level of compressed encoding to make the most of the limited space available for the advertisement data.

Here’s the format used for this frame type:

Eddystone-URL Frame Type Format
Source: Eddystone Specification

The Tx power value is represented in the same way we described in the Eddystone-UID section above.

The URL scheme is defined as follows:

– Different prefixes for URLs are represented as follows:

URL Scheme Prefix
Source: Eddystone Specification

– The main portion of the URL is then included as a sequence of characters. For example, “google” would be represented as a sequence of ‘g’ ‘o’ ‘o’ ‘g’ ‘l’ ‘e’, which would be converted to their ASCII values.

– Finally, the URL expansion (e.g. .com/ or .com) is represented as follows:

HTTP URL Expansion values
Source: Eddystone Specification

For example, if we wanted to represent “https://www.google.com/” with a Tx power of 0 dBm, the Service Data array would look like this:

Frame Type (URL)Tx powerhttps://www.google
.com/
0x100x000x010x670x6f0x6f0x670x6c0x650x00
Example Eddystone-URL Service Data field value

Eddystone-TLM and Eddystone-EID

Eddystone-TLM frames are used for broadcasting telemetry data useful for monitoring the health of beacons in the field. This frame does not contain any identifier, so it is paired with either Eddystone-URL, Eddystone-UID, or Eddystone-EID frames.

When paired with EID frames, the Eddystone-TLM packet is encrypted. When paired with URL or UID frames, however, the TLM packets are unencrypted.

To learn more about these types, refer to the following links:

Implementing Eddystone on nRF52 using Zephyr RTOS

For the practical part of this tutorial, we’ll be using:

  • An nRF52840 development kit (you could use any Zephyr-supported board, but you’ll have to change the board type in the build command arguments).
  • Zephyr RTOS.
  • Smartphone with a BLE scanner app – we’ll be using nRF Connect for iOS.

Note: If you have not worked with Zephyr before, then check out my previous post on Getting Started with BLE development in Zephyr.

Zephyr already provides an example of setting up an Eddystone-URL advertisement, so we’ll be using this as our starting point.

The example is located at <Zephyr root folder>/zephyr/samples/bluetooth/beacon. To build the example, navigate to the <Zephyr root folder>/zephyr/ folder, then run the following:

$ west build -p auto -b nrf52840_pca10056 samples/bluetooth/beacon

Output:

Let’s take a look at the example’s source code first before we flash and run it on the development board.

main.c:

/* main.c - Application main entry point */
/*
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include 
#include 
#include 
#include 
#include 
#include 
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
/*
 * Set Advertisement data. Based on the Eddystone specification:
 * https://github.com/google/eddystone/blob/master/protocol-specification.md
 * https://github.com/google/eddystone/tree/master/eddystone-url
 */
static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
	BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xaa, 0xfe),
	BT_DATA_BYTES(BT_DATA_SVC_DATA16,
		      0xaa, 0xfe, /* Eddystone UUID */
		      0x10, /* Eddystone-URL frame type */
		      0x00, /* Calibrated Tx power at 0m */
		      0x00, /* URL Scheme Prefix http://www. */
		      'z', 'e', 'p', 'h', 'y', 'r',
		      'p', 'r', 'o', 'j', 'e', 'c', 't',
		      0x08) /* .org */
};
/* Set Scan Response data */
static const struct bt_data sd[] = {
	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};
static void bt_ready(int err)
{
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}
	printk("Bluetooth initialized\n");
	/* Start advertising */
	err = bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad),
			      sd, ARRAY_SIZE(sd));
	if (err) {
		printk("Advertising failed to start (err %d)\n", err);
		return;
	}
	printk("Beacon started\n");
}
void main(void)
{
	int err;
	printk("Starting Beacon Demo\n");
	/* Initialize the Bluetooth Subsystem */
	err = bt_enable(bt_ready);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
	}
}

As you can see, the code is very simple. Let’s change the URL to “https://www.google.com/” to match our example in the previous section.

/*
* Set Advertisement data. Based on the Eddystone specification:
* https://github.com/google/eddystone/blob/master/protocol-specification.md
* https://github.com/google/eddystone/tree/master/eddystone-url
*/
static const struct bt_data ad[] = {
  BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
  BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xaa, 0xfe),
  BT_DATA_BYTES(BT_DATA_SVC_DATA16,
      0xaa, 0xfe, /* Eddystone UUID */
      0x10, /* Eddystone-URL frame type */
      0x00, /* Calibrated Tx power at 0m */
      0x01, /* URL Scheme Prefix https://www. */
      'g', 'o', 'o', 'g', 'l', 'e',
      0x00) /* .com/ */
};

Now, that we have made some changes, we need to rebuild the application. Again, run:

$ west build -p auto -b nrf52840_pca10056 samples/bluetooth/beacon

We can now run nRF Connect (iOS, Android, or Desktop) to confirm the changes:

nRF Connect discovering an Eddystone-URL advertising

As you can see, the URL matches exactly what we updated it to be.

Also, the Type detected is “Physical Web” and the Frame Type is 0x10 (URL). The device name “Test beacon” is set in the prj.conf file:

CONFIG_BT=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_DEVICE_NAME="Test beacon"

Eddystone-UID Example

Let’s go ahead and update the example to transmit a UID frame type instead of URL:

/*
* Set Advertisement data. Based on the Eddystone specification:
* https://github.com/google/eddystone/blob/master/protocol-specification.md
* https://github.com/google/eddystone/tree/master/eddystone-url
*/
static const struct bt_data ad[] = {
  BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
  BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xaa, 0xfe),
  BT_DATA_BYTES(BT_DATA_SVC_DATA16,
      0xaa, 0xfe, /* Eddystone UUID */
      0x00, /* Eddystone-UID frame type */
      0x00, /* Calibrated Tx power at 0m */
      0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x080x09, /* 10-byte Namespace */
      0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f) /* 6-byte Instance */
};
nRF Connect discovering an Eddystone-UID advertising

As you can see, the Type detected is “Eddystone” and the Frame Type is 0x00 (UID).

Take your BLE knowledge to the next level

If you’re looking to learn more about beacon development including developing:

1. A BLE broadcaster device that acts as both an iBeacon and Eddystone device

2. A BLE Scanner device that discovers iBeacon Eddystone devices in the area and parses the advertised data down to the byte level. Here’s a screenshot of the data captured and parsed by the scanner:

BLE Scanner Example Serial Output

“Learn The Basics of Bluetooth Low Energy EVEN If You Have No Coding Or Wireless Experience!"

Don't miss out on the latest articles & tutorials. Sign-up for our newsletter today!

Take your BLE knowledge to the next level.

If you’re looking to get access to full video courses covering more topics, then check out the Bluetooth Developer Academy.

As part of all the courses within the Academy, you’ll also be able to download the full source code to use as a reference or use within your own application.

By joining the Bluetooth Developer Academy, you will get access to a growing library of video courses.

The Academy also features access to a private community of Bluetooth experts, developers, and innovators. You’ll get to connect and interact with me and other experts in the Bluetooth space, learn from others’ experiences and knowledge, and share yours as well.

So, what are you waiting for?? Join today!