Skip to content

node-libgpiod examples

Some samples of what you can do combining node ecosystem and sensors. All samples bellow assumes that you did the environment setup correctly.

Web Blinker

You can put a node server running a small page which offers a nice button to make a led blink in no time:

bash
mkdir web-blinker
cd web-blinker
npm init -y
npm i node-libgpiod
# use 'express@4' if node version on the SBC is too much old
npm i express
npm i alpinejs
touch index.js
mkdir public 
touch public/index.html

Define the html page and the script:

html
<!DOCTYPE html>
<html>

<head>
  <style>
    body {
      background-color: black;
      color: white;
      text-align: center;
      padding-top: 50px;
    }

    h1 {
      font-size: 48px;
    }

    p {
      font-size: 24px;
    }
  </style>
  <!-- alpine.js -->
  <script defer src="cdn.js"></script>
</head>

<body x-data="{ blinking: false }">
  <h1>Web Blinker</h1>
  <p>This is a simple web blinker application.</p>
  <button @click="blinking = !blinking ; fetch(`api/led/${blinking ? 'on' : 'off'}`)">
    Blink
    <span x-show="blinking">ON</span>
    <span x-show="!blinking">OFF</span>
  </button>

</html>
javascript
// index.js
const express = require('express');
const gpio = require('node-libgpiod');

const app = express();
const chip = new gpio.Chip(3);

app.use(express.static('public'));
app.use(express.static('node_modules/alpinejs/dist'));

app.get('/api/led/:state', (req, res) => {
  const { state } = req.params;
  console.log(`Received request to turn LED ${state}`);
  try {
    const v = state === 'on' ? 1 : 0;
    const led = chip.getLine(20);
    led.requestOutputMode();
    led.setValue(v);
    led.release();
    res.json({ status: `LED turned ${state}` });
  } catch (error) {
    console.error('Error controlling LED:', error);
    res.status(500).json({ error: 'Failed to control LED' });
  }
});

app.listen(3000, () => {
  console.log('Web Blinker app listening on port 3000');
});

There, a simple web interface using modern javascript, simple like that.

Device ui

Another cool example is this web ui for all device details.

It's like gpioinfo, gpioget and gpioset but served over the network.

bash
mkdir device-ui
cd device-ui
npm init -y
# use 'express@4' for older node versions
npm i express pug htmx.org bulma node-libgpiod
mkdir -p views/{layouts,pages,partials,components,controls}
touch index.js
touch views/layouts/base.pug
touch views/pages/index.pug
touch views/pages/chip.pug
touch views/partials/pin.pug
touch views/components/gpio-chip.pug
touch views/controls/pin-active-state.pug
touch views/controls/pin-consumer.pug
touch views/controls/pin-direction.pug

Bulma CSS framework provides a nice and modern look and feel, htmx gives dynamic behavior in a declarative way and pug eases the creation of interface components.

For example, this pug template renders the chip details page:

pug
//- pages/chip.pug

extends ../layouts/base

include ../components/gpio-pin

block content
  .section
    a(href="/") Back
    h1= `${chip.name} details` 
    h2= `Label: ${chip.label}, Lines: ${chip.lines.length}`
    .is-flex.is-flex-wrap-wrap.is-gap-4
      each line in chip.lines
        .is-flex
          +gpio-pin(line)
    a(href="/") Back

The backend express handler providing the chip details follows:

javascript
//...
app.get('/chip/:name', (req, res) => {
  const chip = new gpio.Chip(req.params.name)
  const lines = []
  for (let i = 0; i < chip.numberOfLines; i++) {
    const line = chip.getLine(i)
    lines.push({
      chipName: chip.name,
      offset: line.offset,
      name: line.name,
      value: line.value,
      consumer: line.consumer,
      direction: line.direction,
      activeState: line.activeState
    })
  }
  res.render('pages/chip', { 
    chip: { name: chip.name, label: chip.label, lines } 
  })
})

The resulting page looks like this:

device ui

Check out the complete sample

Sensor message queue

Further reading

Uou can check the old examples