How to Optimize Zephyr Configuration and Overlays

Jared Wolff · 2021.6.21 · 5 Minute Read · zephyr · nrf9160 feather · overlay · configuration

How to Optimize Zephyr Configuration and Overlays

It’s not uncommon when developing IoT devices run the devices using different environments. For example, you may have a test server for working out the bugs in both your firmware and cloud interface. You may also have a dedicated production server which will run customer applications. As you can imagine, switching between the two can get hairy and error prone. One way we can fix that problem is through some crafty configuration management.

In this post, i’ll review some of the easiest way you can organize your projects for sanity (and fun!) So fire up your favorite text editor and let’s get rolling!

boards directory

The boards directory is a modern addition to Zephyr. Located usually in <your project root>/boards, you can add configuration and device tree overlay files. Zephyr uses these files to configure the compilation pipeline and also used to enable/disable certain features. You should check out the talk I gave about device tree and setting up hardware at the Zephyr Development Summit. (Should be posted here very soon.)

These files usually contain same name as your board (ex. circuitdojo_feather_nrf9160ns.conf and circuitdojo_feather_nrf9160ns.overlay). Let me explain more about each so you have an idea of how to take advantage of this great feature of Zephyr.

.conf files help you define board specific configuration variables. This allows you to turn on and off features if they’re needed by your board.

For example, in order to get the best low power sleep current the accelerometer needs to be enabled (counterintuitive I know):

# Add the accelerometer
CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_LIS2DH=y

The reason why we enable the device is because Zephyr has built in power saving capabilities for devices. Additionally, we enable it in order to disconnect the pull-up connected to the LIS2DH12TR SA0 pin. (More on that in a second!)

.overlay lets you edit your device tree entry for your device. Imagine it as a way of creating application specific patches to the .dts files placed in the zepyr/boards directory.

For the above example, the accelerometer needs to be defined in the device tree along with the disconnect-sdo-sa0-pull-up flag:

// Enable accelerometer with SA0 pull-up disabled (saves 100uA)
&i2c1 {
    lis2dh@18 {
        compatible = "st,lis2dh";
        label = "LIS2DH";
        reg = <0x18>;
        irq-gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>, <&gpio0 30 GPIO_ACTIVE_HIGH>;
        disconnect-sdo-sa0-pull-up;
    };
  };

This flag disables the SA0 pull-up (25k) which will draw ~135µA no matter what. That’s no bueno especially when it comes to low power applications! When optimizing for low power on the nRF9160 Feather, this was the last step from going to about 150µA to about 35µA. (Nearly a 5x improvement!)

I will add that by default Zephyr will look for a directory called boards in your project root. For the following sections, that is not the case. So we’ll need to make sure that Zephyr knows about them using CMakeLists.txt.

Let’s talk more about how we can split up our prj.conf specifically in the next section.

conf directory

While you can specify all your configuration variables in prj.conf you don’t need to. In this section we’ll create a folder called conf and put all our remaining sub-configuration files in here. Then, depending on environment variables set during compilation, we can make Zephyr pick the right one for the job making your life simpler.

Backend

One way you can differentiate the configuration variables between production and development is by using different .conf files. For example, you could have a dev.conf and a prod.conf. If you’re running Amazon AWS you can set the Broker Host Name for MQTT using

CONFIG_AWS_IOT_BROKER_HOST_NAME.

For example you can set your test endpoint in dev.conf:

CONFIG_AWS_IOT_BROKER_HOST_NAME="test.endpoint.com"

While you can set your production endpoint in prod.conf

CONFIG_AWS_IOT_BROKER_HOST_NAME="prod.endpoint.com"

Then you can configure which file to load by using some simple logic in CMakeLists.txt:

# Print out build type
message(STATUS "Build type: ${BUILD_TYPE} 🚀")

# Define configuration files.
list(APPEND CONF_FILE
  ${CMAKE_CURRENT_SOURCE_DIR}/prj.conf
  ${CMAKE_CURRENT_SOURCE_DIR}/conf/${BUILD_TYPE}.conf
  )

In this case i’m using BUILD_TYPE to determine what file to import. BUILD_TYPE simply is used as the prefix of the .conf file. Setting BUILD_TYPE to prod will cause CMake to look for prod.conf.

Not defined? No problem. You can add a check and set a default value for BUILD_TYPE like so:

# Determine the build type
if (NOT BUILD_TYPE)
  set(BUILD_TYPE debug)
endif()

This way when you’re building your project with west you’ll always have a safe default so things don’t break! Remember, you’ll want to put this entry before importing/running list(APPEND within CMakeLists.txt.

MCUBoot

If you find yourself needing to configure MCUBoot, there is no need to edit the files within the zephyr/bootloader folder. You can create .conf and .overlay files just like you can everywhere else in Zephyr!

For example, you can edit your device tree using an .overlay. For example here’s circuitdojo_feather_nrf9160ns.overlay from a project i’m working on:

/ {
	aliases {
		bootloader-led0 = &blue_led;
    };
};

// Full speed ahead
&uart0 {
	status = "okay";
	current-speed = <1000000>;
	tx-pin = <6>;
	rx-pin = <5>;
};

I’m adding an alias for bootloader-led0 since it’s not included in the version of Zephyr i’m using at the moment. (Upstream has it so this is a temporary fix)

Also, importantly, since the nRF9160 Feather requires MCUBoot to use 1M BAUD, i’m also setting that here along with the TX and RX pins.

In order to use these .conf and .overlay files they’ll need to be imported in your project’s CMakeLists.txt. Here’s an example:

# MCUboot related
list(APPEND mcuboot_OVERLAY_CONFIG
  "${CMAKE_CURRENT_SOURCE_DIR}/conf/mcuboot/mcuboot.conf"
  )

# Adding custom overlay
message(STATUS "Adding .overlay for mcuboot.")
list(APPEND mcuboot_DTC_OVERLAY_FILE
  "${CMAKE_CURRENT_SOURCE_DIR}/conf/mcuboot/circuitdojo_feather_nrf9160ns.overlay"
  )

Specifically, Zephyr looks for mcuboot_OVERLAY_CONFIG and mcuboot_DTC_OVERLAY_FILE in order to append any further overlays for the MCUBoot compilation process to use.

Summing it up

Wrangling Zephyr’s configuration system is just one of the important facets that you’ll need to master in order to get the most of this amazing RTOS. Understanding CMake and how west works is critical for your success in using Zephyr. You should now know that you can add board specific overlays, environment specific configurations and MCU configurations/overlays with ease. Give it a shot and let me know how it goes in the comments!

Last Modified: 2021.7.10

Subscribe here!

You may also like

Embedding Rust Into Zephyr Using Cbindgen

I’m a big fan of the Rust programming language. I’ve used it to build servers, develop test firmware, build CLI tools, and more. One of my goals has been to get some type of Rust…

CBOR for Embedded C and Rust

When sending data theres a few ways you can go about it. In the embedded world, it’s not uncommon to serialize data so it can be efficently sent through the ether. On the other end…