zephyr-overlays-modificar-hardware

Using Overlays in Zephyr to Modify Hardware

  • 5 min

Overlay is a fragment of Devicetree that adds or modifies nodes of the base board without editing Zephyr’s original files.

You will likely connect an I2C sensor, an SPI display, or perhaps need to reassign the UART console because it interferes with your design.

This raises the question: Do I have to edit the original .dts file of the board located in the Zephyr installation folder?

Absolutely NO!

If you edit Zephyr’s original files (zephyr/boards/...), you will lose changes when updating and clutter your installation. To avoid this, we use overlays.

What is an Overlay?

We can think of the board’s Devicetree as a base blueprint. An overlay would be a transparent sheet placed on top to add or correct elements without altering the original.

  • You can add new furniture (sensors).
  • You can cross out a wall (disable a peripheral).
  • You can change a room’s color (modify a property, like baudrate).

When we compile with west build, the system takes the base blueprint, sticks our tracing paper on top, and generates the final blueprint.

Creating Our First Overlay

By default, Zephyr’s build system looks for a file named app.overlay in your project’s root folder (at the same level as prj.conf and CMakeLists.txt).

Anything you write there will be added to the definition of the selected board.

Syntax: Referencing Nodes

Whenever a stable node label exists, it is preferable to reference it with &label rather than repeating the full path.

Do not attempt to navigate the entire tree path (/soc/peripheral@4000/i2c@...), as it is fragile. Instead, use the & operator followed by the label of the node you want to modify.

Suppose we want to change the default UART console speed to 9600 baud (the default is usually 115200).

In your app.overlay file:

/* Reference the node with label 'uart0' */
&uart0 {
    current-speed = <9600>;
};
Copied!

That’s it! When compiling, Zephyr will see that you want to modify &uart0 and will override the current-speed property.

How do I know which label to use? You need to look at the original .dts file of your board (in zephyr/boards/...) or check the board’s documentation. The most common ones are &uart0, &i2c0, &spi1, &gpio0, etc.

Adding an I2C Sensor

This is the most common use case. You have a board (e.g., nRF52DK or ESP32) and you connect a BME280 temperature sensor on the I2C pins.

The I2C bus already exists on the board, but the sensor does not. We need to:

  1. Enable the I2C bus (sometimes it comes disabled by default).
  2. Add the sensor node as a “child” of the bus.

Your app.overlay would look like this:

/* 1. Reference I2C0 controller */
&i2c0 {
    /* Enable the peripheral */
    status = "okay";

    /* 2. Add our sensor inside the bus */
    bme280@76 {
        compatible = "bosch,bme280";
        reg = <0x76>; /* I2C address of the sensor */
    };
};
Copied!

Notice how clean it is. We don’t care about the SDA/SCL pins (those are already defined in the &i2c0 node of the base board). We just say: “On the i2c0 bus, there is a bme280 device at address 0x76”.

The status = "okay" is crucial. Many peripherals are defined in hardware with status = "disabled" to save power. If you don’t set it to “okay” in your overlay, the driver will never start.

Reassigning a GPIO

Sometimes the base board design uses pin 5 for the LED, but you want to use pin 5 for a button and move the LED to pin 10.

Here we override the node properties.

/* An alias does not create a node label. Reference the actual path instead. */

/* Option A: Modify existing node */
&{/leds/led_0} {
    /* Change to port gpio0, pin 10 */
    gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
};

/* Option B: Create our own alias */
/ {
    aliases {
        my-custom-led = &my_new_led;
    };

    leds {
        compatible = "gpio-leds";
        my_new_led: led_custom {
            gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>;
        };
    };
};
Copied!

How to Verify it Worked?

Sometimes we make syntax errors or use the wrong label. The definitive way to know what Zephyr is seeing is to inspect the final compiled Devicetree.

After running west build, open the file:

build/zephyr/zephyr.dts

This file is the sum of: Processor DTS + Board DTS + Your app.overlay.

If the change appears there, the overlay has been applied. A syntax error stops the configuration; if there is no error but the change is missing, check the CMake output for the line Found devicetree overlay to confirm which file was loaded.

Board-Specific Overlays

If your project needs to work on two different boards (e.g., a version for Arduino Nano 33 BLE and another for ESP32), and each requires a different configuration, you can create specific files.

Instead of app.overlay, create a boards/ folder in your project and place:

  • boards/<BOARD>.overlay
  • socs/<SOC>_<BOARD_QUALIFIERS>.overlay

The build system selects these files based on the board target. You can also explicitly specify one with -DDTC_OVERLAY_FILE=path/to/file.overlay.