← Back to list

Hardware Abstraction in Zephyr: Part 2 - Zephyr Shields

Zephyr Shields is a mechanism in Zephyr RTOS that allows you to describe external hardware modules (typically expansion boards) separately…

Aliaksandr Kavalchuk · 2026-04-23 10:20 · 15 claps · 2.9 min read
#embedded-systems #firmware #zephyrrtos #device-tree #zephyr
Open on Medium ↗
Wiki topics: TLS · Design Tools & Workflow

Hardware Abstraction in Zephyr: Part 2 - Zephyr Shields

Zephyr Shields is a mechanism in Zephyr RTOS that allows you to describe external hardware modules (typically expansion boards) separately from the main board and integrate them into the project at build time. In essence, it provides an abstraction for additional hardware connected to a base board.

A shield is not just a devicetree overlay, but a set of files located in a board directory:

boards/shields/<shield_name>/
├── shield.yml
├── <shield_name>.overlay
├── Kconfig.shield
└── Kconfig.defconfig
  • shield.yml: Contains metadata about the shield.
  • <shield>.overlay: Describes the hardware part of the shield in devicetree format. It is applied on top of the selected board’s devicetree during the build.
  • Kconfig.shield: Defines Kconfig symbols associated with the shield.
  • Kconfig.defconfig: Provides default configuration values when the shield is used.

NOTE:

You can find a detailed description of all files and their formats in the next Zephyr Project Documentations:

- Shields

- Board Porting Guide

- Configuration System (Kconfig)

Let’s take a look at what these files might roughly look like for the LED Board.

NOTE:

These LED Board Shield files are deliberately kept in a very primitive form. You would not want to use them as-is in a real project, but they are useful here because they make it easier to see how each additional mechanism improves the overall level of abstraction.

led_shield.yml:

name: led_shield
full_name: LED Board Shield
vendor: my_company

led_shield.overlay:

/ {
    led_board: led-board {
        compatible = "my,led-board";
        status = "okay";

        rst-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>;
        pin1-gpios = <&gpio0 7 GPIO_ACTIVE_LOW>;
        pin2-gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;
        pin3-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
    };
};

&uart0 {
    status = "okay";
    current-speed = <115200>;
};

Kconfig.shield:

config SHIELD_LED_SHIELD
    bool "LED Shield"

Kconfig.defconfig:

if SHIELD_LED_SHIELD

config UART_CONSOLE
    y

endif

You can attach one or more shields during the build using the --shield argument:

west build -b <your_board> --shield <shield_name> <your_app>

Example:

west build -b my_board --shield led_shield super_firmware

But this command format works when building without sysbuild. When using sysbuild, shields must be specified via CMake arguments list:

west build -b <your_board> -- \
  -D<target_name>_SHIELD="<shield_1_name> <shield_2_name>"

In multi-image builds (for example, when building both application and network cores), each shield will be applied to all images by default, which may lead to build errors. To avoid this, explicitly specify the shields for each target and use an empty shield for those targets to which it should not apply:

west build -b <your_board> -- \
 -Dapplication_SHIELD="led_shield", -Dnetwork_SHIELD="" <your_app>

As an alternative, this parameter can be set by default in the project’s CMakeLists.txt file:

set(SHIELD <shield_name>)

Zephyr Shields provide a standardized way within the Zephyr ecosystem to describe expansion boards. By following this approach, you can create your own shields that can be reused by other developers, as well as easily integrate third-party shields into your own board design.

However, even though Zephyr Shields significantly improves the architecture and organization of a project, one important limitation remains. In the .overlay file, there is still a direct dependency on specific hardware resources of the main board. In practice, this means that the design is tied not to the connector, but to the actual microcontroller pins:

rst-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>;
pin1-gpios = <&gpio0 7 GPIO_ACTIVE_LOW>;
pin2-gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;
pin3-gpios = <&gpio0 7 GPIO_ACTIVE_LOW>;

While this approach introduces better structure and enables reuse of shields, it still looks as if the shield is connected to the main board, not through a connector, but directly wired to the microcontroller pins.

To address this limitation, we need another Devicetree mechanism — Nexus Nodes, which we will explore in detail in the next article.

Links

Prev: Hardware Abstraction in Zephyr: Naive Approaches

Next: Hardware Abstraction in Zephyr: Part 3 -Device Tree Nexus

Thanks for the support — https://www.buymeacoffee.com/zamuhrishka

Thanks for the support — https://www.buymeacoffee.com/zamuhrishka


메타데이터
post_id
0a0ea8875cb9
slug
hardware-abstraction-in-zephyr-zephyr-shields-separating-boards-and-modules-0a0ea8875cb9
url
https://medium.com/@aliaksandr.kavalchuk/hardware-abstraction-in-zephyr-zephyr-shields-separating-boards-and-modules-0a0ea8875cb9
canonical_url
https://medium.com/@aliaksandr.kavalchuk/hardware-abstraction-in-zephyr-zephyr-shields-separating-boards-and-modules-0a0ea8875cb9
author_url
https://medium.com/@aliaksandr.kavalchuk
status
ok
fetched_at
2026-07-16 00:55:23