How do you show a device connected to and controlled from the Internet? Golioth is literally doing this every day, so we built a piece of custom hardware that shows what’s going on when a device is passing sensor data up to the cloud and receiving commands in return. We call it Ostentus, and it’s a beautiful combination of ePaper, back-lit LEDs, and capacitive touch.

The origins of the word Ostentus come from the Latin: expose to view, exhibit, show. This is a perfect codename for the modular faceplate we built to show off Golioth’s reference designs. Let’s see it in action and dive into the details of how we built it.

Add to any project via I2C

Ostentus communicates with its host hardware over i2c, receiving a simple set of commands to update the display and LEDs.

Golioth is headed to the Embedded World conference next week and you’ll see these faceplates on a number of different demo units. They scroll through different sensor readings, including the option for a summary page. At boot, they display the logo that accompanies the demo. They can also be used to read out connection and other information from the device.

There’s a helper library that is added to an existing project as a submodule. For most of our demos, we just insert a couple dozen lines of code to pipe six(ish) different readings to the display.

Ostentus’ Hardware Origin Story

User interfaces grow in complexity very quickly. Our primary goal was to make it simple to add Ostentus to any IoT project, for any of the SDKs that we support (3 and growing!). That means simplicity from both the hardware and firmware side of things.

The latest Golioth reference designs use the MikroElectronika Click ecosystem for sensors, and include at least to Qwiic connectors. This means two things:

  • We don’t have a ton of extra pins to drive something like a display direct from our chosen microcontroller
  • I2C is already used in pretty much everything we demonstrate

We chose to utilize I2C and have the Ostentus board act as just another I2C peripheral.

Next, we’ve done a lot of work with ePaper through our trainings (and other shenanigans). It is kind of magical how easy it is to read, and how it retains an image even when powered off. We ordered an assortment of different displays in December to spec them out for our purposes.

The chip shortage drove our choice of microcontrollers. We landed on the RP2040 microcontroller because of its knack to remain in-stock. It also helps that it’s a powerhouse, with 133 MHz dual-core, “PIO” which are hardware-based state machines (more on those later), abundant pins, SRAM, external flash…all for a somewhat low price. We had originally planned to populate the RP2040 and its necessary circuitry directly on the PCB, but the ease, availability, and cost of the Pico boards led us to include them as modules.

Finally, we have a wealth of past experience producing circuit boards that themselves were meant to look amazing without being placed in a case. This informed the board design and passive component decisions for a fantastic aesthetic. The LEDs are on the underside of the board, shining up through the substrate to appear as illuminated icons. There are also three capacitive touch pads that allow for user feedback. All of this is wrapped in matte black solder mask, making it look more like a (Dark) Apple product and less a PCB. The result is a perfect replacement for the plain plastic covers of the project boxes we told you about last week.

Is it Firmware or Software?

PCB faceplate on a Golioth power monitor reference design

The heavy lifting for the display graphics is all handled by the RP2040 on the board. When we landed on this chip we looked around for other projects using it along with ePaper and found the Pimoroni Badger 2040. That dev board is based around MicroPython, which we hadn’t originally set out to use, but it makes a lot of sense. You can plug USB into it and upload new graphics without rebuilding/reflashing firmware. That has modularity written all over it! We did some early testing to see if the pimoroni-pico repository could be extended for Golioth’s needs and it looked promising.

Unfortunately, Ostentus didn’t have enough physical space for the same ePaper display. We spun up our own driver based on the Good Display sample code for the GDEH0154D67, a 1.54″ square display that packs in a remarkable 200×200 pixel resolution.

Communications proved a bit of a challenge. Ostentus is an I2C peripheral device and needs to listen on the bus while a lot of other stuff is going on. We were lucky to find Daniel Gorbea’s I2C multi-address library. It builds for the Pico-SDK, utilizing the PIO and interrupts for tight hardware timing. This receives incoming I2C messages over the Qwiic connector, immediately updating LED state for certain commands, and buffering the rest for MicroPython to access.

MicroPython makes images, fonts, and screen control a breeze. There was a learning curve in getting the C code to play nicely with the Python, but it’s not to hard to pick up. For this year’s conference we’ve set up the MicroPython layer to run a slideshow of sensor values. The controlling device registers each slide over I2C, sending a string as the label and assigning a unique key. That key is later used to send updated values as they become available.

What a Show!

It’s been a whirlwind adventure getting to this point. But let’s be honest, most of the Internet of Things industry happens in abstract and non-obvious ways. Golioth moves the ball on securely connecting and controlling microcontroller-level devices, and lots of them. Ostentus makes it much easier for us to show that story in real-time, at a booth, in the middle of tens of thousands of awesome embedded engineers. We can’t wait you see you next week. At the very least, you’ll want to stop by for a hands-on view of our sleek new Ostentus board!

In a best case scenario, once an IoT fleet is deployed, you never need to (physically) touch them again. Golioth helps give you tools to work with your devices remotely and make this a reality. Today, we’ll look at dynamically modifying the number of logs being sent back to the Cloud. This allows fleet managers to peek into individual devices without needing to waste data and battery power by always sending every log message back to Golioth.

Logging to the cloud is already built into Golioth, so it’s really just a matter of tuning how many logs are being sent by your devices. Golioth hooks into the Zephyr RTOS Logging service, which we’ll be showcasing here today.

Background on Remote Logging with Zephyr

The Golioth Zephyr SDK has remote logging built it, and in our sample applications (like the hello sample) it is enabled by default in the prj.conf files:

CONFIG_LOG_BACKEND_GOLIOTH=y
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048

At the top of each C file you need to register for logging. This is also a good place to set the default logging level, which I’ll refer to as the “compiled-in” logging level.

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(remote_logging_example, LOG_LEVEL_DBG);

This compiled-in level is an important decision because the preprocessor will not include logging calls if they have a higher value than this parameter. For instance, if you set the default to LOG_LEVEL_ERR, you cannot remotely turn on debugging messages because LOG_LEVEL_DBG is a higher logging level. Here is the hierarchy of logging levels in Zephyr:

#define LOG_LEVEL_NONE 0U
#define LOG_LEVEL_ERR  1U
#define LOG_LEVEL_WRN  2U
#define LOG_LEVEL_INF  3U
#define LOG_LEVEL_DBG  4U

In our case, the solution is to use LOG_LEVEL_DBG when compiling, and programmatically set the value to a lower level at run time. This will make your binary a bit larger, since the strings for every logging message will be included, but it delivers the option to turn on debugging messages after deployment which in most cases is worth the extra bytes of flash.

Write a Function to Set Log Levels at Run Time

Now we need a function that, when called, will automatically adjust the logging level for every logging source on the device.

#include <zephyr/logging/log_ctrl.h>

void change_logging_level(int log_level) {
    int source_id = 0;
    char *source_name;
    while(1) {
        source_name = (char *)log_source_name_get(0, source_id);
        if (source_name == NULL) {
            break;
        } else {
            LOG_WRN("Settings %s log level to: %d", source_name, log_level);
            log_filter_set(NULL, 0, source_id, log_level);
            ++source_id;
        }
    }
}

This function uses Zephyr’s logging control to query the name of each logging source that is available on the system. It then uses that source name to set the new logging level.

If you are putting cellular devices into the field, you probably don’t want to have logging turned on very high (or at all) by default since you’ll be paying for bandwidth. The first thing you can do at run time is call this function:

change_logging_level(LOG_LEVEL_ERR);

Now, only error messages will be logged to the Golioth cloud.

Setting Log Level Remotely

There are two obvious ways you can go about setting log levels remotely: the Golioth Remote Procedure Call (RPC) service, and the Golioth Settings service. Since the most likely use for this is turning on logs for a single device (and not fleetwide all at the same time), using a remote procedure call makes the most sense to me.

// Remote procedure call (RPC) callback for setting the logging levels
static enum golioth_rpc_status on_set_log_level(QCBORDecodeContext *request_params_array,
                       QCBOREncodeContext *response_detail_map,
                       void *callback_arg)
{
    double a;
    uint32_t log_level;
    QCBORError qerr;

    QCBORDecode_GetDouble(request_params_array, &a);
    qerr = QCBORDecode_GetError(request_params_array);
    if (qerr != QCBOR_SUCCESS) {
        LOG_ERR("Failed to decode array item: %d (%s)", qerr, qcbor_err_to_str(qerr));
        return GOLIOTH_RPC_INVALID_ARGUMENT;
    }

    log_level = (uint32_t)a;

    if ((log_level < 0) || (log_level > LOG_LEVEL_DBG)) {

        LOG_ERR("Requested log level is out of bounds: %d", log_level);
        return GOLIOTH_RPC_INVALID_ARGUMENT;
    }

    change_logging_level(log_level);
    return GOLIOTH_RPC_OK;
}

// Register RPC to listen for "set_log_levels"
// This should be called from your "on_connect()" callback
golioth_rpc_register(rpc_client, "set_log_level", on_set_log_level, NULL);

There are two parts to the function above. The first is a callback that will run when a remote procedure call (RPC) instruction is received from the Golioth servers. It will get incoming log level as a parameter, validate it, then run the function we discussed in the previous section to change the log levels.

The second part of the code is the act of registering the RPC. This tells the Golioth servers that this device wants to be notified whenever a callback with the name "set_log_level" is issued from the Golioth web console, or via the Golioth REST API.

Golioth RPC for setting logging levels

The Golioth web console can be used to send an RPC, or you may do so using the Golioth REST API

Here’s an example of using the web console interface to submit this RPC. I sent a request to change to log level 3 (LOG_LEVEL_INF), and upon success/failure we get a notification message back. This RPC was successful, and took 913 milliseconds for the device to receive the message, execute it, and report the results.

[00:00:37.736,663] <inf> app_work: Sending hello! 2
[00:01:07.738,800] <inf> app_work: Sending hello! 3
[00:01:36.947,418] <wrn> app_rpc: Settings golioth_dfu log level to: 3
--- 12 messages dropped ---
[00:01:36.947,448] <wrn> app_rpc: Settings golioth_rd_template log level to: 3
[00:01:36.947,479] <wrn> app_rpc: Settings golioth_samples log level to: 3
[00:01:36.947,479] <wrn> app_rpc: Settings golioth_system log level to: 3
[00:01:36.947,509] <wrn> app_rpc: Settings lightdb log level to: 3
[00:01:36.947,540] <wrn> app_rpc: Settings log log level to: 3
[00:01:36.947,570] <wrn> app_rpc: Settings lte_lc log level to: 3
[00:01:36.947,601] <wrn> app_rpc: Settings lte_lc_helpers log level to: 3
[00:01:36.947,631] <wrn> app_rpc: Settings mcuboot_util log level to: 3
[00:01:36.947,662] <wrn> app_rpc: Settings modem_antenna log level to: 3
[00:01:36.947,692] <wrn> app_rpc: Settings mpu log level to: 3
[00:01:36.947,723] <wrn> app_rpc: Settings net_buf log level to: 3
[00:01:36.947,753] <wrn> app_rpc: Settings net_coap log level to: 3
[00:01:36.947,753] <wrn> app_rpc: Settings net_core log level to: 3
[00:01:36.947,784] <wrn> app_rpc: Settings net_if log level to: 3
[00:01:36.947,814] <wrn> app_rpc: Settings net_shell log level to: 3
[00:01:36.947,845] <wrn> app_rpc: Settings net_sock log level to: 3
[00:01:36.947,875] <wrn> app_rpc: Settings net_sock_addr log level to: 3
[00:01:36.947,906] <wrn> app_rpc: Settings net_sock_tls log level to: 3
[00:01:36.947,937] <wrn> app_rpc: Settings net_sock_wrapper log level to: 3
[00:01:36.947,967] <wrn> app_rpc: Settings net_socket_offload log level to: 3
[00:01:36.947,998] <wrn> app_rpc: Settings net_utils log level to: 3
[00:01:36.948,028] <wrn> app_rpc: Settings nrf_modem log level to: 3
[00:01:36.948,059] <wrn> app_rpc: Settings os log level to: 3
[00:01:36.948,089] <wrn> app_rpc: Settings pm log level to: 3
[00:01:36.948,120] <wrn> app_rpc: Settings settings log level to: 3
[00:01:36.948,150] <wrn> app_rpc: Settings shell.shell_uart log level to: 3
[00:01:36.948,181] <wrn> app_rpc: Settings shell_uart log level to: 3
[00:01:36.948,211] <wrn> app_rpc: Settings soc log level to: 3
[00:01:36.948,242] <wrn> app_rpc: Settings stream log level to: 3
[00:01:36.948,272] <wrn> app_rpc: Settings uart_nrfx_uarte log level to: 3
[00:01:37.741,027] <inf> app_work: Sending hello! 4
[00:02:07.743,041] <inf> app_work: Sending hello! 5
uart:~$ 

On the device side, we can see the output of our RPC on a serial terminal (above). There are a lot of logging sources running on this device and they have all been individually set to level 3.

Remember, if the compiled-in level for any given source has been set lower (to only show errors, or to show no logging), setting a higher number at run time will not return additional messages because higher-level messages were not included in the build.

There and Back Again

What does it take to troubleshoot an IoT device in the field? If designed correctly, it will not take a physical visit to the device, but merely a few remote communications. Ideally, you will turn on debugging, analyze the issues you’re having, and then send a command to adjust accordingly.

But even if you haven’t planned very far ahead, with Golioth you can still enable these features. We recommend that every device you put in the field have Golioth Over-the-Air (OTA) firmware updates enabled. That way, you can send these remote-logging features to your devices, even if they are already in the field.

Do you have questions or suggestions on adjusting logging levels remotely? We’d love to hear from you on the Golioth Forum!

One of my first engineering jobs was in the Test and Measurement space. Tracing everything back to standards and calibration is a key part of the process. It takes a long time and is taken very seriously. I had many learning experiences that reinforced the importance of calibration (despite my tongue-in-cheek article title).

IoT devices often don’t have the luxury of being as accurate: the cost of sensors, the power usage of analog measurements, the “awake” windows for battery power devices… all of these things contribute to different priorities while measuring the physical world. However, if you have a precise (repeatable) sensor, you can utilize the trend of the data in a useful way.

If you ARE going to calibrate, it’s normally done in a sensor library. For certain sensors, Zephyr excels at this. There are built in sensor libraries that return standard values. You can even pull calibrated and normalized readings from the sensor in real time from Zephyr’s sensor shell. I consistently use sensors like the BME280 and the LIS2DH in my reference designs since it is really easy to add the readings to the pile of data I send back to Golioth.

What happens when a sensor driver is not in tree and I don’t want to go about writing my own sensor driver? I pull the raw readings to the cloud and work with them there!

Prototyping, not production

When you are trying to prove your IoT system can work or test out a business idea, you don’t always start by making production ready designs. But you can still make useful designs by pulling raw data readings.

I did this recently with an i2c sensor that was pulling in ADC readings. The “counts” that I gathered from the ADC are tied to physical phenomenon (in this case, soil moisture), but they are not absolute values. Instead I am able to gather the readings using direct ADC readings and then publish them to Golioth’s LightDB Stream service.

Below, I will discuss three ways you can interact with this data using other Golioth services and interactively produce even more useful data. These include tweaking threshold settings, calibrating via remote procedure call (RPC), and using OTA to upload new machine learning sets.

Set “thresholds” with Settings

One of my favorite things to use the Golioth Settings Service for is interacting with data and creating an extra layer of intelligence. For the IoT Trashcan Monitor reference design, I do this to set “threshold” on each device in the field. There is a time-of-flight sensor that gathers the distance from the top of the trashcan to the top of the garbage in that trashcan. What happens if you want to utilize the same sensor but on different sized trashcans? What if you want to set the “100%” level of the trashcan differently, say if one part of the national park needs to have a cleaner look than another?

You could keep this intelligence on the Cloud, but then you don’t get the benefit of the device reporting it’s various levels, so it’s harder to read from a terminal or in the logs. I push a level I created on the Settings Service down to each device that defines different thresholds:

I also utilize these levels coming back from each trashcan to trigger icons on our Grafana dashboards, which makes it even easier to tell which devices require intervention.

The above is just for the Trashcan example, there are loads of other examples where you might want to have field-settable values, from the installer or technician. In the case of the soil moisture sensor, I want to be able to calibrate “wet” and “dry” (per the simplistic calibration instructions) and then do some interpolation in between.

On the fly modifications with Remote Procedure Calls

Most of the time when using raw data from a sensor, it is done in a “device to cloud” context. You take the reading from the sensor and ship it off to be dealt with by larger computers (AKA “the cloud”). However, some applications will include the need for some kind of feedback from the cloud computing element. You could argue this is what we were doing above, since the Settings Service is pushing data down to the device.

Sometimes you want to be able to inject data into your data measurement and management process on the device, which is a perfect use case for Remote Procedure Calls (RPCs). One way to think of RPCs is if you were accessing a function between two different parts of your code. You put the function prototype in your code.h file… except now you can access that function from the cloud. So maybe I don’t want to do a full calibration on the device, but it is beneficial to set an offset for something like an i2c-based thermocouple measurement chip like the MCP96L01T. I could easily pipe raw data from the chip up to the cloud, but I might want to change a setting for the resolution of that data or the cold junction compensation temperature.

Registers on the MCP960X family of parts. You could write a function to change these values with raw i2c writes to the chip and have the function be accessible via a Golioth RPC.

I could have a function on the device that I use during start that sets these values like set_thermocouple_resolution() and set_cold_junction_temp_c(), which I use during startup. Under the covers they would be simple i2c writes to the device to set registers with bit-masked values. However, I could also expose these to the cloud using an RPC. When I call that RPC from the REST API or the Golioth Console, I also pass a value (in this case a new resolution setting or cold junction temp), it gets validated on the device as an acceptable value, and then a success message is sent back to the cloud once its executed. Add in logging messages into the device-side code, and you should be able to easily see that the device has successfully switched modes and the raw data being piped back is now different. (The change means a higher resolution or a different cold junction compensation.)

Machine Learning + data capture + OTA

The ultimate (and most trendy) way to deal with sensor data is to not care at all about the sensor output. Instead, capture and correlate data with desired behaviors; collect data with a “known good” and a “known bad” state of your machine, for instance, to allow the model to discern between the two.

Golioth has the tools to enhance this method of working with data, on a local machine or from afar. First, you can capture general data using LightDB stream, sending back your raw data readings from your sensor. If desired, you could also link a button, switch, or other input on the device to correlate when you are performing “Action A” that is different from “Action B”. Next, you capture and ingest that data into a machine learning algorithm like PyTorch or TinyML. Finally, when you create or revise your model and build it into your design, you can upload that new firmware using the Golioth Over-The-Air (OTA) service. Over time, you can continue to refine the model and even combine it with the other methods mentioned above.

Start prototyping today!

One thing I hope you get out of reading this article is the prototyping capabilities available to you when you use Golioth. While I benefit from the Zephyr driver ecosystem (as well as driver ecosystems from other SDKs that we support), I don’t want to feel limited when I want to try out a new chip that isn’t in-tree yet. I feel comfortable doing things like raw i2c and SPI read/write functions, and now can make that data available to the Cloud for even faster prototyping. If you need help prototyping your next IoT device, stop by our Forum to discuss or send an email to [email protected].

Cellular IoT products struggle with battery life. Getting and staying connected to a cell tower takes a good amount of energy. Though we’re past the days of GSM drawing amps of current (!), it is still costly to open sessions to the tower. Understanding how your device is communicating with the Cloud is crucial to building robust devices that will last years in the field.

This webinar will include Golioth team members, alongside Jared Wolff of CircuitDojo. Jared is an early adopter of Golioth and a Golioth Ambassador, as well as an advocate for Zephyr devices. Golioth utilizes Jared’s nRF9160 Feather board designs in all of our current cellular-based Reference Designs.

What you can expect from this Webinar

First and foremost, we hope to make this a more dynamic and interactive session than many technical webinars. (No one will be reading Powerpoint slides in a monotone voice here!) The session will cover:

  • Understanding your connection to cellular towers
  • Understanding your power draw when in a passive or sleep mode
  • Measurement challenges for embedded cellular projects
  • Methods for saving data (and power) when connecting to the cloud
  • Building robust device health metrics for your fleet
  • System architecture decisions for lower power circuit boards

Sign up now!

This webinar is at 1 pm EST / 10 am PST on January 18th, 2023. If you can’t make it the day of, you can still still sign up to access the on-demand content. Those who attend will have an opportunity to ask questions towards the end of the presentation.

Golioth is secure by default, offering a couple of different ways for your devices to establish a secure connection to the Golioth Servers. When trying out features in the lab, pre-shared keys (PSK) are fine. But when moving devices into production, there is no substitute for certificate-based authentication.

Today we are announcing the ability to use Certificates with the Golioth platform.

Certificates deliver numerous security benefits when compared to pre-shared keys. This is especially true when it comes to provisioning your IoT fleet. As devices roll off the assembly line, they can be granted individual device certificates signed using a trusted chain of root certificates and intermediate certificates. At that point, the devices are not yet registered on the Golioth server. When they first connect, the certificates are verified against the chain of trust and a record of the trust-verified device is created. This simplifies the registration of a large influx of new devices, as happens in a production environment.

Let’s walk through the process used to get to that point.

How to generate and use certificates with Golioth

Generating a self-signed root certificate

A root certificate is a cryptographic public/private key pair. The private key is used to sign all device certificates. The public key is uploaded to the Golioth server and used at the project level to verify each device certificate when establishing a secure connection.

SERVER_NAME='golioth'

# Generate an elliptic curve private key 
# Run `openssl ecparam -list_curves` to list all available algorithms
# Keep this key safe! Anyone who has it can sign authentic-looking device certificates
openssl ecparam -name prime256v1 -genkey -noout -out "${SERVER_NAME}.key.pem"

# Create and self-sign a corresponding public key / certificate
openssl req -x509 -new -nodes -key "${SERVER_NAME}.key.pem" -sha256 -subj "/C=BR/CN=Root ${SERVER_NAME}" -days 1024 -out "${SERVER_NAME}.crt.pem"

The code above generates two files. The golioth.key.pem is the private key which you must safeguard. All credentials created from this private root key will be fully trusted. Golioth will not have a copy of this key.

The golioth.crt.pem is the public key that is uploaded to Golioth. This serves as a way to verify device credentials as trusted. It cannot be used to sign new device certificates, only the private key has that power.

Upload the root certificate public key to Golioth

Certificate authentication

As of December 2022, the Golioth console includes a “Certificates” option on the left sidebar menu. The resulting window is used to upload the root certificate public key. This is all that you need to do to prepare the Golioth Cloud for your fleet. As long as each IoT device has its own certificate signed with your root certificate’s private key, it will be added to this project the first time it tries to connect to Golioth.

Generate device certificates

PROJECT_SLUG='project_slug'
PRIMARY_HARDWARE_ID='primary_hardware_id'
SERVER_NAME='golioth'
CLIENT_NAME="${PROJECT_SLUG}-${DEVICE_ID}"

# Generate an elliptic curve private key
openssl ecparam -name prime256v1 -genkey -noout -out "${CLIENT_NAME}.key.pem"

# Create a certificate signing request (CSR)
# (this is what you would normally give to your CA / PKI to sign)
openssl req -new -key "${CLIENT_NAME}.key.pem" -subj "/C=BR/O=${PROJECT_SLUG}/CN=${PRIMARY_HARDWARE_ID}" -out "${CLIENT_NAME}.csr.pem"

# Sign the certificate (CSR) using the previously generated self-signed root certificate
openssl x509 -req \
    -in "${CLIENT_NAME}.csr.pem" \
    -CA "${SERVER_NAME}.crt.pem" \
    -CAkey "${SERVER_NAME}.key.pem" \
    -CAcreateserial \
    -out "${CLIENT_NAME}.crt.pem" \
    -days 500 -sha256

The root certificate is the source of trust, and all device certificates are created using this private key. This is why it is crucial that you safeguard your root certificate(s). Device credentials are created using the private key and added to your fleet during production. If newly created devices are compromised, it will not affect any other device credentials already out in the field because they don’t hold the (root) private key, they were simply generated from it.

The code above uses the Golioth Project ID as the PROJECT_SLUG and the SERVER_NAME to connect to the correct project. The root certificate public key was uploaded to this project in the previous section. When creating device certificates, the PRIMARY_HARDWARE_ID is a unique identifier that you generate (e.g. MAC address, naming scheme, any string will work). You need one of these for each device in your fleet.

As with the root certificate, the device certificate creation process will generate a public/private key pair. Both are loaded onto the device and used when establishing a secure connection to the Golioth Servers.

Golioth uses ECDSA (a note for other encryption geeks like us)

When a device is ready to connect to Golioth, it will request and download a server-side (public) certificate. That too will be signed by a trusted authority. The device will need to verify that certificate to ensure it’s not attempting to connect to a bad actor, such as someone pretending to be Golioth by using a “man-in-the-middle” (MITM) attack.

At Golioth, we are embedded developers as well as Cloud experts, so we chose an encryption that is as friendly as possible to resource-constrained devices. That is why we have all the certificates in the chain use ECDSA keys. They are significantly smaller to transfer and less resource intensive for embedded processing.

Summary

We’ve previously written about the importance of using certificates for Internet of Things (IoT) authentication. Certificate validation scales well, especially when it comes to provisioning devices during manufacture. New credentials can be signed with a root certificate without needing to share them back to the cloud server. And since those devices will be sent out into the field there’s an additional security benefit: compromised devices don’t expose symmetrical keys as is the case with PSK-based encryption.

In most cases, production deployments should consider certificate-based authentication for their IoT fleet. The certificate feature is built into Golioth and ready to use today.

We’d love to hear about how you are securing your company’s IoT fleet. Share your questions and tips on the Golioth Forum and don’t hesitate to reach out to our Developer Relations team if you’re interested in a guided tour of our certificate-based authentication!

Jon Beri, Chris Gammell, and Mike Szczys are headed for Pasadena this weekend for the Hackaday Superconference! After a hiatus of several years, we’re excited to get back out into the hardware community and this has become something of a can’t miss event for that crowd.

This is the first time the three of us will be there showcasing Golioth. We’d love to discuss what our next-gen device management can do for you IoT fleet. Let us know you’ll be there using the DevRel email, so we don’t miss a chance to discuss in person.

Open Source RTOS on a Connected ePaper Badge

To kick off the con this Friday we’re presenting our developer training as a Supercon workshop. It focuses on getting started with the Zephyr RTOS.

We’ve been polishing our approach to training all year and this is the culmination of that work. A month ago Chris wrote about the Kasm “container in a browser” experience we’re using to pre-install all the build tools. We’ll be using that approach this weekend so that attendees can be up and compiling within minutes of starting. The workshop will build some muscle memory on the compiling/flashing process, then dive into Devicetree, Pin Control, working with sensors, and the basics of device/cloud interactivity needed in a successful IoT deployment.

The tickets for the workshop sold out right away. If you’re at Supercon, we’d be happy to go through the training with you during “alley-con”. Find us at any time during the weekend to try it out yourself, or even to get a quick badge demo. If you can’t make it to the conference, you can do a self-guided version of the Golioth Developer Training or sign up for future versions of in-person and remote hardware training.

Super-what?

This is the sixth Hackaday Superconference and the first in-person even following a two-year pandemic hiatus. An intimate gathering of fewer than 500 people, everyone you see there has a fascinating backstory, usually involving (or adjacent to) electronic hardware creation. There’s a workshop on designing your own ASICs, talks on wearables, manufacturing, grant-writing for open-source, and the list goes on. Our own Chris Gammell is presenting on his experience as a one-engineer dev shop. Both Mike and Chris helped to kickstart the conference 6 years ago and we’re thrilled that it’s still such a vibrant gathering of hardware enthusiasts.

Almost everyone brings along demos of what we’ve been working on; you can bank on us having a bunch of Golioth demos in our backpacks. There’s also a custom electronic badge, this year it’s an intricate handheld 4-bit computer, complete with a 12-button input register for shifting in your machine-language formatted commands.

Given that there is an input/output expansion header on this badge, it would be a shame if we made it through the weekend without connecting this thing to the Internet in an interesting way. Hit us up on the Golioth Forum thread for this post if you have some ideas on where we can go with that concept.

Take Us for a Test-Drive

We’re not stopping until we’ve spread the word about Golioth. Device management is the piece of IoT ecosystem that companies keep reinventing–a painful and fraught process. Our Dev Tier is free for the first 50 devices.

As your fleet moves from 10 to 100 to 1,000 devices, you face a growing set of problems with handling the data, command and control of the devices themselves, remote firmware updates, and the big one: making sure it remains secure. Give us a chance to show you how we’ve solved these problems, and many you haven’t even thought of yet.

Head over to the Golioth Console to get your free account, then checkout out the Getting Started docs, or jump into the Developer Training we mentioned earlier. Interested in joining a future in-person or remote training? Sign up here.

What is Golioth LightDB State

The Internet of Things is all about transferring data between devices and the cloud. Of course not all data is equal, but with just a few core services you can cover all the bases. One example is the need for persistent data that is easily accessible.

With the Golioth LightDB State service, data can be stored and retrieved from the both the Device and the Cloud side. This is useful for reporting status information, restoring configurations after a power cycle/network outage, and indicating desired changes.

How Golioth enables persistent data

Recording sensor readings might be the first use that comes to mind for IoT devices (our LightDB Stream service is built for that!) but many times we also need stateful data.

State data has no concept of time but instead answers the question: “what was the last status we received?” It acts as a single point of truth that’s quick and easy to access from both the device side and the cloud side. Every time you request a state endpoint you can be sure you’re getting the most recently updated value.

Devices in the field can use state data to report their actual state, things like:

  • What is the timestamp of the last reboot?
  • How full is that SD card used for offline storage
  • What units has the user set for sensor data?

From the Cloud side, state data may be used for command and control. By setting a “desired state” in the Golioth LightDB State, devices with infrequent connects will receive their marching orders the next time they reconnect. Of course this use-case is also solved by the Golioth Device Settings Service which allows project-wide settings adjustments.

LightDB State data can be as simple as a single key/value pair, or as complex as a nested data structure. Devices registered to observe values will be notified every time there is new state data available.

LightDB State using Zephyr RTOS

Your device chooses a path to store and access the LightDB State data. The cloud doesn’t need to be prepared in advance. If your device sends data to a new state endpoint it will automatically be created. There are number of examples of LightDB State API in the Golioth Zephyr SDK.

/* Setting a value on the "counter" endpoint */
err = golioth_lightdb_set_cb(client, "counter",
                 GOLIOTH_CONTENT_FORMAT_APP_JSON,
                 sbuf, strlen(sbuf),
                 counter_set_handler, NULL);

/* Observing data changes on the "desired/my_config" endpoint */
err = golioth_lightdb_observe_cb(client, "desired/my_config",
                 GOLIOTH_CONTENT_FORMAT_APP_JSON,
                 desired_state_handler, NULL);

Here we see asynchronous API calls to set a counter endpoint and to observe data changes on a desired/my_config endpoint. In both cases a “handler” callback function is registered to process the received data (in the case of the observe operation) or handle error messages received from the server.

The Golioth Zephyr SDK also includes the option to send your sensor data using CBOR encoding. This is an alternative to JSON which serializes the data stream to a more compact form to save battery (less radio-on time) and bandwidth (fewer bits being transmitted).

LightDB State using ESP-IDF

The ESP-IDF SDK shares a very similar set of API calls. The device again chooses the endpoint to use on the Golioth LightDB State service. The golioth_basics sample code demonstrates how to use the set/get/observe functions.

/* Setting a value on the "counter" endpoint */
golioth_lightdb_set_int_async(client, "counter", counter, NULL, NULL);

/* Observing data changes on the "desired/my_config" endpoint */
golioth_lightdb_observe_async(client, "desired/my_config", on_my_config, NULL);

Here we see asynchronous API calls used to set the counter endpoint and observe the desired/my_config endpoint.

LightDB State using your own solution

For the extra ambitious out there, you could roll your own device side code that allows you to connect to Golioth. This is the kind of work our wonderful firmware team does whenever they spin up a new SDK. Some of the required elements include:

  • mbedtls in order to create a DTLS connection to the Golioth Cloud
  • A CoAP library
  • (optional) a CBOR library to encode the data

We hardly ever recommend doing this unless you are a very advanced user. Instead, our existing SDKs cover a wide swath of parts, ecosystems, and Real Time Operating Systems. If you are interested in one we don’t already cover, please let us know.

Formatting data and access from the cloud

The Golioth LightDB State service will accept and store any properly-formatted data encoded in either JSON or CBOR. If the endpoint doesn’t yet exist, it will be created when data is received. null will be received when requesting a non-existent endpoint.

As an example, your device might send the following JSON string to LightDB state:

{ "status": { "last_reboot": 1666586559, "location": {"lat":42.988814,"lon":-90.139254,"alt":369.7,"accuracy":2.3,"speed":0.0,"heading":0.0,"time":"2022-10-31T22:49:03.239Z"}, "debug_mode": 0 } }

This imaginary device is recording the last time it rebooted, saving a location from its GPS unit, and reporting on whether or not the debug mode is activated. Whether this data is sent as JSON (which is the case here) or CBOR, the Golioth Console will unpack the information and display it as human-readable JSON:

Golioth LightDB State

What’s really great is that once the data arrives on the Golioth Servers it’s incredibly easy to work with. You can get real-time updates using WebSockets, connect your data to your favorite cloud provider (e.g. AWS, Azure, or GCP) using our Output Streams, and access/update the data using the Golioth REST API.

Start using LightDB State Today!

All of our Device SDKs show how to use LightDB State with minimal code writing from the user. Just pass your state data up to Golioth and you’ll be on your way. Stop by our Forums or send us an email if you get stuck.

Golioth added to Infineon ModusToolbox

Golioth now supports Infineon parts via the ModusToolbox™. We added Golioth device management to the ecosystem a few weeks ago and the example code is available right now to run on Infineon’s line of microcontrollers talking to their Wi-Fi parts.

ModusToolbox™ (MTB) is a software support tool from Infineon Technologies. It includes partner SDKs alongside the company’s officially supported IDEs, drivers, and examples. You can pull in the Golioth example and all dependencies using the Eclipse IDE that is included in MTB, or via the command line tool.

Infineon’s PSoC™ 6 chips are feature rich 32-bit Arm microcontrollers. Paired with the Infineon 4343W, it’s a perfect platform for IoT device builders, and exactly the kind of constrained device that Golioth was built for.

Take advantage of Over-the-Air (OTA) firmware updates, time-series databases, state data management, remote logging, plus the command/control features like remote procedure call (RPC) and the device settings service. The API calls for each of these are demonstrated and well-commented in the golioth_main.c file.

Try the Golioth example using ModusToolbox™

PSoC 6 Wi-Fi BT Prototyping Kit (CY8CPROTO-062-4343W)

We run the Golioth example on the PSoC™ 6Wi-Fi BT Prototyping Kit (CY8CPROTO-062-4343W). Here’s how to try it for yourself:

1. Install Infineon’s ModusToolbox™

Begin by downloading and installing ModusToolbox™ for your system. Then run modustoolbox-eclipsewhich is located in the ide_3.0/eclipse/ subfolder.

2. Create a Golioth Example project

With the Eclipse IDE open, click on File→New→ModsToolbox Application. This will launch the project creator window.

Select the CY8CPROTO-062-4343W from the list of PSoC™ 6 boards and then click next.

Choose the Golioth Example from the Wi-Fi list and click on the Create button. This will take a couple of minutes to clone the Golioth code and all dependencies.

3. Compile and Install MCUboot

Golioth uses MCUboot as the secure bootloader for our Over-the-Air updates. Before flashing the app to the board, we need to compile and install MCUboot. I did this using the IDE’s built-in terminal.

First, we need to install the MCUboot dependencies.

cd ~/mtw/mtb_shared/mcuboot/v1.8.1-cypress/scripts/
python -m pip install -r requirements.txt

Now we can compile and flash MCUboot. Remember to plug a USB cable into the KITPROG3 connector on your PSoC™ 6 devboard before running the program command:

cd ~/mtw/Golioth_Example/bootloader_cm0p/
make build_proj -j8
make program_proj

4. Compile and flash the Golioth App

Before compiling the Golioth App we need to give it credentials to connect to Wi-Fi and also to authenticate with the Golioth server. These are set in the ~/mtw/Golioth_Example/golioth_app/source/golioth_main.hfile.

Use the Wi-Fi credentials for your local access point. Get device credentials from the Golioth Console. If you’ve haven’t yet set up an account, check out our Quickstart. (With Golioth’s Dev Tier your first 50 devices are free.)

Once you’ve saved your changes to the golioth_main.h file, use the terminal to compile and flash the app to your PSoC™ 6 board:

cd ~/mtw/Golioth_Example/golioth_app
make build_proj -j8
make program_proj

Taking the Golioth App for a test drive

[INF] MCUBoot Bootloader Started
[INF] External Memory initialized w/ SFDP.
[INF] boot_swap_type_multi: Primary image: magic=unset, swap_type=0x1, copy_don3
[INF] boot_swap_type_multi: Secondary image: magic=unset, swap_type=0x1, copy_d3
[INF] Swap type: none
[INF] User Application validated successfully
[INF] Starting User Application (wait)...
[INF] Start slot Address: 0x10018400
[INF] MCUBoot Bootloader finished
[INF] Deinitializing hardware...
External Memory initialized w/ SFDP.
=========================================================
[GoliothApp] Version: 1.0.0, CPU: CM4

=========================================================
[GoliothApp] Watchdog timer started by the bootloader is now turned off to mark.
[GoliothApp] User LED toggles at 1000 msec interval

WLAN MAC Address : 74:7A:90:D4:5F:04
WLAN Firmware    : wl0: Jul 18 2021 19:15:39 version 7.45.98.120 (56df937 CY) FWID 01-69db62cf
WLAN CLM         : API: 12.2 Data: 9.10.39 Compiler: 1.29.4 ClmImport: 1.36.3 Creation: 2021-07-18 19:03:20 
WHD VERSION      : v2.5.0 : v2.5.0 : GCC 10.3 : 2022-09-23 13:14:02 +0800
Wi-Fi Connection Manager initialized.
Successfully connected to Wi-Fi network 'MyWiFiAP'.
IP Address Assigned: 192.168.1.153
Secure Sockets initialized
I (5515) golioth_main: Waiting to Golioth to connect...
I (5638) golioth_coap_client: Start CoAP session with host: coaps://coap.golioth.io
I (5643) libcoap: Setting PSK key

I (5649) golioth_coap_client: Entering CoAP I/O loop
I (5886) golioth_main: Golioth client connected
I (5996) golioth_fw_update: Current firmware version: 1.0.0
I (6060) golioth_fw_update: Waiting to receive OTA manifest
I (6258) golioth_fw_update: Received OTA manifest
I (6258) golioth_fw_update: Manifest does not contain different firmware version. Nothing to do.
I (6260) golioth_fw_update: Waiting to receive OTA manifest
I (6322) golioth_main: Synchronously got my_int = 42
I (6323) golioth_main: Entering endless loop
I (9006) golioth_main: Callback got my_int = 42
I (9286) golioth_main: Setting loop delay to 5 s

By monitoring the serial output from the device (that’s /dev/ttyACM0 on my system) we can see all the parts of the app at work. The board powers up and reports the firmware version before connecting to Wi-Fi. Once a Golioth connection is established it checks for firmware updates before it starts writing data to the cloud.

You can view device logs remotely through the Golioth console. Here we see “Sending hello!” messages arriving along with a counter.

Viewing state data for this device, the counter variable is update at the same rate as the hello messages. In the Device Settings on the left sidebar of the console you can add LOOP_DELAY_S and remotely control how how many seconds the device pauses for between sending back these message. It’s a perfect way to control sensor reading frequency across your entire fleet.

Adding Golioth to existing Infineon PSoC™ 6 projects

The Golioth example that is part of ModusToolbox™ is a great blueprint for adding device management to your PSoC™ 6 projects. We’d love to hear what you’re build, and if you need some help we’re here to lend a hand. Show off your successes and post questions on the Golioth forum. Feel free to reach out to the Golioth Developer Relations team to set up a demo or discuss the needs of your IoT fleet.

The Golioth Zephyr SDK is now 100% easier to use. The new v0.4.0 release was tagged on Wednesday and delivers all of the Golioth features with less coding work needed to use them in your application. Highlights include:

  • Asynchronous function/callback declaration is greatly simplified
    • User-defined data can now be passed to callbacks
  • Synchronous versions of each function were added
  • API is now CoAP agnostic (reply handling happens transparently)
  • User code no longer needs to register an on_message() callback
  • Verified with the latest Zephyr v3.2.0 and NCS v2.1.0

The release brings with it many other improvements that make your applications better, even without knowing about them. For the curious, check out the changelog for full details.

Update your code for the new API (or not)

The new API includes some breaking changes and to deal with this you have two main options:

  1. Update legacy code to use the new API
  2. Lock your legacy code to a previous Golioth version

1. Updating legacy code

The Golioth Docs have been updated for the new API, and reading through the Firmware section will give you a great handle on how everything works. The source of truth continues to be the Golioth Zephyr SDK reference (Doxygen).

Updating to the new API is not difficult. I’ve just finished working on that for a number of our hardware demos, including the magtag-demo repository we use for our Developer Training sessions. The structure of your program will remain largely the same, with Golioth function names and parameters being the most noticeable change.

Consider the following code that uses the new API to perform an asynchronous get operation for LightDB State data:

/* The callback function */
static int counter_handler(struct golioth_req_rsp *rsp)
{
    if (rsp->err) {
        LOG_ERR("Failed to receive counter value: %d", rsp->err);
        return rsp->err;
    }

    LOG_HEXDUMP_INF(rsp->data, rsp->len, "Counter (async)");

    return 0;
}

/* Register the LightDB Get callback from somewhere in your code */
static int my_function(void)
{
    int err;
    err = golioth_lightdb_get_cb(client, "counter",
                                 GOLIOTH_CONTENT_FORMAT_APP_JSON,
                                 counter_handler, NULL);
}

Previously, the application code would have needed to allocate a coap_reply, pass it as a parameter in the get function call, use the on_message callback to process the reply, then unpack the payload in the reply callback before acting on it. All of that busy work is gone now!

With the move to the v0.4.0 API, we don’t need to worry about anything other than:

  • Registering the callback function
  • Working with the data (or an error message) when we hear back from Golioth.

You can see the response struct makes the data itself, the length of the data, and the error message available in a very straightforward way.

A keen eye already noticed the NULL as the final parameter. This is a void * type that lets you pass your user-defined data to the callback. Any value that’s 4-bytes or less can be passed directly, or you can pass a pointer to a struct packed with information. Just be sure to be mindful of the memory allocation lifespan of what you pass.

All of the asynchronous API function calls follow this same pattern for callbacks and requests. The synchronous calls are equally simple to understand. I found the Golioth sample applications to be a great blueprint for updating the API calls in my application code. The changelog also mentions each API-altering commit which you may find useful for additional migration info.

The Golioth Forum is a great place to ask questions and share your tips and tricks when getting to know the new syntax.

2. Locking older projects to an earlier Golioth

While we recommend updating your applications, if you do have the option to continue using an older version of Golioth instead. For that, we recommend using a west manifest to lock your project to a specific version of Golioth.

Manifest files specify the repository URL and tag/hash/branch that should be checked out. That version is used when running west update, which then imports a version of Zephyr and all supporting modules specified in the Golioth SDK manifest to be sure they can build the project in peace and harmony.

By adding a manifest to your project that references the Golioth Zephyr SDK v0.3.1 (the latest stable version before the API change) you can ensure your application will build in the future without the need to change your code. Please see our forum thread on using a west manifest to set up a “standalone” repository for more information.

A friendlier interface improves your Zephyr experience

Version 0.4.0 of the Golioth Zephyr SDK greatly improves the ease-of-use when adding device management features to your IoT applications. Device credentials and a few easy-to-use APIs are all it takes to build in data handling, command and control, and Over-the-Air updates into your device firmware. With Golioth’s Dev Tier your first 50 devices are free so you can start today, and as always, get in touch with us if you have any questions along the way!

Over the air updates (OTA)

Internet of Things (IoT) devices are meant to be out in the world, sending back sensor data and providing remote control for all kinds of automation. That provides an interesting challenge when you want to update the way the device operates. Updating device firmware Over-The-Air (OTA) is a critical feature of every IoT deployment.

While OTA updates go by a number of names (DFU, FOTA, etc.) Golioth specializes in making these features available and easy to manage for huge fleets made up of numerous different hardware variants.

OTA Is Difficult

We know what you’re thinking… your design is feature complete when manufactured, why update the firmware? Just kidding, we know you’d never say that. This isn’t about how well you can write the device code today. Including the ability to remotely update firmware will extend the service life of your products, whether it’s adding features you haven’t yet thought about, or patching security vulnerabilities that don’t currently exist. Everyone needs this!

So why aren’t firmware update features more common in embedded devices, especially those that are going to be IoT devices? In short: because it’s difficult!

When an engineer starts a new embedded project, it is almost always local to their development laptop or desktop. They compile and flash “hello world” to their embedded device and then try to get an LED blinking. If the engineer is starting with a simple main.c file and expecting to build things up from there, they need to layer on network stacks, IP handling, message parsing, and a bunch more just to talk to the internet. Then they need to set up the Cloud side of things. Maybe the engineer expects they can fetch from a file hosting service–not the best, but it works in a pinch. But what about managing the entire process? You could implement remote control to tell a device when to update, but that requires even more Cloud side code. Or you can choose to do device-side control that determines when and how to run an update. In both cases, you also need to understand how to properly interact with the device bootloader without bricking the device in the field.

When engineers have this many tasks to get OTA going, it’s no wonder this task is left for “some day”.

How Golioth Manages OTA Updates

As a device management cloud, Golioth delivers all of the tools you need for dependable OTA. That means associating new firmware binaries with specific hardware variants in your fleet, using a rollout process that informs the device a new version is available, and confirming the state of the currently running firmware. Among the most popular Golioth features is a one-click roll-back to previous firmware version.

Manage different firmware using device blueprints

Golioth is secure by default. Firmware is downloaded over an encrypted connection, the signed binary is verified before upgrading, and devices validate that the new binary works before completely switching over.

Golioth OTA release management

Rollout (or roll back) a release with the click of a button. Device Tags filter which devices will get the update.

You can segment your fleet for early testing using our tagging feature. Then roll out new firmware to just a few devices to ensure all is well before taking the plunge on upgrading everything. When you need to troubleshoot a device, the history of its last update is readily available on the Golioth Console or via our REST API.

Take Golioth OTA for a test drive

With Golioth’s Dev Tier, your first 50 devices are free. That’s more than enough to test out OTA, alongside all the other fun features of Golioth like time-series data, the device settings service, and remote procedure calls.

Currently you can choose from two different Golioth SDKs: Zephyr or ESP-IDF.

Golioth OTA using Zephyr

The Golioth Zephyr SDK is a cross-platform solution that currently offers first-class support for the nRF9160 (cellular), mimxrt1060-evkb (Ethernet), ESP32 (WiFi). Any device that uses Zephyr as the Real Time Operating System (RTOS) and uses MCUboot as the bootloader should work with Golioth.

Device serial output shows new firmware release detected, downloaded, updated, and verified after reboot.

Begin with the Golioth Quickstart that will walk through the process of signing up for the Dev tier. Move on to the hardware section to install and configure a Zephyr workspace. Then checkout the DFU (device firmware update) sample from our SDK.

Golioth OTA using ESP-IDF

Anyone working with devices from Espressif should consider using the Golioth ESP-IDF SDK. It currently offers first-class support for all chips in the ESP32 family.

Begin with the Golioth Quickstart that will walk through the process of signing up for the Golioth Dev tier. Move on to the hardware section to install and configure an ESP-IDF workspace. Then checkout the golioth-basics sample from our SDK which includes a well-commented example for using the OTA feature.

Managing firmware update is a critical piece of IoT

Problems on one device sitting on a workbench can be solved by plugging in a cable and updating the firmware directly. One thousand devices spread out over a country (or even merely separated from that firmware engineer) is a very different problem. The ability to update devices in the field means you’re not sending your coworkers to tilt at windmills in faraway lands.

Building and maintaining an OTA system is not a trivial operation, and at Golioth we pour our time and energy into getting it right. This means building with time-tested, industry leading open-source tools like MCUboot, and leveraging our vast experience in the IoT industry to plan for challenges long before you face them.

Give our Dev Tier a try and you’ll be up and running before you know it. Stop by our Forums, our Discord, or send us an email if you get stuck.