Skip to main content

How To Use Bluetooth For BlinkUp

A Demo Device-activation App

The imp004m’s support for Bluetooth LE (impOS™ 38) will provide customers with an alternative means to provision production devices (ie. blessed devices) with an impCloud™ enrollment token, plan ID and end-user WiFi credentials, the process known as ‘device activation’. This guide shows how this can be achieved.

The Electric Imp BlinkUp™ SDK already incorporates the ability to retrieve an enrollment token and plan ID and make them available to the host mobile app rather than deliver them to the target imp-enabled device using optical BlinkUp. Accompanying this guide is a sample iOS app which demonstrates this approach and which works in conjunction with the Squirrel code referenced in this article.

Important Note Working with the BlinkUp SDK requires an API Key which is only made available to Electric Imp customers. If you are not an Electric Imp customer, the app code will not work for you.

The iOS App

Note The iOS code, written in Swift, can be run in Xcode’s Device Simulator, but this will not be able to access Bluetooth. To use Bluetooth, you must run the app on a connected iDevice.

The iOS app is used to scan for nearby Bluetooth-enabled imp-based devices, to select one of them, and then to choose a local wireless network, enter its password and transmit that information to the test device.

The app can perform a full device activation or simply update an already activated device’s WiFi details. Full device activation requires a BlinkUp API key. The app prompts you for a BlinkUp API key, or you can enter your key by tapping Actions in the navigation bar and then Enter your BlinkUp API key from the menu. Your BlinkUp API key will be stored in the iOS keychain. If you wish to clear a stored password, open the ‘Enter your BlinkUp API key’ panel, ensure no key is entered, and tap Submit.

To test full activation on a development device, the device must have first been added to your Electric Imp account (using the Electric Imp mobile app) and then added to any Development Device Group.

The app can also be used to clear a device’s WiFi settings.

Device Scanning

If you tap Actions > Start Scan or drag the screen downwards, the app will begin scanning for nearby Bluetooth LE devices with a pre-set GATT service UUID — the BlinkUp service defined in the Squirrel code (see below). If it detects such a device, the app connects to it and reads two of the standard Device Info service characteristics: for the model number and for the serial number. These values are used by the accompanying Squirrel code to provide, respectively, the host device’s imp type and device ID, both of which are displayed by the app upon receipt. The app uses the imp type to set the device’s icon.

The values are read sequentially, the receipt of the first triggering the request for the second. When the second is received, the app closes the connection. You should always close a Bluetooth LE connection as soon as you have read all the information you need, in order to minimize energy consumption.

Device Configuration

Tapping on a discovered device causes the app to connect to the device once more and retrieve a list of WiFi networks detected by the device. This list is used to populate a picker from which the user can select which network they would like the device to connect to. The list indicates whether any given network requires a password, and a field is provided to enter that password. Leave the password field blank when you connect to open networks.

Tapping Send BlinkUp causes the app to connect again to the device and this time send it the SSID of the selected network and the password from the entry field. If the user has entered a BlinkUp API key (see above), the app will also connect to the Electric Imp impCloud and retrieve a production plan ID and an enrollment token — these too will be sent to the device.

Finally, the app writes an arbitrary value to the device to trigger the application of the supplied data and to cause the device to reboot, reconnect and (if a plan ID and enrollment token have been provided) activate. If an activation takes place, the app polls the impCloud to discover when the device has been activated. When the device has been activated, this polling will receive its agent URL — the indication that activation has succeeded — which is then displayed by the app.

Setting only the device’s WiFi credentials does not make use of the BlinkUp SDK. Polling does not take place in this case: you will have to check the device directly to confirm that it has connected. Clearing the WiFi settings does not make use of the BlinkUp SDK.

The Squirrel Application Code

The device-side Squirrel code sets up the device’s Bluetooth LE sub-system, serves the BlinkUp and Device Info GATT services, and begins advertising the device to other Bluetooth LE-compatible units such as the iPhone on which the iOS is running.

Bluetooth Activation

The device code wraps its functionality into a simple structure that represents how a full customer application might work. When the code starts running, it checks the imp004m’s SPI flash for a signature indicating that the device has been activated. If the signature is present, the application code flow is executed (the dummy function start() is called) ; if not, a separate code flow is triggered in which the imp004m’s Bluetooth radio is enabled and configured (doBluetooth()):

// Start by checking the imp004m SPI flash for a signature
// If it is present (it is four bytes of 0xC3 each), the code
// jumps to the application flow; otherwise we run the activation
// flow, ie. set up and run Bluetooth LE
if ("spiflash" in hardware && imp.info().type == "imp004m") {
    hardware.spiflash.enable();
    local bytes = hardware.spiflash.read(0x0000, 4);
    local check = 0;
    foreach (byte in bytes) {
        if (byte == 0xC3) check++;
    }

    if (check >= 4) {
        // Device is activated so go to application code
        start();
    } else {
        // Device is not activated so bring up Bluetooth LE
        doBluetooth();
    }
} else {
    // Just start the app anyway and ignore Bluetooth
    start();
}

Upon a successful transfer of enrollment data from the mobile app, the Squirrel code configures the imp004m’s settings accordingly, writes the signature to the SPI flash and triggers a Squirrel restart. This ensures that when Squirrel is running again, it takes the application code path.

A real-world application would need to allow end-users to re-configure their devices, perhaps by pressing a reset button on the product which would trigger code that clears the signature from the SPI flash so that on restart, the unit once more enables Bluetooth and waits for configuration data. Alternatively, such an app might require the Bluetooth subsystem to be continuously active and not just for the initial end-user activation period.

Bluetooth Setup

The Squirrel code’s Bluetooth component is straightforward and included in the form of a class library, BTLEBlinkUp. Its constructor takes the imp GPIO pins and UART connected to the Bluetooth LE sub-system (see ‘Using Bluetooth LE with the imp004m’). If no arguments are provided, the instance defaults to the set-up for the Electric Imp imp004m Breakout Board.

// This function defines this app's activation flow: preparing the device
// for enrollment into the Electric Imp impCloud and applying the end-user's
// local WiFi network settings.
function doBluetooth() {
    // Instantiate the BTLEBlinkUp library
    bt = BTLEBlinkUp();

    // Don't use security
    bt.setSecurity(1);

    agent.on("set.agent.url", function(data) {
        bt.agentURL = data;

        // Set the device up to listen for BlinkUp data
        bt.listenForBlinkUp(null, function(data) {
            // This is the callback through which the BLE sub-system communicates
            // with the host app, eg. to inform it activation has taken place
            if ("address" in data) server.log("Device " + data.address + " has " + data.state);
            if ("security" in data) server.log("Connection security mode: " + data.security);
            if ("activated" in data && "spiflash" in hardware && imp.info().type == "imp004m") {
                // Write BlinkUp signature post-configuration
                hardware.spiflash.enable();
                local ok = hardware.spiflash.write(0x0000, "\xC3\xC3\xC3\xC3", SPIFLASH_PREVERIFY);
                if (ok != 0) server.error("SPIflash write failed");
            }
        });

        server.log("Bluetooth LE listening for BlinkUp...");
    }.bindenv(this));

    agent.send("get.agent.url", true);
}

Similarly, the constructor defaults to pre-defined GATT service and characteristic UUIDs, but you can pass in a table containing your own values if you prefer — and should do with your commercial code. It must contain the keys shown in the following example:

// Set up custom BlinkUp service UUIDs
local uuids = { "blinkup_service_uuid":    "FADA47BEC45548C9A5F2AF7CF368D719",
                "ssid_setter_uuid":        "5EBA195632D347C681A6A7E59F18DAC0",
                "password_setter_uuid":    "ED694AB947564528AA3A799A4FD11117",
                "planid_setter_uuid":      "A90AB0DC7B5C439A9AB52107E0BD816E",
                "token_setter_uuid":       "BD107D3E48784F6DAF3DDA3B234FF584",
                "blinkup_trigger_uuid":    "F299C3428A8A4544AC4208C841737B1B",
                "wifi_clear_trigger_uuid": "2BE5DDBA32864D09A652F24FAA514AF5",
                "wifi_getter_uuid":        "57A9ED95ADD54913849457759B79A46C" };

// Instantiate the BTLEBlinkUp library
bt <- BTLEBlinkUp(null, uuids);

The UUIDs are:

  • "blinkup_service_uuid" — the BlinkUp service
  • "ssid_setter_uuid" — the WiFi SSID writing service characteristic
  • "password_setter_uuid" — the WiFi password writing service characteristic
  • "planid_setter_uuid" — the activation plan ID writing service characteristic
  • "token_setter_uuid" — the activation token writing service characteristic
  • "blinkup_trigger_uuid" — the activation trigger writing service characteristic
  • "wifi_clear_trigger_uuid" — the clear WiFi settings trigger writing service characteristic
  • "wifi_getter_uuid" — the device WiFi scan reading service characteristic

The code now powers up the imp004m’s Bluetooth radio and configures it for use (the call to the bluetooth class’ open() method. The firmware for the Bluetooth radio, which is passed into open(), is stored within the Squirrel, but a real-world application might place this in the SPI flash within the factory and read it back at this point.

With Bluetooth active, the code establishes the BlinkUp service and seven characteristics using the default or supplied UUIDs (see above). Four of the characteristics receive (‘write’) respectively the WiFi SSID, the WiFi password, the enrollment token and the plan ID. The fifth characteristic works in the opposite direction: it provides information the mobile app can read back from the imp004m: a list of the WiFi networks the imp004m can see around it. The fifth is a dummy: its write operation does not store data but is used to trigger the writing of the received WiFi and enrollment data to the imp004m storage. Likewise, writing to the sixth characteristic triggers the clearance of the imp’s stored WiFi credentials.

The code also configures the Device Info service and then calls servegatt() to make these services available to any devices which subsequently connect to the imp004m. Establishing a handler to deal with such connections is performed by calling the onconnect() method, which takes the handler as its argument. BTLEBlinkUp makes use of this internally; it supports a separate callback to communicate back with the host Squirrel. This second callback is registered with either the onConnect() (note the case) function or listenForBlinkUp() and triggered by connection and disconnection events, and to inform the host that device activation has taken place.

// Device information service
service = { "uuid": 0x180A,
            "chars": [
              { "uuid": 0x2A29, "value": "Electric Imp" },           // manufacturer name
              { "uuid": 0x2A25, "value": hardware.getdeviceid() },   // serial number
              { "uuid": 0x2A24, "value": imp.info().type },          // model number
              { "uuid": 0x2A23, "value": (agentURL != null ? agentURL : "null") },   // system ID (agent ID)
              { "uuid": 0x2A26, "value": imp.getsoftwareversion() }] // firmware version
            };

// Add services to the list of GATT services offered
services.append(service);

// Begin serving
ble.servegatt(services);

Finally, we set the Bluetooth LE advertising mechanism to send out a simple advertisement — this is what the mobile app looks for when the user initiates a scan in the iOS app. This is done within the library code by calling startadvertise() on the bluetooth object and passing in the advertising data. This is a sequence of 8-bit values: the first byte is the length of the data that follows (17 bytes), followed by a single-byte data type value set by the Bluetooth LE standard: 0x07 which indicates that what follows is a list of all the 128-bit service UUIDs provided by the device. Here there is only one, as set in the service definition above. In the advertising data it is provided in big endian format.

Device Activation

The enrollment data received from the iOS app is collated by the imp in a table, blinkupData, which is initialized with keys for the data itself, a flag (updated) which is set when data is written so that Squirrel can be sure that there will be enrollment data to use, and a function, update(), that is called by the dummy characteristic described above. This function calls two key imp API methods — imp.setwificonfiguration() and imp.setenroltokens() — to apply the new WiFi credentials and enrollment data. These will be applied when the imp next connects, so the code reboots the imp, forcing the new credentials to be used and connection progress to be shown on the imp004m’s BlinkUp LED.

// Define the Enrollment Token setter characteristic
chrx = {};
chrx.uuid <- _uuids.token_setter_uuid;
chrx.write <- function(conn, v) {
    _blinkup.token = v.tostring();
    _blinkup.updated = true;
    server.log("Enrolment Token set");
    return 0x0000;
}.bindenv(this);

service.chars.append(chrx);

Before the restart, the code needs to write the ‘I am configured’ signature to SPI flash so that on restart the device will run the application code path. This is triggered via the callback passed into the BTLEBlinkUp listenForBlinkUp() method.

function listenForBlinkUp(advert = null, callback = null) {
    // This is a convenience method for serving BlinkUp. It assumes that you have already specified
    // the required level of security, using setSecurity(). It uses default values for advertising
    // min. and max. interval values, and serves only the BlinkUp and Device Information services
    serve();
    onConnect(callback);
    advertise(advert);
}

Agent Code

The sample Squirrel code provides a web UI which is served by the agent and can be visited by entering the agent URL into a desktop browser, or by selecting the ‘Open Agent URL’ option presented by the app after the target device has activated.

In addition to displaying device information — in the real world, an application might present a device management UI, for example — the web interface allows you to clear the activation signature the code applies to the device’s SPI flash. This is for the purposes of testing. The signature is set so that, after a restart, the device runs is expected application flow rather than the pre-application BlinkUp flow. Clearing the signature, followed by a restart, puts the device back into BlinkUp mode.

Bluetooth Bonding

While impOS 38 supports the use of a six-digit PIN code to authorize a connection between the imp004m and a mobile device, it does not support bonding, the process by which the mobile device records that the Bluetooth peripheral may connect multiple times under the same credentials. If you wish to make use of encryption, for which using a PIN is required, every attempt the mobile device makes to the imp004m will trigger a request for the PIN.

iOS handles the pin request for you automatically, when your code attempts to read or write a characteristic’s value, but will not notify your app whether the user has entered a correct PIN, an in correct PIN, or hit the PIN request dialog’s Cancel button. The only indication provided is the (eventual) successful read of the value.

iOS Bluetooth Attribute Caching

By default, iOS caches the attribute information it discovers from devices, as does Android. This ensures that future scans need not use the radio, conserving power. However, it also means if you change your Squirrel app’s served attributes during development, they will not be immediately detected by the app.

The easiest approach to dealing with this is to disable then re-enable Bluetooth on your Apple device — try switching to Airplane mode and then back again. You may also need to power-cycle the device.