Getting Started with UART on Zephyr using HydraBus

January 15, 2026 in Embedded4 minutes

A practical guide to setting up UART on custom pins with Zephyr and testing the communication using HydraBus

In this post, I’ll show you how to set up UART communication on the STM32H573I-DK board using Zephyr RTOS, and how to test it with a HydraBus.

This article is a practical application of the Getting Started with Zephyr documentation. The goal is to get familiar with Zephyr OS and dust off my HydraBus that has been sitting unused for too long.

Hardware Setup

  • STM32H573I-DK: Development board with STM32H573 MCU
  • HydraBus: Hardware hacking tool for protocol analysis
  • Jumper wires: To connect UART3 pins to HydraBus

On the STM32H573I-DK, UART3 is available on the Arduino connector. Connect:

  • UART3 TX → HydraBus RX (D1)
  • UART3 RX → HydraBus TX (D0)
  • GND → GND

Verifying USART3 in the Devicetree

Before creating the project, confirm that USART3 exists in the board’s devicetree:

west build -b stm32h573i_dk samples/hello_world
cat build/zephyr/zephyr.dts | grep -A 10 "usart3: arduino_serial"

Output:

usart3: arduino_serial: serial@40004800 {
    compatible = "st,stm32-usart",
                 "st,stm32-uart";
    reg = < 0x40004800 0x400 >;
    clocks = < &rcc 0x9c 0x40000 >;
    resets = < &rctl 0xe92 >;
    interrupts = < 0x3c 0x0 >;
    pinctrl-0 = < &usart3_tx_pb10 &usart3_rx_pb11 >;
    pinctrl-names = "default";
    current-speed = < 0x1c200 >;
    status = "okay";
};

USART3 is already defined with status “okay” and mapped to PB10 (TX) and PB11 (RX), which correspond to the Arduino connector pins D1 and D0.

Zephyr Project Structure

Create a new project with this structure:

uart_demo/
├── CMakeLists.txt
├── prj.conf
├── boards/
│   └── stm32h573i_dk.overlay
└── src/
    └── main.c

CMakeLists.txt

The CMake build file integrates your application with the Zephyr build system:

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(uart_demo)
target_sources(app PRIVATE src/main.c)

prj.conf

The Kconfig file enables the necessary drivers for UART communication:

CONFIG_SERIAL=y
  • CONFIG_SERIAL=y: Enables the serial driver subsystem, which provides the core UART functionality. Without this, the UART peripheral cannot be used.

boards/stm32h573i_dk.overlay

The devicetree overlay enables USART3, which is routed to the Arduino connector pins (D0/D1) on the STM32H573I-DK board:

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

src/main.c

The application code initializes USART3 and sends “ping” every second. This demonstrates basic UART output using Zephyr’s polling API:

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>

#define PING_INTERVAL_MS 1000

int main(void)
{
	const struct device *uart3 = DEVICE_DT_GET(DT_NODELABEL(usart3));

	if (!device_is_ready(uart3)) {
		printk("USART3 not ready\n");
		return -1;
	}

	printk("Starting ping on USART3\n");

	while (1) {
		const char *msg = "ping\r\n";
		for (int i = 0; msg[i] != '\0'; i++) {
			uart_poll_out(uart3, msg[i]);
		}
		k_msleep(PING_INTERVAL_MS);
	}

	return 0;
}

This simple program sends “ping” every second over UART3. Key functions and macros used:

  • DT_NODELABEL(usart3): Returns the devicetree node identifier for the node with the label usart3. Labels are defined in the devicetree and provide a human-readable way to reference nodes.
  • DEVICE_DT_GET(node_id): Retrieves a pointer to the device structure at compile time from a devicetree node identifier. This is the recommended way to get device handles in Zephyr.
  • device_is_ready(dev): Checks if a device has been successfully initialized and is ready for use. Always verify this before using a device.
  • uart_poll_out(dev, c): Sends a single character over UART using polling mode. This function blocks until the character is transmitted.

Observing the Communication with HydraBus

Once the firmware is flashed on the STM32H573I-DK, you can observe the UART communication using HydraBus.

Finding the HydraBus Serial Port

Connect the HydraBus to your computer via USB and identify the serial port:

dmesg | grep tty

Look for a line mentioning ttyACM:

cdc_acm 1-1:1.0: ttyACM0: USB ACM device

Connecting to HydraBus

Open a serial terminal on the identified port:

minicom -D /dev/ttyACM0

Configuring UART Mode

In the HydraBus terminal, enter UART mode:

> uart
Device: UART1
Speed: 9600 bps
Parity: none
Stop bits: 1
uart1> show pins
TX: PA9
RX: PA10

Set the baud rate to 115200 to match our firmware settings:

uart1> speed 115200
Speed: 115200 bps

Entering Bridge Mode

To passively monitor the UART communication, enter bridge mode. This connects the HydraBus UART RX directly to your terminal:

uart1> bridge

You should now see the “ping” messages appearing every second:

ping
ping
ping
...

Press any key to exit bridge mode and return to the HydraBus prompt.

Conclusion

Zephyr makes embedded development remarkably straightforward, even for low-level protocols like UART. The ability to configure peripheral settings (baud rate, pins, etc.) directly in the devicetree means firmware can be easily ported between different MCU platforms by simply swapping the overlay file. This hardware abstraction is powerful: write your application logic once, and adapt it to new hardware with minimal code changes.

HydraBus proves to be an invaluable prototyping and testing tool. Its interactive shell makes it easy to quickly validate hardware communication without writing dedicated test code. Combined with pyHydrabus, it opens the door to automated testing scenarios—imagine scripting a full protocol validation suite that sends commands and verifies responses programmatically.

References