One of the most common questions we hear from our community is how to add Golioth to existing ESP-IDF projects. Until recently, our go-to resource was a blog post that walked users through the process. While the fundamentals of adding Golioth to CMake-based projects remain the same, that guide has become outdated due to significant updates to the Golioth API. Naturally, many users have noticed this and have pointed out areas where the older guide no longer applies.

The most notable change is the evolution of the Golioth Firmware SDK. We’ve made significant improvements, including combining with our previously standalone Zephyr SDK, making it a unified SDK for multiple platforms. These changes make it easier for developers to switch between frameworks, leverage common tools, and maintain consistency across IoT projects. However, they also mean some of the specifics in the original ESP-IDF blog post no longer align with the latest practices.

For example, in step 4 of the original post, users were instructed to include the golioth.h header file to access the API functions. That file no longer exists, so if you’re using the latest version of the Golioth Firmware SDK, following the original guide will lead to confusion and build errors.

Another limitation of the original post is that it doesn’t address the needs of users who are new to CMake. If you’re just starting with ESP-IDF and want to leverage the common example code we provide in our SDK, the older guide won’t be of much help.

We’re here to remedy that. Whether you’re updating an existing project or starting fresh, this updated post will guide you step-by-step through the process of adding Golioth to your ESP-IDF project.

Getting Started with Golioth Integration

This guide focuses on adding Golioth to an existing ESP-IDF application, so we’re assuming you already have the ESP-IDF development environment set up on your system. If not, we recommend following the ESP-IDF Quickstart Guide to get up and running before proceeding.

For this walkthrough, we’ll use the /esp-idf/examples/wifi/getting_started/station example code as our starting point. This example provides a solid foundation for adding Golioth functionality. To begin, copy the example project to a new folder, which we’ll call esp_hello_golioth:

cp -r ~/esp-idf/examples/wifi/getting_started/station esp_hello_golioth

Step 1: Adding Golioth as a Component

To integrate Golioth into your project, we first need to include the Golioth Firmware SDK as a submodule. This allows you to pull in the latest SDK features while keeping your project organized. However, for this step to work, your project must be under version control. If it isn’t already, initialize a Git repository:

cd ~/esp_hello_golioth
git init

Next, navigate to your project directory and add the Golioth Firmware SDK as a submodule. Run the following commands to include the SDK and ensure all its dependencies are updated:

git submodule add https://github.com/golioth/golioth-firmware-sdk.git submodules/golioth-firmware-sdk
git submodule update --init --recursive

After running these commands, you’ll find the Golioth Firmware SDK located in the submodules folder, specifically pulled from the SDK’s main branch.

Working with Specific SDK Releases

While using the main branch ensures you’re working with the latest code, it’s often better to lock your project to a specific SDK release for stability and consistency. To do this, you can checkout a specific tag or release version after adding the submodule:

cd submodules/golioth-firmware-sdk
git checkout <release-tag>
git submodule update --init --recursive

Replace <release-tag> with the version of the SDK you wish to use, such as v0.15.0. This ensures your project always uses a stable and tested version of the Golioth SDK, avoiding potential issues with breaking changes or experimental updates.

With the Golioth SDK installed as a component, we are ready to move on to the project configuration.

Step 2: Configuring CMake for the Golioth SDK

To integrate the Golioth SDK into your ESP-IDF project, we need to modify the CMake configuration files to ensure the SDK is included during the build process. This involves updating the top-level CMakeLists.txt file and the CMakeLists.txt file in the main directory.

Add the Golioth SDK to the Component Directories

First, we need to tell CMake where to find the Golioth SDK components. Open the project’s top-level CMakeLists.txt file and add the following line before the project() directive:

list(APPEND EXTRA_COMPONENT_DIRS submodules/golioth-firmware-sdk/port/esp_idf/components)

This informs CMake to include the Golioth SDK’s ESP-IDF components, which are located in the submodules/golioth-firmware-sdk/port/esp_idf/components folder.

Specify Golioth as a Dependency

Next, we need to declare the Golioth SDK as a dependency in your project’s main/CMakeLists.txt file. For the ESP-IDF example code we’re using, there are no existing dependencies defined. To add Golioth, insert the following at the top of the main/CMakeLists.txt file:

set(deps
"golioth_sdk"
)

This step ensures that CMake properly resolves the Golioth SDK as a required component during the build process.

Why These Changes Matter

By making these adjustments, we’re effectively linking the Golioth SDK into your project’s build system. This allows you to call Golioth APIs and ensures that the SDK is correctly compiled along with the rest of your application.

Now, let’s move on to configuring the application!

Step 3: Configuring Kconfig Settings

To ensure the Golioth SDK functions correctly, we need to enable specific configuration options using ESP-IDF’s Kconfig system. These settings enable essential features like Mbed-TLS and Golioth-specific options. Instead of modifying configurations manually each time, we’ll replace the contents of the originalsdkconfig.defaults file in the top-level project folder with the following:

CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
CONFIG_MBEDTLS_PSK_MODES=y
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
CONFIG_MBEDTLS_TLS_CLIENT_ONLY=y
CONFIG_LWIP_NETBUF_RECVINFO=y
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_GOLIOTH_AUTO_LOG_TO_CLOUD=y
CONFIG_GOLIOTH_COAP_REQUEST_QUEUE_MAX_ITEMS=20

Explanation of Key Settings

  • Mbed-TLS Configuration
    • Golioth relies on Mbed-TLS for secure communication. The first four CONFIG_MBEDTLS_ options enable support for DTLS, PSK modes, and client-only TLS.
  • Networking Enhancements
    • CONFIG_LWIP_NETBUF_RECVINFO enables additional networking features needed by Golioth, while the FreeRTOS settings CONFIG_FREERTOS_USE_TRACE_FACILITY and CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS help monitor and debug system performance.
  • Wi-Fi Adjustment
    • Since this example focuses on station mode, we disable unnecessary SoftAP support with CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n.
  • Code Optimization
    • The setting CONFIG_COMPILER_OPTIMIZATION_SIZE=y ensures the code is optimized for size, which is often critical for IoT devices.
  • Golioth-Specific Settings
    • CONFIG_GOLIOTH_AUTO_LOG_TO_CLOUD enables automatic uploading of GLTH_LOGX log statements to the Golioth Logs service.
      CONFIG_GOLIOTH_COAP_REQUEST_QUEUE_MAX_ITEMS sets the maximum number of queued CoAP requests for smooth operation.

Why These Settings Matter

The configurations in sdkconfig.defaults ensure that all necessary features are enabled and optimized for using Golioth with ESP-IDF. The final two symbols are particularly useful for automatically uploading log messages to the Golioth Logs service, making debugging and monitoring easier.

With this step complete, your project is now configured to build with Golioth’s essential components.

Step 4: Using Golioth API Calls in Your Code

Now that the Golioth Firmware SDK is configured in your project, you can start incorporating its functionality into your application. The first step is to include the appropriate header file in your code to provide access to Golioth’s API, enabling communication with Golioth services:

#include <golioth/client.h>

For demonstration purposes, we’ll create a simple counter that runs in an infinite loop. Every five seconds, the counter value will be sent to Golioth as a Log message. Below is the modified app_main function, with the new code starting at line 14:

void app_main(void)
{
    //Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_init_sta();

    //Everything above this line is unchanged from the ESP-IDF example code
    const char* psk_id = "your-golioth@psk-id";
    const char* psk = "your-golioth-psk";

    const struct golioth_client_config config = {
        .credentials = {
        .auth_type = GOLIOTH_TLS_AUTH_TYPE_PSK,
        .psk = {
            .psk_id = psk_id,
            .psk_id_len = strlen(psk_id),
            .psk = psk,
            .psk_len = strlen(psk),
    }}};

    struct golioth_client *client = golioth_client_create(&config);
    assert(client);

    uint16_t counter = 0;
    while(1) {
        GLTH_LOGI(TAG, "Hello, Golioth! #%d", counter);
        ++counter;
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

For simplicity, in this example, we’ll hardcode the credentials (PSK-ID and PSK) directly in the application code. While this approach is sufficient for demonstration purposes, it’s never recommended for production use. In the next blog post, we’ll explore how to securely store these credentials using the ESP-IDF common code from the Firmware SDK to set them in the NVS (Non-Volatile Storage) subsystem.

If you’re following along, the next step is to set up your WiFi credentials. You can do this in one of two ways:

  1. Use menuconfig to configure the WiFi settings interactively
  2. Modify the default values directly in the main/Kconfig.projbuild file

Viewing the Results on the Golioth Console

Once your application is running, you can observe its behavior through the serial output. This output confirms that the Golioth client has successfully connected to the platform and is sending log messages as expected. Here’s a sample snippet of what the serial output might look like:

I (896) wifi:AP's beacon interval = 102400 us, DTIM period = 1
I (3316) esp_netif_handlers: sta ip: 192.168.8.113, mask: 255.255.255.0, gw: 192.168.8.1
I (3316) wifi station: got ip:192.168.8.113
I (3316) wifi station: connected to ap SSID:mpuric password:abcdeedcba
I (3326) golioth_mbox: Mbox created, bufsize: 2520, num_items: 20, item_size: 120
I (3336) wifi station: Hello, Golioth! #0
I (3396) golioth_coap_client_libcoap: Start CoAP session with host: coaps://coap.golioth.io
I (3396) golioth_coap_client_libcoap: Session PSK-ID: 20240403080418-esp32@dev
I (3406) libcoap: Setting PSK key

I (3416) golioth_coap_client_libcoap: Entering CoAP I/O loop
I (4376) golioth_coap_client_libcoap: Golioth CoAP client connected
I (8336) wifi station: Hello, Golioth! #1
I (13336) wifi station: Hello, Golioth! #2
I (18336) wifi station: Hello, Golioth! #3
I (23336) wifi station: Hello, Golioth! #4

From the log messages, you’ll see the counter value incrementing every five seconds, just as we implemented in the code. This data is now available in the Golioth Console:

What’s Next?

In this demo, we’ve covered the basics of integrating Golioth into an ESP-IDF project and sending log messages. In the next blog post, we’ll walk through using and adapting the common example code from the Golioth Firmware SDK to speed up your development process!

There are 512 supported boards (according to find -name board.yml | wc -l) already in the Zephyr tree. Most of them are real hardware platforms and the remaining ones are virtual. Why would you bother with a virtual platform? Zephyr can probably build for the SoC or development board of your choice, right? In this post, I’m going to talk about the reasons you want to try out Native Simulator.

Spoiler: Your Zephyr applications development time will drop through the floor.

Zephyr support for virtual platforms

Zephyr comes with support for various virtual platforms, but two of them are most widely used:

  • QEMU
  • Native Simulator

Both are extensively used in Zephyr Continuous Integration pipelines as well as during development by Zephyr users.

QEMU

QEMU is a generic machine emulator. It emulates CPUs by interpreting architecture-specific instructions as well as some peripherals like UART, flash, and networking adapters. Its main advantage is that binary (compiled code) running on QEMU is very similar to the binary that runs on a real hardware. All the low-level instructions, memory-mapped peripheral access, constrained RAM, thread context switching, thread stack sizes, interrupt handling, step-debugging with GDB, and many others mechanisms behave almost the same as on a real microcontroller.

Networking with QEMU can be achieved by setting up a TUN/TAP interface on a Linux host system. Once set up, you attach to the emulated network adapter that is handled by Zephyr drivers. The application is built with Zephyr and has access to the same network as the host machine (like a Linux laptop). After correctly configuring the TUN/TAP interface it is possible to access internet without additional hardware.

Native Simulator

Native Simulator is a POSIX architecture based “board” (Zephyr target) that runs as a standalone Linux executable. It is based on native_simulator and Zephyr POSIX architecture. As opposed to QEMU, it does not need any middle layer that emulates instructions or peripheral access. Instead, Zephyr (under Native Simulator) runs natively on Linux with very little overhead. Most of the time, it’s as fast as any regular Linux application.

However, Native Simulator does not emulate microcontroller peripherals the same way as QEMU does. It has special modules and functions called trampolines. As an example, instead of using memory mapped I/O to handle UART drivers (and logging and shell modules that utilize UART backend) there are trampolines to translate UART access APIs to pseudo-terminal I/Os on the Linux host.

Networking with Native Simulator was possible with TUN/TAP interface. So development experience in terms of IoT applications was similar to QEMU.

The need for offloaded sockets

Issues with TUN/TAP

Networking with QEMU and/or Native Simulator requires root privileges on the host computer in order to create the TUN/TAP network interface. It routes the traffic between Zephyr and the internet. This is a bit of an inconvenience for hackers that have Zephyr SDK installed directly on their Linux workstation. Setting up proper privileges in Docker is possible as well, when such a container is used for development purposes. But what about networking in CI pipelines with GitHub Actions or GitLab CI? The only option to get that working are self-hosted runners.

Use of TUN/TAP interface allows us to test almost the entire Zephyr networking stack, down to the Ethernet layer. However there is no platform-specific driver that talks to an Ethernet phy. Instead, there is a driver that sends Ethernet frames to a virtual TUN/TAP interface that requires setup on the host (e.g. Linux) system. This has advantages like higher code coverage when testing IoT applications.

Unfortunately, there are many disadvantages as well. Setting up TUN/TAP interface requires running as a privileged user on the host system. This might not be an issue on personal PC or laptop. However, root access inside Docker might not always be possible. This is especially true when using existing infrastructure, like GitHub Codespaces, GitHub-hosted runners in GitHub Actions, or hosted GitLab Runners in GitLab.

Offloaded sockets as an alternative

Zephyr has quite a unique feature called socket offloading. This is a mechanism that allows us to utilize (offload to) an external networking stack. Such a stack can be implemented as a 3rd-party library with proprietary drivers that come with a modem. Alternatively, we could use this with an external modem, commonly used with AT commands. In both cases, the contract between the Zephyr application and the offloaded networking stack is socket-level API. One example platform that uses socket offloading is the Nordic nRF9160.

Native Simulator is just a Linux executable. There are no special permissions required to access internet when writing regular Linux programs in C.

What if BSD the compatible sockets API (socket(), connect(), recv(), send(), …) could be exposed to Zephyr when running under Native Simulator? This should be possible with a bunch of trampolines between Zephyr world and Linux world.

Native Simulator Offloaded Sockets

Implementation of socket offloading for Native Simulator was part of a recent hackday project I worked on at Golioth. At the end of day, UDP communication was working, without any setup. This confirmed the idea about networking in Zephyr without root privileges. The next step in the following months was contributing the work to Zephyr with many followup improvements, so that the community can use it.

Development speed

Why should Native Simulator be used for IoT firmware development instead of real hardware? Flashing firmware on a device, connecting to the internet, and then executing application takes a considerable amount of time. This is where Native Simulator with offloaded sockets shines.

Flashing is not part of the testing process when using Native Simulator. Connecting to the internet (e.g. using WiFi or Cellular) is not needed, since the host machine is connected all the time. And lastly, executing application code is much faster on the beefy host machine compared to a very constrained microcontroller.

This is just theory, so let’s look at some timing measurements for those not convinced yet. We’ll use http_getwith TLS with minimal modifications required to get connected to a WiFi Access Point. Modified code is available at https://github.com/mniestroj/zephyr/tree/native-sim-http-get-benchmark.

In this example we’ll use nRF52840DK with ESP32 running ESP-AT firmware. This is what the “flash + execute” process looks like:

Zephyr's http_get on native_sim vs nrf52840dk

This is how much time it took for each platform to run http_get sample (once it was already built):

  • Native Simulator: 0.42 s
  • nRF52840-DK: 16.80 s (flash 10.90 s, run 5.90 s)

Wouldn’t you like to go 40 times faster in your development?

Next steps

Many improvements to Native Simulator Offloaded Sockets were contributed to Zephyr upstream last month. Those will be part of upcoming Zephyr 3.7.0 (planned for release on 2024/07/26). When the Golioth Firmware SDK includes those changes, it will be much faster to develop and test IoT applications.

Golioth works with Qualcomm

We’re excited to announce the latest update to the Golioth Firmware SDK, release 0.12.0, which now includes support for Zephyr’s newly introduced Modem Subsystem. This enhancement significantly increases the flexibility of our platform, enabling support for a broader array of cellular modem technologies, starting with Qualcomm. 0.12.0 adds support for the Quectel BG95, joining the Nordic Semiconductor nRF9160 (our go-to modem around here!) as a first class cellular modem. We also introduced additional ways to securely store credentials.

Zephyr’s New Modem Subsystem

Introduced in Zephyr 3.5.0, the Modem Subsystem is a unified interface for modem drivers. This addition simplifies the integration of cellular modems (and others) into Zephyr-based projects, greatly expanding the range of devices and technologies that developers can utilize effectively. For a detailed overview of the modem subsystem, check out this summary from Zephyr’s Developer Advocate, Benjamin Cabé.

Integration in Golioth Firmware SDK

With the integration of this modem subsystem in the Golioth Firmware SDK, Golioth users can now more flexibly incorporate a wider array of modem technologies into their IoT projects. There are a lot of great modems and module vendors in the market and providing choice is at the heart of what we do at Golioth.

First Supported Modem and Board

The first modem we are supporting with this updated SDK is the BG95 from Quectel, based on Qualcomm technology. The BG95 is paired with the nRF52840 on the RAK5010 development board from RAKwireless. This combination highlights the flexibility of Qualcomm’s technology integrated into Quectel’s hardware, offering developers robust tools for deploying cellular IoT solutions efficiently.

Why Qualcomm?

We chose to support Qualcomm modems because our community asked for it! Developers have different design needs and want maximum flexibility. They need more options that accommodate diverse business needs. Qualcomm chipsets offer the latest in connectivity protocols and radio technology at competitive prices. Qualcomm provides silicon and support for a wide ecosystem of module vendors, such as Quectel, U-Blox, Telit, and more. Golioth customers have used Qualcomm modems in their products in the past, but needed to do much of the integration engineering themselves. Zephyr’s Modem Subsystem makes it easier to develop applications that integrate Qualcomm modems. Connecting this wider range of modems to Golioth is more hands-off for the user, reducing complexity. Developers can focus more on innovation and less on technical hurdles.

Also in This Release

In addition to new modem support, this release introduces a another feature: DTLS socket offloading for Zephyr. This includes an example for the long-supported Nordic Semiconductor nRF9160.

DTLS socket offloading leverages a modem’s secure credential store, which allows for the use of secure, offloaded sockets. This means there is not any private key material in RAM. This can be a significant advantage as it helps reduce RAM usage, code size, CPU load, and power consumption. Actual benefits will vary depending on the application and how the code is utilized.

This new feature enhances device security and efficiency, contributing further to the versatility and robustness of the Golioth Firmware SDK. Mike previously wrote how to store credentials on the nRF9160 using TLS tags.

Getting Started

To get started with the latest SDK:

  1. Update to the newest release, 0.12.0, from the Golioth Firmware SDK repository.
  2. Explore the documentation and examples provided for integrating the RAK5010 board or try DTLS socket offloading with the nRF9160.
  3. Visit our community forums or support channels if you need help or want to discuss your projects.

Focused on Your Success

At Golioth, we’re committed to providing you with the tools and flexibility needed to succeed in the fast-evolving world of IoT. By adding support for new modems and enhancing the ways you can manage credentials, we aim to streamline your development process and empower your innovative projects. Whether you’re integrating the latest modem technology or implementing secure credential management, Golioth is here to support every step of your journey towards building smarter, more connected solutions.

The latest release of the Golioth Firmware SDK is now available! Tagged with v0.11.0, this release includes support for Zephyr v3.6.0, nRF Connect SDK (NCS) v2.5.2, and ESP-IDF v5.2.1.

Our SDK supports multiple platforms and we actively track (and often participate in) upstream development. As new platform releases become available, we test against the Golioth platform, update as necessary, and cut new releases so the latest and greatest is available in your projects.

Also notable in these release is the addition of several code examples in the ESP-IDF port. We have long included examples in the Zephyr port that show each Golioth service used in isolation. The same examples are being added to ESP-IDF, alongside an example that shows off everything. These are nice references when adding a single service to existing applications.

Numerous minor annoyances have also been vanquished. Remote procedure call (RPC) error messages have been clarified when requesting an RPC that is not registered. Log messages for the LTE monitor in our Zephyr common library can now be filtered by log level. And a number of small fixes rounds out the list.

Check out all of the updates in the release notes. Star the repository for notifications of new releases. Please let us know in the forum if you find any issues or have feature requests!

Back in September, we released beta support for Zephyr RTOS in the Golioth Firmware SDK, as an additional option alongside our existing standalone Zephyr SDK. Today, we’re excited to remove the beta tag and announce full support for Zephyr in our cross-platform Firmware SDK. 

 

Over the last several months we’ve been improving the efficiency of the Firmware SDK and it is now on par with the standalone Zephyr SDK. We’ve expanded our automated test coverage, taken a fine-tooth comb through our APIs and documentation, and listened to and incorporated your feedback (thanks!). All of this means that the version of the SDK we’re releasing today is our best yet – and all of our customers will get these improvements at the same time, whether using Zephyr, ESP-IDF, or any other platform.

 

That’s why we chose to incorporate Zephyr support into our Firmware SDK and deprecate our standalone Zephyr SDK. We’re big fans of Zephyr, but our goal is to support the IoT community at large as a universal IoT connector, and that includes folks who choose to use other platforms. In order to provide the highest quality support to all of our users while moving as fast as possible to deliver new features and value, it made the most sense to invest in a single unified Firmware SDK.

 

Of course, our commitment to Zephyr itself hasn’t changed. We continue to be financial sponsors of the project, sit on both the Technical and Marketing Steering Committees, and provide free Zephyr training. And you’ll be able to find us at the Zephyr Developer Summit in Seattle in April.

Ready to get started?

You’ll find the same features and quality you’ve come to expect from Golioth in our Firmware SDK, but we’ve taken this opportunity to rework some of our APIs. Migrating from the Zephyr SDK is easy, and we’ve prepared a migration guide to help you along the way. For those beginning a new project, our getting started documentation will get you up and running in no time.

What does this mean for the Zephyr SDK?

As of today, our standalone Zephyr SDK is deprecated and not recommended for new designs. We may make periodic bug fixes and security updates to the SDK, but we won’t be adding new features or actively developing the standalone Zephyr SDK. This will result in accelerated delivery of features and support in our cross-platform Firmware SDK, as our team is now singularly focused.

 

We will reach formal end-of-life for the standalone Zephyr SDK on July 31, 2024, at which point we will no longer make any updates to the SDK. If you have any questions, please reach out to your Golioth account rep or contact us at [email protected].



This post has been superseded by a new post on how to add the Golioth FW SDK to an ESP-IDF project, as of December 2024. The post below will be kept for historical reasons, but please utilize the steps in the new post going forward.

Adding Golioth’s device management features to an existing ESP-IDF project is a snap. The Golioth Firmware SDK has excellent support for Espressif parts. Simply add Golioth as a component in your build, enable it in the CMake and Kconfig files, and start using the API functions.

Today I’m going to walk through how to do this using an ESP32 development board, however, any of the ESP32 family of chips (ESP32s2, ESP32c3, etc.) will work. Advanced users will also want to review the Golioth Firmware SDK Integration Guide which is the bases for this post.

Walkthrough

Since this article is about adding Golioth to an existing ESP-IDF application, I assume you already have the ESP-IDF installed. If you do not, you may consider following our ESP-IDF quickstart guide before continuing.

For illustration purposes, I’ve copied Espressif’s /esp-idf/examples/wifi/getting_started/station example code to a folder called esp_hello_golioth and will add the Golioth device management features to the program.

cp -r ~/esp-idf/examples/wifi/getting_started/station esp_hello_golioth

If you are following along, you will want to set your WiFi credentials either by using menuconfig or by changing the default values in main/Kconfig.projbuild.

1. Install Golioth as a component

Add the Golioth Firmware SDK as a submodule of your project. (Note that this means you need to have your project under version control. Hint: git init)

cd ~/esp_hello_golioth
git submodule add https://github.com/golioth/golioth-firmware-sdk.git third_party/golioth-firmware-sdk
git submodule update --init --recursive

2. Add the Golioth SDK to your CMake configuration

We need to tell CMake to include the Golioth SDK in the build. This is done by editing the project’s top-level CMakeLists.txt file and adding the following line before the project() directive in that file:

list(APPEND EXTRA_COMPONENT_DIRS third_party/golioth-firmware-sdk/port/esp_idf/components)

We also need to add the Golioth SDK as a dependency. This is done by editing the deps list in the CMake file in the main directory of the project. For this ESP-IDF example code, there are no existing deps so I added the following to the top of main/CMakeLists.txt:

set (deps
     "golioth_sdk"
)

3. Add Kconfig settings

Golioth needs MbedTLS so it must be enabled via the Kconfig system. I created an sdkconfig.defaults file in the top-level folder and added the following:

CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
CONFIG_MBEDTLS_PSK_MODES=y
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
CONFIG_GOLIOTH_AUTO_LOG_TO_CLOUD=1
CONFIG_GOLIOTH_COAP_REQUEST_QUEUE_MAX_ITEMS=20

The final two symbols were added because I plan to send logging messages to the Golioth servers.

4. Use Golioth API calls in your C code

Now that we’ve unlocked Golioth we can start making API calls. Gain access to the functions by including the Golioth header file:

#include "golioth.h"

For this demo I created a counter to keep track of a forever loop. After a five-second pause, the counter value is sent to Golioth as a Log message, then sent as a LightDB State value. The highlighted code beginning at line 14 is what I added.

void app_main(void)
{
    //Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_init_sta();

    //Everything above this line is unchanged from the ESP-IDF example code

    const char* psk_id = "your-golioth@psk-id";
    const char* psk = "your-golioth-psk";

    golioth_client_config_t config = {
            .credentials = {
                    .auth_type = GOLIOTH_TLS_AUTH_TYPE_PSK,
                    .psk = {
                            .psk_id = psk_id,
                            .psk_id_len = strlen(psk_id),
                            .psk = psk,
                            .psk_len = strlen(psk),
                    }}};
    golioth_client_t client = golioth_client_create(&config);

    uint16_t counter = 0;
    while(1) {
        GLTH_LOGI(TAG, "Hello, Golioth! #%d", counter);
        golioth_lightdb_set_int_async(client, "counter", counter, NULL, NULL);
        ++counter;
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

For simplicity I hardcoded the Golioth credentials (PSK-ID/PSK) which is not a best practice as hardcoded credentials will not survive a firmware upgrade. Both shell and Bluetooth provisioning are demonstrated in the golioth_basics example code which stores these credentials in non-volatile flash.

See the results on the Golioth Console

The serial output of this app shows the Golioth client connecting and Log messages being sent.

I (937) wifi:AP's beacon interval = 102400 us, DTIM period = 2
I (1647) esp_netif_handlers: sta ip: 192.168.1.159, mask: 255.255.255.0, gw: 192.168.1.1
I (1647) wifi station: got ip:192.168.1.159
I (1647) wifi station: connected to ap SSID:TheNewPeachRepublic password:123456789101112internetplease
I (1657) golioth_mbox: Mbox created, bufsize: 2184, num_items: 20, item_size: 104
I (1667) wifi station: Hello, Golioth! #0
W (1677) wifi:<ba-add>idx:0 (ifx:0, c6:ff:d4:a8:fa:10), tid:0, ssn:1, winSize:64
I (1677) golioth_coap_client: Start CoAP session with host: coaps://coap.golioth.io
I (1697) libcoap: Setting PSK key

I (1697) golioth_coap_client: Entering CoAP I/O loop
I (1917) golioth_coap_client: Golioth CoAP client connected
I (6707) wifi station: Hello, Golioth! #1
I (11707) wifi station: Hello, Golioth! #2
I (16707) wifi station: Hello, Golioth! #3
I (21707) wifi station: Hello, Golioth! #4
I (26707) wifi station: Hello, Golioth! #5
I (31707) wifi station: Hello, Golioth! #6
I (36707) wifi station: Hello, Golioth! #7
I (41707) wifi station: Hello, Golioth! #8
I (46707) wifi station: Hello, Golioth! #9

Both Log messages and State data can be accessed from the tabs along the top of the device view in the Golioth Console. Navigate there by selecting Devices from the left sidebar menu and then choosing your device from the resulting list.

The log view shows each message as it comes in, and from the timestamps we can see that the five-second timer is working. For long-running operations, there are time-window selection tools as well as filters for log levels, devices, and modules.

The LightDB State view shows persistent data so you will always see the most recently reported value at the “counter” endpoint. Make sure the refresh dialog in the upper right is set to Auto-Refresh: Real-time to ensue you see the updates as they arrive from the device.

Give Golioth a try!

We built Golioth to make IoT design easier for firmware developers. We’d love it if you took our platform for a test-drive! With our Dev Tier, your first 50 devices are always free. Grab an ESP32 and you’ll have data management, command and control, and OTA firmware update in your toolbelt in no time.