The Eddystone Observer scans for Eddystone beacons that are running the Eddystone Service example (see there for general information about Eddystone beacons). It reads the advertising packets broadcast by these beacons, and prints a human-readable version of the advertised URLs to the serial console. he canonical source for this example lives at https://github.com/ARMmbed/mbed-os-example-ble/tree/master/BLE_EddystoneObserver

The Eddystone Observer scans for Eddystone beacons that are running the Eddystone Service example (see there for general information about Eddystone beacons). It reads the advertising packets broadcast by these beacons, and prints a human-readable version of the advertised URLs to the serial console.

Running the application

Requirements

General hardware information is in the main readme.

This sample requires two devices - one to broadcast the beacon and one to scan for the broadcast. If you have more devices, you can use them as extra beacons.

You need a terminal program to listen to the observer's output through a serial port. You can download one, for example:

  • Tera Term for Windows.
  • CoolTerm for Mac OS X.
  • GNU Screen for Linux.

Building instructions

Building with mbed CLI

If you'd like to use mbed CLI to build this, then you should refer to the main readme. The instructions here relate to using the developer.mbed.org Online Compiler

In order to build this example in the mbed Online Compiler, first import the example using the ‘Import’ button on the right hand side.

Next, select a platform to build for. This must either be a platform that supports BLE, for example the NRF51-DK, or one of the following:

List of platforms supporting Bluetooth Low Energy

Or you must also add a piece of hardware and the supporting library that includes a Bluetooth Low Energy driver for that hardware, for example the K64F or NUCLEO_F401RE with the X-NUCLEO-IDB04A1

Once you have selected your platform, compile the example and drag and drop the resulting binary onto your board.

For general instructions on using the mbed Online Compiler, please see the mbed Handbook

  • Build the Eddystone Observer application and install it on your board as explained in the building instructions. Leave the board connected to your computer.

Checking console output

To see the application's output:

  • Check which serial port your Eddystone Observer is connected to.
  • Run a terminal program with the correct serial port and the baud rate set to 9600. For example, to use GNU Screen, run: ``screen /dev/tty.usbmodem1412 9600``.
  • The Eddystone Observer should start printing URLs of nearby Eddystone beacons to the terminal.

source/main.cpp

Committer:
mbed_official
Date:
2017-09-08
Revision:
42:092c08942a29
Parent:
29:083b9a2cb114

File content as of revision 42:092c08942a29:

/* mbed Microcontroller Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <events/mbed_events.h>
#include "mbed.h"
#include "ble/BLE.h"

static const int URI_MAX_LENGTH = 18;             // Maximum size of service data in ADV packets

static EventQueue eventQueue(/* event count */ 16 * EVENTS_EVENT_SIZE);

DigitalOut led1(LED1, 1);

void periodicCallback(void)
{
    led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */
}

void decodeURI(const uint8_t* uriData, const size_t uriLen)
{
    const char *prefixes[] = {
        "http://www.",
        "https://www.",
        "http://",
        "https://",
        "urn:uuid:"
    };
    const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
    const char *suffixes[] = {
        ".com/",
        ".org/",
        ".edu/",
        ".net/",
        ".info/",
        ".biz/",
        ".gov/",
        ".com",
        ".org",
        ".edu",
        ".net",
        ".info",
        ".biz",
        ".gov"
    };
    const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);

    size_t index = 0;

    /* First byte is the URL Scheme. */
    if (uriData[index] < NUM_PREFIXES) {
        printf("%s", prefixes[uriData[index]]);
        index++;
    } else {
        printf("URL Scheme was not encoded!");
        return;
    }

    /* From second byte onwards we can have a character or a suffix */
    while(index < uriLen) {
        if (uriData[index] < NUM_SUFFIXES) {
            printf("%s", suffixes[uriData[index]]);
        } else {
            printf("%c", uriData[index]);
        }
        index++;
    }

    printf("\n\r");
}

/*
 * This function is called every time we scan an advertisement.
 */
void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params)
{
    struct AdvertisingData_t {
        uint8_t                        length; /* doesn't include itself */
        GapAdvertisingData::DataType_t dataType;
        uint8_t                        data[1];
    } AdvDataPacket;

    struct ApplicationData_t {
        uint8_t applicationSpecificId[2];
        uint8_t frameType;
        uint8_t advPowerLevels;
        uint8_t uriData[URI_MAX_LENGTH];
    } AppDataPacket;

    const uint8_t BEACON_UUID[sizeof(UUID::ShortUUIDBytes_t)] = {0xAA, 0xFE};
    const uint8_t FRAME_TYPE_URL                              = 0x10;
    const uint8_t APPLICATION_DATA_OFFSET                     = sizeof(ApplicationData_t) + sizeof(AdvDataPacket.dataType) - sizeof(AppDataPacket.uriData);

    AdvertisingData_t *pAdvData;
    size_t index = 0;
    while(index < params->advertisingDataLen) {
        pAdvData = (AdvertisingData_t *)&params->advertisingData[index];
        if (pAdvData->dataType == GapAdvertisingData::SERVICE_DATA) {
            ApplicationData_t *pAppData = (ApplicationData_t *) pAdvData->data;
            if (!memcmp(pAppData->applicationSpecificId, BEACON_UUID, sizeof(BEACON_UUID)) && (pAppData->frameType == FRAME_TYPE_URL)) {
                decodeURI(pAppData->uriData, pAdvData->length - APPLICATION_DATA_OFFSET);
                break;
            }
        }
        index += (pAdvData->length + 1);
    }
}

void onBleInitError(BLE &ble, ble_error_t error)
{
   /* Initialization error handling should go here */
}

void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
    BLE&        ble   = params->ble;
    ble_error_t error = params->error;

    if (error != BLE_ERROR_NONE) {
        onBleInitError(ble, error);
        return;
    }

    if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
        return;
    }

    ble.gap().setScanParams(1800 /* scan interval */, 1500 /* scan window */);
    ble.gap().startScan(advertisementCallback);
}

void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
    BLE &ble = BLE::Instance();
    eventQueue.call(Callback<void()>(&ble, &BLE::processEvents));
}

int main()
{
    eventQueue.call_every(500, periodicCallback);

    BLE &ble = BLE::Instance();
    ble.onEventsToProcess(scheduleBleEventsProcessing);
    ble.init(bleInitComplete);

    eventQueue.dispatch_forever();

    return 0;
}