I built a Zephyr-inspired device-tree & driver framework for ESP-IDF

User avatar
mingshu
Posts: 3
Joined: Wed Dec 18, 2024 7:41 am

I built a Zephyr-inspired device-tree & driver framework for ESP-IDF

Postby mingshu » Fri Jun 26, 2026 8:06 am

Hello,

I have been working on a small open-source project called LWDT: https://github.com/oldrev/lwdt

It is a lightweight devicetree and driver framework for native ESP-IDF projects. The goal is to solve a problem I kept running into on ESP32-family boards: once you have more than one hardware variant, board-specific code tends to spread everywhere. GPIO numbers end up hardcoded in application logic, sdkconfig starts carrying board wiring, drivers grow #ifdef branches, and reusing the same custom driver across different boards becomes more painful than it should be.

The inspiration comes from Zephyr RTOS. I love Zephyr's board IDs, decoupled drivers, devicetree-generated macros, and priority-sorted init model. But for many production ESP32 projects, giving up native ESP-IDF is just not a option. ESP-IDF has excellent vendor support, solid Wi-Fi/BLE provisioning, mesh, optimization, and first-class support for new Espressif silicon.

So, LWDT tries to bring a subset of that Zephyr-style device-tree (in Jsonnet format) and driver framework into ESP-IDF instead of replacing it.

What it currently does:
  • Board selection: Select boards with a stable vendor/model ID, similar Zephyr's west -b.
  • Single source of truth: Keep board hardware description in a Jsonnet board.lwdt file instead of scattering it through C code, CMake, and sdkconfig.
  • Target automation: Automatically sets the correct IDF_TARGET from board metadata during the CMake configure phase.
  • Compile-time codegen: Generates C access macros from Jsonnet-backed devicetree files with zero runtime parsing overhead.
  • Overlays support: Supports .lwdt.overlay files for project-level or environment-specific hardware overrides.
  • Loosely-coupled drivers: Provides a small drvfx framework with static device objects.
Quick example:

A board configuration looks roughly like this (using Jsonnet for a cleaner):

Code: Select all

local esp32c3 = import "soc/esp32/esp32c3.lwdt";

esp32c3 + {
  board: {
    label: "board",
    led_gpio: 8,
    led_active_low: 1,
    led_name: "ESP32-C3 SuperMini",
  },
}
Then application code can access it through generated macros without knowing anything about the physical layout:

Code: Select all

#include <lwdt/lwdt.h>
#include "lwdt_generated.h"

#define BOARD_NODE      LWDT_NODELABEL(board)
#define BLINK_GPIO      LWDT_PROP(BOARD_NODE, led_gpio)
#define LED_ACTIVE_LOW  LWDT_PROP(BOARD_NODE, led_active_low)
And the app builds seamlessly via native tooling:

Code: Select all

idf.py build -DLWDT_BOARD_ID=nologo/esp32-c3-supermini
Current status & feedback


This is still early stage and intentionally lean. Current built-in drivers include I2C, SPI, and RTC examples. The project currently focuses on proving the board/BSP, overlay, generated macro, and auto-sorting initialization model.

AI Disclosure


Parts of the drivers in this project were rewritten from my open-source Borneo-IoT LED Controller firmware (https://github.com/borneo-iot/borneo). The rewritten code has been further refined and optimized with the assistance of AI tools.

Have fun!

Who is online

Users browsing this forum: ChatGPT-User, Google [Bot] and 7 guests