Connecting Conexio Stratus to Golioth

banner

Preface

Millions of transactions with vending machines, gas pumps, kiosks, price checkers, and retail point-of-sales stations occur daily from internally run embedded systems. Embedded systems are used actively in the home, medical facilities, and the industrial sector. Embedded systems are also a part of machine-to-machine (M2M) interfaces like processing equipment, controllers, sensors, and robotics/automation equipment.

Uptime is important for embedded devices, but guaranteeing uptime is challenging. Having embedded devices that can work by themselves is a great time saver because you can leave them to the task that they were designed for. Therefore, remotely located, devices need to be monitored to gain visibility and the status report on the performance of these devices. Such remote management tools can alert administrators when errors occur or devices fail to work at all, so they can identify and resolve the errors quickly.

One such observability platform is Golioth. Golioth allows device makers to deliver on the promise of cloud-connected IoT, enabling hardware developers to build end-to-end IoT-connected products using their choice of hardware and connectivity, all without the need for a team of cloud engineers. From device messaging to security, firmware updates, analytics, and more, the platform provides device services that work with every developer’s hardware and firmware requirements. At the moment, Golioth supports embedded IoT devices running ZephyrRTOS. And the good news is that Golioth is now open to use for developers.

This tutorial…

Covers how to connect your Conexio Stratus device to the Golioth platform. Specifically, this post will demonstrate how to:

  • Setup the toolchains and the Golioth SDK.
  • Connect Stratus kit to the Golioth cloud, periodically send environmental data, and monitor the device.
  • Perform Stratus Device Firmware Upgrade (DFU) procedure over the cellular network.

We have a lot to cover, so let’s dive in.

Setting Up the Toolchains

Golioth SDK is built around ZephyrRTOS and comes as a separate module. Since the Stratus device requires nRF Connect SDK for cellular connectivity, we first need to initialize the nRF Connect SDK as per the Golioth docs. So choose the directory on your machine where you want to install the Golioth SDK with the following command. For this tutorial, we are naming our directory where the Golioth SDK will reside to be golioth-sdk.

west init -m https://github.com/nrfconnect/sdk-nrf --mr v1.7.1

Once the nRF SDK is fetched, navigate to the west.yml located in golioth-sdk/nrf and add the following entry in manifest/projects/subtree

# Golioth repository.
- name: golioth
  path: modules/lib/golioth
  revision: main
  url: https://github.com/golioth/zephyr-sdk.git
  import:
    name-allowlist:
      - qcbor

After updating the west.yml file, it should now look similar to this:

...
    - name: memfault-firmware-sdk
      path: modules/lib/memfault-firmware-sdk
      revision: 0.25.0
      remote: memfault
    # Golioth repository.
    - name: golioth
      path: modules/lib/golioth
      revision: main
      url: https://github.com/golioth/zephyr-sdk.git
      import:
        name-allowlist:
          - qcbor
  # West-related configuration for the nrf repository.
  self:
    # This repository should be cloned to ncs/nrf.
    path: nrf
    # This line configures west extensions which are currently only
    # for internal use by NCS maintainers.
    west-commands: scripts/west-commands.yml

Now to clone all the required repositories, by issuing the following command:

west update

Depending on your network speed, this will take several minutes so sit back tight until the process completes. Upon successful completion, you should see the golioth directory under

golioth-sdk/modules/lib/golioth/

Bravo. For now, our SDK is all set. Let’s head over to the Golioth cloud to create an account.

Registration and Device Setup on the Golioth Cloud

Join the Golioth and create a user account here. And yes, for now joining Golioth is Free.

fig 1

Once your account is created, head over to the “Projects” menu on the left-hand pane and Create a Project as shown:

fig 2

Give your device a name and click Save.

Note: One can also achieve the steps shown here using the Golioth command-line tools (CLI) as outlined in the documentation. For this tutorial, we will use the Golioth console for setting up our devices and for faster provisioning.

Next, we need to provision our devices and acquire the credentials to connect it to the Golioth platform. Expand the “Management” menu and click Devices. Then, on the right-hand top view, click Create and select Provision with Credentials.

fig 3

This will pop up a Device fast path Provision window as shown:

fig 4

Next, choose a name for your device, give your device a tag label (read more about Golioth tags here), choose an identity (ID), or keep the default one generated automatically. Finally, add your pre-shared key (PSK) as per your liking and click Save. Later, we will need this ID and the PSK to authenticate our Stratus device with the Golioth platform.

At this point, we have all the required details to connect and publish data from our Conexio Stratus device to the Golioth. Let’s head over to the device firmware side.

Stratus Sample Application

We have extended the Light DB stream sample application provided in the Golioth SDK to connect the Stratus kit to the Golioth and stream sensory data. The extended application periodically samples the environmental data from the onboard SHT4x sensor and publishes it to the /temp and /humidity Light DB stream path. For the first part of this post, the full application can be found in the conexio-stratus-firmware/samples/golioth/stratus_lightdb_stream repo on GitHub. Copy the project folder and place it in your Golioth SDK samples directory.

golioth-sdk/modules/lib/golioth/samples

Below is the snapshot of the code’s main loop:

void main(void)
{
	char str_temperature[32];
	char str_humidity[32];

	LOG_DBG("Start Stratus <> Golioth Light DB sensor stream sample");

	const struct device *sht = DEVICE_DT_GET_ANY(sensirion_sht4x);
	
	int err;

	if (!device_is_ready(sht)) {
		LOG_ERR("Device %s is not ready.\n", sht->name);
		return;
	}

	golioth_system_client_start();

	while (true) {
		
		/* Fetch environmental data from SHT4X sensor */
		fetch_sensor_data(sht);

		snprintk(str_temperature, sizeof(str_temperature), "%d.%06d", temp.val1, abs(temp.val2));
		str_temperature[sizeof(str_temperature) - 1] = '\0';

		LOG_DBG("Sending temperature data %s", log_strdup(str_temperature));

		err = golioth_lightdb_set(client,
					  GOLIOTH_LIGHTDB_STREAM_PATH("temp"),
					  COAP_CONTENT_FORMAT_TEXT_PLAIN,
					  str_temperature,
					  strlen(str_temperature));
		if (err) {
			LOG_WRN("Failed to send temperature data: %d", err);
		}

        /* Let previous data transaction to complete */
		k_sleep(K_SECONDS(1));

		snprintk(str_humidity, sizeof(str_humidity), "%d.%06d", hum.val1, abs(hum.val2));
		str_humidity[sizeof(str_humidity) - 1] = '\0';

		LOG_DBG("Sending humidity data %s", log_strdup(str_humidity));

		err = golioth_lightdb_set(client,
					  GOLIOTH_LIGHTDB_STREAM_PATH("humidity"),
					  COAP_CONTENT_FORMAT_TEXT_PLAIN,
					  str_humidity,
					  strlen(str_humidity));
		if (err) {
			LOG_WRN("Failed to send humidity data: %d", err);
		}
        /* Sleep for X seconds before sampling again */
		k_sleep(SLEEP_TIME_SEC);
	}
}

Add Golioth credentials to the Application Code

First, we will have to add the Golioth credentials (ID and PSK) that we configured above using the Golioth console into the application code. You will need to edit samples/stratus_lightdb_stream/prj.conf with your credentials and update the following parameters.

# Golioth credentials
CONFIG_GOLIOTH_SYSTEM_CLIENT_PSK_ID="my-psk-id"
CONFIG_GOLIOTH_SYSTEM_CLIENT_PSK="my-psk"

Compiling the Sample Application using nRF Connect for VS Code

Next, open the sample application in your favorite code editor such as Visual Studio Code, or simply use the nRF Connect extension for VS Code for compiling your application. If using the nRF Connect extension, select and open the stratus_lightdb_stream project and Add the Build Configuration as shown, and click Build Configuration. Now, you should see the build process kicking in the background and generating the required files and binaries for the sample application.

fig 5

Once the project is compiled successfully, in the project navigation panel, you will see all the generated files.

fig 6

Well done! You have successfully managed to compile the sample application using the nRF Connect extension.

Compiling the Sample Application using West

To compile the application using west, open a terminal window in the application directory (stratus_lightdb_stream) and issue the following command:

west build -b conexio_stratus_ns

In case you do not want to recall the west commands simply command the following in the terminal, and the included python script will take care of the rest.

python3 ./generate_firmware.py

Flashing the Sample Application

Once the application is compiled successfully using either of the tools above, connect the Stratus device to the USB port and put it into the DFU mode.

Flash the compiled firmware using newtmgr:

newtmgr -c serial image upload build/zephyr/app_update.bin

Open up a serial console with a baud rate of 115200 and reset the Stratus device. The following serial UART output will be displayed in the terminal.

uart:~$ *** Booting Zephyr OS build v2.6.99-ncs1-rc2  ***
<inf> golioth_system: Initializing
<dbg> golioth_lightdb_stream.main: Start Stratus <> Golioth Light DB sensor stream sample
<inf> golioth_system: Starting connect
<dbg> golioth_lightdb_stream.main: Sending temperature data 25.961318
<inf> golioth_system: Client connected!
<dbg> golioth_lightdb_stream.main: Sending humidity data 17.426406
<dbg> golioth_lightdb_stream.main: Sending temperature data 25.971999
<dbg> golioth_lightdb_stream.main: Sending humidity data 17.373001

Once the device is connected to the Golioth cloud, it will start sending the environmental data. To confirm that the Stratus device is actually connected and communicating to the Golioth backend, head over to the Golioth console, and your device Status should now indicate as “online”.

fig 7

To observe the incoming data from the Stratus device, on the left-hand under the Monitor menu, click Light DB Stream and the Query Response window will appear. One should now see the incoming data from the device as shown below.

If you do “Houston, we have lift-off”… 🚀. Your device is now connected and communicating with the Golioth cloud.

fig 8

Explore around the console to see other features currently offered by the Golioth platform. One such feature that is of utmost importance for IoT devices is the ability to update firmware remotely. We will next look into this feature with a sample application.

Device Firmware Update (DFU) using Golioth

Over-the-air (OTA) firmware updates are a vital component of any IoT system. Over-the-air firmware updates refer to the practice of remotely updating the code on an embedded device.

The value of incorporating OTA update capabilities into a connected product include:

  • The ability to add new software features to a product after a device has been deployed in the field to improve functionality over time.
  • The opportunity to rapidly respond to bugs and security vulnerabilities without the need for physical recalls of devices or truck rolls; an expensive task for the business.
  • Ensuring embedded developers can quickly prototype and seamlessly roll out new versions of the device firmware, speeding up innovation cycles.

Recently, Golioth has released a firmware update feature in their latest SDK. Using this, we will demonstrate how to:

  • Generate a new firmware that is compatible and can be updated remotely to the Stratus device.
  • Perform the DFU procedure using the Golioth console by creating Artifacts and Releasing the firmware to the fleet of devices.

Overview of the DFU Sample Application

The DFU sample application extends the Light DB stream example that we have explored above for connecting the Stratus to the Golioth platform with an added ability to periodically check for any firmware updates. This mimics the realistic IoT application where a remote device might be performing some tasks (here, frequently sampling and sending the environmental data) while also being able to be updated remotely with new features or bug fixes.

The sample application for Stratus DFU resides in the conexio-stratus-firmware/samples/golioth/stratus_dfu repo on GitHub. Go ahead and copy the project folder and place it in your Golioth SDK samples directory.

golioth-sdk/modules/lib/golioth/samples

Add Golioth credentials to the Application Code

Add your Golioth credentials by editing the samples/stratus_dfu/prj.conf and update the following parameters.

# Golioth credentials
CONFIG_GOLIOTH_SYSTEM_CLIENT_PSK_ID="my-psk-id"
CONFIG_GOLIOTH_SYSTEM_CLIENT_PSK="my-psk"

Building and Flashing the Sample Application

Open a terminal window in the application directory (stratus_dfu) and compile the firmware for the Stratus device using:

west build -b conexio_stratus_ns

Once the application is compiled successfully, connect the Stratus device to the USB port and put it into the DFU mode.

Next, flash the compiled firmware using newtmgr:

newtmgr -c serial image upload build/zephyr/app_update.bin

Open up a serial console and reset the Stratus device. The following sample serial UART output will be displayed:

uart:~$ *** Booting Zephyr OS build v2.6.99-ncs1-1  ***
<inf> golioth_system: Initializing
<dbg> golioth_dfu.main: Start DFU sample
<inf> golioth_system: Starting connect
<inf> golioth_system: Client connected!
<dbg> golioth_dfu.main: Sending temperature data 26.185626
<dbg> golioth_dfu.main: Sending humidity data 16.270538

So far so good. This is similar to our previous example. Next, we will prepare the new firmware version that we would like to update the Stratus device with remotely.

Generating New OTA Firmware

For testing the DFU mechanism, we need to first assign a new firmware version during the image build process to distinguish between the current running firmware (golden image) and the new incoming one.

To generate the new application firmware, execute the following command:

west build -b conexio_stratus_ns -- -DCONFIG_MCUBOOT_IMAGE_VERSION=\"2.0.0\"

Here, we have named our new image to be version 2.0.0. You can assign a version number to anything you like which makes identifying the firmware release easier.

Creating the OTA Firmware Release

In order to create a new firmware release, two steps are required. First, in the Golioth console, we need to Create an Artifact. An Artifact is simply the actual firmware binary that we have generated above which is compatible with the MCUboot mechanism. MCUboot is a library that can be integrated into a bootloader to securely perform firmware updates.

Under Firmware Updates, click Artifacts, and Creating an Artifact window will open. Assign a name to the Package, followed by the Version number. Here, we have assigned a name as “main” with firmware version “2.0.0” (same as the version number we used when generating the DFU firmware above).

Under Artifacts, navigate to the build folder of your project and upload the following binary: app_update.bin.

stratus_dfu/build/zephyr/app_update.bin

Next, hit Save.

fig 9

After saving the Artifact, you should now see the Size of the binary that will be transmitted to the device to be updated with Type as mcuboot.

fig 10

The next step is to create the Release. Creating a Release allows the rolling out of the latest firmware to the targeted fleet of devices. To do so, click Release on the left-hand menu, and set the Release Flag as your choice. Here we have chosen V2.0.0 to indicate that we are releasing version 2.0.0 of the firmware.

Next, assign the Device Tags. Device Tags allows releasing the firmware to the subset of your fleet. In our case, we will only release the latest firmware to those devices that are tagged with “stratus”. You will notice that for this demonstration, our “Affected devices count” is 1, indicating that only one device that has been tagged with “stratus” will be updated out of the entire fleet of devices. Other devices will remain untouched. A cool feature, I think!

Finally, select your Artifacts, which will be the artifact that we created above, and click Save.

fig 11

Upon saving the release, you should now see all the designated parameters.

fig 12

At this point, if you are happy with the firmware release, go ahead and enable Rollout by toggling the Rollout button. Enabling rollout allows the Golioth cloud to send down the firmware to the remote device/s, the next time when the devices connect to the Golioth cloud.

As soon as you hit rollout, you will now notice the following activity on the device side.

[00:00:04.971,679] <inf> golioth_system: Client connected!
[00:00:05.489,624] <dbg> golioth_dfu: Desired
                                       a3 01 1a 61 ac 13 f0 02  78 40 33 61 64 34 32 37 |...a.... x@3ad427
                                       32 35 31 38 31 66 38 37  31 36 39 32 62 65 35 66 |25181f87 1692be5f
                                       62 66 65 32 32 32 62 32  30 37 61 61 33 64 34 35 |bfe222b2 07aa3d45
                                       37 35 62 39 34 30 30 36  36 30 64 36 63 30 30 38 |75b94006 60d6c008
                                       66 37 66 34 30 36 33 32  33 33 03 81 a6 01 64 6d |f7f40632 33....dm
                                       61 69 6e 02 65 32 2e 30  2e 30 03 78 40 63 39 35 |ain.e2.0 .0.x@c95
                                       39 64 30 62 30 31 62 33  62 65 62 65 38 64 32 33 |9d0b01b3 bebe8d23
                                       64 63 34 61 36 63 30 33  34 36 31 36 33 64 37 61 |dc4a6c03 46163d7a
                                       37 65 36 66 63 33 63 39  31 64 66 31 31 30 38 63 |7e6fc3c9 1df1108c
                                       37 37 39 32 38 31 35 65  30 61 32 35 36 04 1a 00 |7792815e 0a256...
                                       04 88 a4 05 70 2f 2e 75  2f 63 2f 6d 61 69 6e 40 |....p/.u /c/main@
                                       32 2e 30 2e 30 06 67 6d  63 75 62 6f 6f 74       |2.0.0.gm cuboot
[00:00:05.489,929] <inf> golioth: Manifest sequence-number: 1638667248
[00:00:06.066,284] <dbg> golioth_dfu.data_received: Received 1024 bytes at offset 0
[00:00:06.066,436] <inf> mcuboot_util: Swap type: none
[00:00:06.066,436] <inf> golioth_dfu: swap type: none
[00:00:06.670,684] <dbg> golioth_dfu.data_received: Received 1024 bytes at offset 1024
[00:00:07.221,252] <dbg> golioth_dfu.data_received: Received 1024 bytes at offset 2048
[00:00:07.735,870] <dbg> golioth_dfu.data_received: Received 1024 bytes at offset 3072
[00:00:08.340,301] <dbg> golioth_dfu.data_received: Received 1024 bytes at offset 4096
[00:00:09.010,253] <dbg> golioth_dfu.data_received: Received 1024 bytes at offset 5120
...

The Stratus device detects the desired incoming image and starts receiving the new firmware in chunks of 1kb over the cellular network. Once the full image has been received (which might take a minute or two depending on the size) and saved onto the internal flash of the nRF9160 SiP, the device will start the firmware upgrade procedure by commanding a reboot.

...
[00:02:41.984,100] <dbg> golioth_dfu.data_received: Received 1024 bytes at offset 294912
[00:02:42.606,506] <dbg> golioth_dfu.data_received: Received 1024 bytes at offset 295936
[00:02:43.055,969] <dbg> golioth_dfu.data_received: Received 827 bytes at offset 296960 (last)
[00:02:43.155,822] <inf> golioth_dfu: Requesting upgrade
[00:02:43.156,188] <inf> golioth_dfu: Rebooting in 1 second(s)

At this point, the MCUboot will start fetching the information of the image from the image start address, verify, and mark the images ready to be installed after which it will load the newly downloaded image.

Once the boot process has been successfully completed you will now see the latest firmware running on your Stratus device:

uart:~$ *** Booting Zephyr OS build v2.6.99-ncs1-1  ***
[00:00:00.216,461] <inf> golioth_system: Initializing
[00:00:02.709,014] <dbg> golioth_dfu.main: Starting Stratus DFU sample
[00:00:02.709,167] <inf> golioth_system: Starting connect
[00:00:02.718,475] <dbg> golioth_dfu.main: Sending temperature data 26.383230
[00:00:03.719,146] <dbg> golioth_dfu.main: Sending humidity data 19.673294
[00:00:04.032,409] <inf> golioth_system: Client connected!
[00:00:04.481,170] <dbg> golioth_dfu: Desired
                                       a3 01 1a 61 ac 13 f0 02  78 40 33 61 64 34 32 37 |...a.... x@3ad427
                                       32 35 31 38 31 66 38 37  31 36 39 32 62 65 35 66 |25181f87 1692be5f
                                       62 66 65 32 32 32 62 32  30 37 61 61 33 64 34 35 |bfe222b2 07aa3d45
                                       37 35 62 39 34 30 30 36  36 30 64 36 63 30 30 38 |75b94006 60d6c008
                                       66 37 66 34 30 36 33 32  33 33 03 81 a6 01 64 6d |f7f40632 33....dm
                                       61 69 6e 02 65 32 2e 30  2e 30 03 78 40 63 39 35 |ain.e2.0 .0.x@c95
                                       39 64 30 62 30 31 62 33  62 65 62 65 38 64 32 33 |9d0b01b3 bebe8d23
                                       64 63 34 61 36 63 30 33  34 36 31 36 33 64 37 61 |dc4a6c03 46163d7a
                                       37 65 36 66 63 33 63 39  31 64 66 31 31 30 38 63 |7e6fc3c9 1df1108c
                                       37 37 39 32 38 31 35 65  30 61 32 35 36 04 1a 00 |7792815e 0a256...
                                       04 88 a4 05 70 2f 2e 75  2f 63 2f 6d 61 69 6e 40 |....p/.u /c/main@
                                       32 2e 30 2e 30 06 67 6d  63 75 62 6f 6f 74       |2.0.0.gm cuboot
[00:00:04.481,475] <inf> golioth: Manifest sequence-number: 1638667248
[00:00:04.482,543] <inf> golioth_dfu: Desired version (2.0.0) matches current firmware version!

Note that the application running on your device now detects that the current running image version matches with the one on the Golioth cloud.

You have now successfully upgraded your Stratus device firmware using the remote firmware update capability of the Golioth platform. Mission Accomplished!

Conclusion

In this post, we have merely touched on all the features that the Golioth platform offers. However, this tutorial sets a solid foundation for connecting, monitoring, and updating your Stratus devices via the Golioth. As the Golioth team is constantly updating the platform, expect more feature rollouts soon. The full application for this tutorial can be found in the conexio-stratus-firmware repo on GitHub.

If you want to create and connect your own IoT applications without having to worry about cellular data or contracts, grab your own Conexio Stratus kit today.

Thank you and happy coding. 🙏