Visualizing Bluetooth OTA Firmware Updates

One of the best things about Golioth Connectivity is it makes Bluetooth devices act like all the other devices on the Golioth cloud. And one of my favorite things about Golioth is the ability to do Over-The-Air (OTA) firmware updates without any fuss. Today we’re not only going to show how the sample in our Pouch repository implements OTA, but we’ll visualize it on a development board that has a lot of LEDs and activate the firmware update using the accelerometer. Let’s dig in.

How OTA works with Pouch

Pouch is the Golioth innovation that enables Bluetooth devices to talk through standard gateways up to the Cloud and send/receive data like any other device on the cloud. It is end-to-end encrypted and can access nearly all of the same services as cellular, Wi-Fi, or Ethernet devices running the Golioth Firmware SDK. Here’s the order of events, as it works in our reference implementation:

  1. Bluetooth device advertises that there is data to send
  2. Gateway initiates connection with Bluetooth device
  3. Bluetooth device sends data up to the cloud (uplink)
  4. After uplink, available data (downlink) is sent down as a response, including services like OTA or Golioth settings
  5. Bluetooth device disconnects and the entire cycle can start anew, depending on the device

Note that it doesn’t need to work in that order, since many of the services in Pouch are independent of one another. Furthermore, it’s possible for you to create a completely bespoke version of the Gateway that handles Pouch traffic. But currently, our demos follow the order of events listed above.

In my setup, I am using the pre-compiled binary that is built for the NXP FRDM-RW612. Once I program that in and add Golioth credentials to the Gateway, I basically don’t need to think about it again. The Gateway logs the connections that are happening, but cannot see any of the traffic transiting through, since only the Cloud has keys paired with the Bluetooth device.

Example log output from the FRDM-RW612 acting as a gateway

Looking at the BLE GATT sample on the Pouch repository, we can see that OTA updates are already implemented using MCUboot. The device reports its firmware version using the VERSION file in the repository:

This is used to report MCUboot firmware version at startup and to check for new available versions on the cloud. When a new version of firmware is deployed to a Cohort and the Bluetooth device checks in with the Cloud, that device will see the updated version of firmware is available as part of the downlink data. It sounds complex, but works really smoothly…let’s look at how.

Visualizing OTA updates

We have been working on a demo using the Tikk board as an asset tracker, albeit a very LED-flashy one. Part of this work was developing a wake-on-movement behavior using the accelerometer (more about that in future posts). We pulled that work and the ability to control the LED matrix on the front of the open source Tikk board. So we put that all together and what do we get? A Bluetooth device that prompts you to shake it to get an update to the OTA firmware!

Modifications

This took the standard files you see in BLE GATT demo and added some new functionality. First in the ota_manifest_receive function, we add in some LED messaging to report the received firmware version and also clear out any previous indication of status, in case an OTA restarts.

static void ota_manifest_receive(const struct golioth_ota_manifest_component *components,
                                 size_t num_components)
{

    char buf[32];
    for (int i = 0; i < num_components; i++)
    {
        fw_size = components[i].size;
        LOG_DBG("Target: %s@%s, %d bytes",
                components[i].name,
                components[i].target,
               fw_size);
        LOG_HEXDUMP_DBG(components[i].target_hash,
                        GOLIOTH_OTA_COMPONENT_HASH_BIN_LEN,
                        "Target hash:");
        if (0 != strcmp(components[i].current, components[i].target))
        {
            golioth_ota_mark_for_download(components[i].name);
        }
        snprintk(buf,sizeof(buf),"FW ver %s ready", components[i].target);
    }
    led_message(buf);
    led_message("Shake to start");

    clear_led_progress_bar();
}

In ota_main_receive we see how the blocks of data are received from the cloud 1K at a time. Much like the blockwise_upload that sends data in the other direction, the total image size is split up into chunks and then tracked block by block. Since we know the current block and we know the total size, we can do some math on the progress and apply that to the 105 LEDs on the front of the Tikk board.

static void ota_main_receive(const void *data, size_t offset, size_t len, bool is_last)
{
    LOG_DBG("Received %d bytes at offset %d", len, offset);

    int err = 0;
    if (count==0)
    {
        led_message("Starting FW update");
        count++;
    }
    if (0 == offset)
    {
        err = flash_img_init(&flash_context);
        if (err)
        {
            LOG_ERR("Failed to init flash write");
            return;
        }
        clear_led_progress_bar();
    }

    check_progress((uint32_t)offset);

    err = flash_img_buffered_write(&flash_context, data, len, is_last);
    if (err)
    {
        LOG_ERR("Failed to write to flash: %d", err);
        return;
    }

    if (is_last)
    {
        err = boot_request_upgrade(BOOT_UPGRADE_PERMANENT);
        if (err)
        {
            LOG_ERR("Failed to request upgrade");
            return;
        }

        LOG_INF("Rebooting to apply upgrade");

#if IS_ENABLED(CONFIG_LOG)
        while (log_process())
        {
        }
#endif
        led_message("Rebooting");
        k_sleep(K_SECONDS(3));

        sys_reboot(SYS_REBOOT_WARM);
    }
}

At the end of the OTA process, we accept the last block and then kick off a worker to reboot the processor. This also gives us time to send one last message to the user with the LED matrix.

Try it out yourself

While the Tikk board itself is not available, you can try out OTA on your own Bluetooth devices right now. Grab one of the supported Gateway dev boards, upload the binary, program one of the supported Bluetooth nodes with a sample, and kick off an OTA within your cohort. You’ll see the device pulling down blocks and rebooting, ready to face the world with a shiny new version of firmware.

Chris Gammell
Chris Gammell
Chris is the Head of Developer Relations and Hardware at Golioth. Focusing on hardware and developer relations at that software company means that he is trying to be in the shoes of a hardware or firmware developer using Golioth every day. He does that by building hardware and reference designs that Golioth customers can use to bootstrap their own designs.

Post Comments

No comments yet! Start the discussion at forum.golioth.io

More from this author

Related posts

spot_img

Latest posts

How to Use Golioth Blockwise Stream to Upload Large IoT Data Payloads

Golioth allows large data uploads using the Firmware SDK and via blockwise stream calls. This post shows how to modify the Stream sample to try uploading large amounts of arbitrary data and route that data through Golioth Pipelines.

Golioth Joins STMicroelectronics Partner Program

Golioth and STMicroelectronics are officially partners. Golioth will enhance ST offerings and make devices more secure, more capable, and easier to manage from afar.

Bluetooth Gateways in the Field: The Globalscale GTI-RW612

Golioth Connectivity enables developers to connect Bluetooth devices to the cloud using Gateways, including microcontroller based gateways. Today we're discussing the Globalscale GTI-RW612 gateway....

Want to stay up to date with the latest news?

Subscribe to our newsletter and get updates every 2 weeks. Follow the latest blogs and industry trends.