# Experimental libgpiod GPIO nodes for Node-RED on Raspberry Pi Zero 2 W / Debian Trixie

**URL:** <https://discourse.nodered.org/t/experimental-libgpiod-gpio-nodes-for-node-red-on-raspberry-pi-zero-2-w-debian-trixie/101745>\
**Category:** Developing Nodes\
**Created:** [5 September 2026 18:02 UTC](https://discourse.nodered.org/t/experimental-libgpiod-gpio-nodes-for-node-red-on-raspberry-pi-zero-2-w-debian-trixie/101745 "2026-09-05T18:02:12Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![devifast](https://avatars.discourse-cdn.com/v4/letter/d/43a26b/32.png) [@devifast](https://discourse.nodered.org/u/devifast)\
**Post date:** [5 September 2026 18:02 UTC](https://discourse.nodered.org/t/experimental-libgpiod-gpio-nodes-for-node-red-on-raspberry-pi-zero-2-w-debian-trixie/101745/1 "2026-09-05T18:02:12Z")

</div>

Experimental libgpiod GPIO nodes for Node-RED on Raspberry Pi Zero 2 W / Debian Trixie

Before anything else, I want to make the intention of this project very clear.

**This is not a criticism of `node-red-node-pi-gpio`, its author, its maintainers, Node-RED, DietPi, or anyone involved in those projects.**

Quite the opposite.

I have used the original Node-RED Raspberry Pi GPIO nodes for years, and I have personally corresponded with `@dceejay` in the past. I have a great deal of respect for his work and for the Node-RED project as a whole.

I would also like to explicitly acknowledge `@MichaIng`, who leads the DietPi project.

An earlier discussion about Raspberry Pi GPIO on DietPi/Trixie was an important part of the path that eventually led to this experiment:

> [@Rpi gpio library not work with dietpi trixie](https://discourse.nodered.org/t/rpi-gpio-library-not-work-with-dietpi-trixie/100595/57):
>
> Isn't this an extra step if you are processing the I/O at the Pi anyway? Of course there are use cases when you want to have one or multiple tiny sensors somewhere remotely, and one central system to process stuff, but otherwise this sounds more like a workaround than a solution. And which API/library do you use on the microcontroller itself? I imagine there are low-level methods, likely somewhat vendor-specific (prone to same problem the RPis ran into), but otherwise it is probably the very s…

That discussion solved an immediate DietPi/lgpio problem, but it also raised a broader question.

If the I/O is physically connected to the Raspberry Pi anyway, is moving it to an external microcontroller necessarily the best solution, or is that sometimes simply another workaround?

That question stayed with me.

Later, when we encountered continuous CPU overhead on GPIO inputs, it brought us back to the same idea:

**keep the GPIO on the Raspberry Pi, but try using the modern Linux GPIO interface directly.**

That is how this project started.

* * *

## Who developed this?

This project was developed collaboratively by **two participants: myself and ChatGPT from OpenAI**.

I provided:

- the real Raspberry Pi hardware;
- the original problem;
- the relay and input board;
- measurements;
- engineering requirements;
- all physical testing;
- acceptance criteria.

ChatGPT contributed substantially to:

- investigation of the GPIO software stack;
- analysis of the CPU behaviour;
- architecture;
- implementation;
- code review;
- lifecycle design;
- fault analysis;
- test planning.

I want to say this explicitly because I do not want to claim sole credit for work that was genuinely done together.

**Without ChatGPT's contribution, this particular implementation in its current form would not have been possible for me.**

At the same time, this is not being presented as a finished or perfect solution.

We are almost certainly missing something.

That is one of the main reasons for publishing it.

* * *

# Very important: hardware scope

Everything described below has currently been developed and tested on **one Raspberry Pi model only** :

## Raspberry Pi Zero 2 W

Test environment:

- Raspberry Pi Zero 2 W
- DietPi 10.6 RC2
- Debian 13.6 Trixie
- Linux `6.18.39+rpt-rpi-v8`
- Node-RED 5.0.6
- Node.js 26.8.1
- `python3-libgpiod` 2.2.0
- `/dev/gpiochip0`

We are **not claiming compatibility with Raspberry Pi 3, Raspberry Pi 4, Raspberry Pi 5, Compute Modules, or any other model at this stage**.

This is especially important for Raspberry Pi 5, where the GPIO architecture involves RP1 and the gpiochip topology may differ.

The current implementation assumes the arrangement that we have actually verified on our Raspberry Pi Zero 2 W.

Support for other Raspberry Pi generations should be established through real testing rather than assumptions.

Testing from people with Pi 3, Pi 4, Pi 5 or Compute Modules would therefore be extremely valuable.

* * *

# Why did we start this?

After moving a Raspberry Pi Zero 2 W installation to DietPi / Debian Trixie, the standard Node-RED GPIO nodes worked functionally after the earlier DietPi/lgpio issues had been addressed.

However, we later noticed another behaviour.

With a GPIO input active, one `lgpio` notification thread was consuming approximately **4–4.5% CPU continuously** on our system.

We investigated this rather than assuming that the Node-RED node itself was responsible.

On this particular Trixie installation, the compatibility path was approximately:

```plaintext
node-red-node-pi-gpio
        ↓
Python RPi.GPIO
        ↓
python3-rpi-lgpio
        ↓
lgpio

```

The Python `nrgpio.py` process itself was not busy-looping.

We traced most of the CPU usage to one notification thread lower in the compatibility stack.

This distinction is important:

**we are not saying that `node-red-node-pi-gpio` itself is badly designed.**

The behaviour we observed appears to be associated with the compatibility stack used on this particular modern Debian Trixie installation.

* * *

# Direct libgpiod test

We then tested the same GPIO directly with `gpiomon`.

The continuous CPU usage essentially disappeared.

That led us to try a completely separate approach using:

```plaintext
python3-libgpiod

```

directly.

The result became:

```plaintext
node-red-contrib-dietpi-gpiod

```

Current tested candidate:

```plaintext
v0.1.3

```

Source repository:

> **[GitHub - devifast/node-red-contrib-dietpi-gpiod](https://github.com/devifast/node-red-contrib-dietpi-gpiod)**
>
> Contribute to devifast/node-red-contrib-dietpi-gpiod development by creating an account on GitHub.

* * *

# Design goals

The project deliberately does very little.

Currently it supports:

- Digital GPIO IN
- Digital GPIO OUT

That is all we needed.

There is currently no:

- PWM
- servo support
- analogue I/O
- keyboard/mouse functionality
- attempt to reproduce every feature of `node-red-node-pi-gpio`

The goal is **not feature parity**.

The goal is a small, event-driven digital GPIO implementation for our Debian Trixie environment.

* * *

# Architecture

Instead of starting a separate Python helper for every GPIO node, all dietpi-gpiod nodes share **one Python helper process** :

```plaintext
gpiod_helper.py

```

Conceptually:

```plaintext
Node-RED GPIO nodes
        ↓
one shared gpiod_helper.py
        ↓
python3-libgpiod
        ↓
Linux GPIO character device

```

Input GPIO request file descriptors are registered with a blocking selector.

There is no GPIO polling loop.

Inputs use libgpiod edge events.

Debounce is configured through libgpiod `LineSettings`.

* * *

# CPU results

With real hardware connected and multiple GPIO inputs active, we measured load averages such as:

```plaintext
0.02 / 0.02 / 0.00

```

The shared helper itself was typically around:

```plaintext
0.0–0.2% CPU

```

during our tests on the Raspberry Pi Zero 2 W.

The important result for us was not the exact percentage.

It was that adding multiple GPIO inputs did **not** recreate the previous continuous per-input CPU overhead.

* * *

# Real hardware testing

Our test board has:

- 8 digital inputs
- 8 relay outputs

All eight inputs were tested physically.

## Inputs

| Function | Physical pin | BCM GPIO |
| --- | --- | --- |
| Input 1 | 29 | GPIO5 |
| Input 2 | 22 | GPIO25 |
| Input 3 | 18 | GPIO24 |
| Input 4 | 16 | GPIO23 |
| Input 5 | 15 | GPIO22 |
| Input 6 | 13 | GPIO27 |
| Input 7 | 12 | GPIO18 |
| Input 8 | 11 | GPIO17 |

## Relay outputs

| Function | Physical pin | BCM GPIO |
| --- | --- | --- |
| Relay 1 | 31 | GPIO6 |
| Relay 2 | 32 | GPIO12 |
| Relay 3 | 33 | GPIO13 |
| Relay 4 | 36 | GPIO16 |
| Relay 5 | 35 | GPIO19 |
| Relay 6 | 38 | GPIO20 |
| Relay 7 | 37 | GPIO26 |
| Relay 8 | 40 | GPIO21 |

The runtime uses BCM/libgpiod line numbers.

* * *

# Output state

For an output, the helper does not simply assume that the last command represents the current GPIO state.

After:

```plaintext
set_value()

```

it performs:

```plaintext
get_value()

```

and reports the logical GPIO state back to the Node-RED node.

The node status therefore shows, for example:

```plaintext
GPIO6 = 0

```

or:

```plaintext
GPIO6 = 1

```

This is **logical GPIO readback** only.

It must not be confused with physical feedback from a relay, contactor, valve, pump, etc.

Applications requiring confirmation that the external device really operated still need a separate physical feedback input.

* * *

# GPIO ownership

Only one dietpi-gpiod node may own a BCM GPIO.

For example, if GPIO5 is already configured as an input and another dietpi-gpiod node attempts to use GPIO5 as an output, the second node is rejected:

```plaintext
GPIO5: already used by another dietpi-gpiod node

```

The original owner continues operating.

The rejected node remains unclaimed/error instead of pretending to control the GPIO.

An output node does not execute `0` or `1` commands until its GPIO has been successfully claimed.

* * *

# Fault testing

We deliberately tested failure cases rather than only normal operation.

## Forced helper crash

We deliberately killed the shared helper with:

```bash
kill -9 <helper-pid>

```

Observed behaviour:

1. the helper terminated;
2. an active test relay dropped out;
3. Node-RED automatically started a new helper;
4. only one helper process remained;
5. GPIO inputs were reclaimed automatically;
6. input events resumed;
7. outputs returned to their configured `Initial` state.

The last point is intentional.

After a helper failure we **do not restore the previous commanded output state**.

Outputs return to their configured safe Initial state.

For example:

```plaintext
GPIO6 = 1
helper crashes
new helper starts
Initial = 0
GPIO6 returns to 0

```

* * *

# Helper lifecycle

The JavaScript side uses explicit lifecycle states:

```plaintext
STOPPED
STARTING
RUNNING
STOPPING

```

The Python helper sends:

```plaintext
READY

```

before GPIO claims are issued.

Once shutdown has started, no new GPIO commands are sent to the old helper.

If new GPIO nodes require a helper while the old process is stopping, the code waits for the old helper to actually exit before starting another one.

This was added after we reproduced a real Deploy race in an earlier development version.

* * *

# Node-RED Deploy test

We tested Deploy with an output active.

Observed behaviour:

- only one helper remained;
- no new kernel/libgpiod `Device or resource busy` error occurred;
- all eight inputs recovered;
- the active relay returned to its configured `Initial = 0` state.

* * *

# Node-RED service restart

We also tested:

```bash
systemctl restart node-red

```

with an active relay.

The relay dropped out and returned to the configured safe Initial state.

* * *

# Cold boot / power cycle

The Raspberry Pi Zero 2 W was tested from a complete power-off condition with the real relay board connected.

We did not observe any relay turning on unexpectedly during boot.

This is important for our application, although this behaviour obviously depends on both the Raspberry Pi and the external relay circuitry.

* * *

# What we still do not know

A lot.

This project has been tested intensively on **one Raspberry Pi Zero 2 W**.

That does not make it universally correct.

Areas where community testing would be especially useful include:

- Raspberry Pi 3
- Raspberry Pi 4
- Raspberry Pi 5
- Compute Modules
- different Debian releases
- Raspberry Pi OS
- different libgpiod versions
- different gpiochip layouts
- long-duration operation
- different relay/input hardware
- unusual GPIO configurations
- failure scenarios we have not considered

**We are almost certainly missing something.**

Please treat this project as something to review and test, not as a claim that every possible case has been solved.

* * *

# Relationship to the existing Node-RED GPIO project

Again:

**this project is not intended to replace, criticise or attack `node-red-node-pi-gpio`.**

The original project is much more mature and supports considerably more functionality.

It has also been an important reference for expected Node-RED behaviour and user-interface ideas.

Our implementation takes a different architectural path because we were investigating a specific behaviour on Debian Trixie.

Where ideas or behaviour have been informed by the existing project, we want that influence acknowledged openly and correctly.

We intend to comply strictly with all applicable open-source licence and attribution requirements.

If anyone sees anything in the source, attribution, naming, packaging or licensing that should be corrected, please tell us.

We will correct it.

* * *

# Acknowledgements

I would especially like to thank:

`@dceejay`

for his work on the original Node-RED Raspberry Pi GPIO implementation and for everything he has contributed to Node-RED over the years.

And:

`@MichaIng`

for his work leading DietPi and for the earlier DietPi/Trixie GPIO discussion.

The question raised in that discussion — whether GPIO should really need to be moved away from the Raspberry Pi or whether a modern direct API could be used instead — was an important part of the path that eventually led us to experiment with `libgpiod`.

* * *

# Open for review and contribution

This project is being published specifically so other people can inspect it.

Code review, criticism, fault testing, corrections and improvements are welcome.

If the architecture is wrong somewhere, please tell us.

If there is a race we missed, please try to break it.

If our assumptions about libgpiod are wrong on another Raspberry Pi generation, we want to know.

If the Node-RED packaging or naming should be changed, we would appreciate guidance.

If we have misunderstood or missed an open-source licensing requirement, please tell us and we will correct it.

And if this approach proves useful, contributions to make it more robust across Raspberry Pi generations would be very welcome.

`@dceejay` and `@MichaIng` — if you have the time, I would especially value your technical opinion on what we have built and on anything important that we may have overlooked.

The objective is simple:

**we encountered a real problem, investigated it, built and tested one possible solution, and are now opening that work to the community so it can be checked, corrected and improved.**

Source repository:

> **[GitHub - devifast/node-red-contrib-dietpi-gpiod](https://github.com/devifast/node-red-contrib-dietpi-gpiod)**
>
> Contribute to devifast/node-red-contrib-dietpi-gpiod development by creating an account on GitHub.

---

<div class="post-metadata">

**Author:** ![MichaIng](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/michaing/32/24824_2.png) [@MichaIng](https://discourse.nodered.org/u/MichaIng)\
**Post date:** [6 September 2026 11:25 UTC](https://discourse.nodered.org/t/experimental-libgpiod-gpio-nodes-for-node-red-on-raspberry-pi-zero-2-w-debian-trixie/101745/2 "2026-09-06T11:25:40Z")

</div>

Just mentioning that this will work on Raspberry Pi OS the same way, and most likely on any mainline Linux based distro, too.

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [6 September 2026 12:47 UTC](https://discourse.nodered.org/t/experimental-libgpiod-gpio-nodes-for-node-red-on-raspberry-pi-zero-2-w-debian-trixie/101745/3 "2026-09-06T12:47:28Z")

</div>

Looks like excellent work on an area of Node-RED that many people may have given up on (because of the Pi architecture). So thanks for contributing this to the community, it is efforts like yours that move the platform ever forwards.

I'm afraid I gave up on using Pi's myself and even when using them, never used the GPIO for anything more than a pre-made "hat". The problem (not so much with the Zero's) was that the Pi was relatively expensive and was all too easy to break via a miss-configured GPIO. So most of us took the sensible and more flexible approach of using $2 ESP devices for GPIO which also added positional flexibility.

Many thanks for sharing with the community.

---

<div class="post-metadata">

**Author:** ![PizzaProgram](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/pizzaprogram/32/19431_2.png) [@PizzaProgram](https://discourse.nodered.org/u/PizzaProgram)\
**Post date:** [9 September 2026 10:48 UTC](https://discourse.nodered.org/t/experimental-libgpiod-gpio-nodes-for-node-red-on-raspberry-pi-zero-2-w-debian-trixie/101745/4 "2026-09-09T10:48:59Z")

</div>

Excellent work!

I just like to mention one thing to keep in mind when testing / developing:

- When I've created the [new Firmata-Node](https://discourse.nodered.org/t/announce-node-red-node-arduino-firmata-remastered-v1-3-4-20250331/96317/9) (called "Arduino node" = bad naming)
- Everything worked perfectly fine and stable with RPi PICO boards connected via USB port to the RPi4: "on a desk".
- But after connecting some real 240V engines to the relay board  
(which is theoretically optic-relay protected)
- Probably the sparks of the relays have created strange EMP effects, which causing many times a complete stop of the communication.  
_(The only solution is to unplug the board from USB and replug ☹ )_

So it is advisable to test it that way too.
