If you want to know how to use Node.js on Raspberry Pi for hardware control in 2026, the rules have changed. With the release of the Raspberry Pi 5 and the shift to Raspberry Pi OS Bookworm (Kernel 6.1+), the legacy sysfs GPIO interface (/sys/class/gpio) is disabled by default. This means older, popular libraries like onoff will silently fail or throw permission errors. To reliably control hardware, you must use a library that interfaces directly with the Pi 5’s new RP1 southbridge chip via memory mapping or the pigpiod daemon.

This guide targets the Raspberry Pi 5 (4GB variant) running Node.js 20 LTS, using the pigpio npm package to drive digital I/O and hardware PWM.

The Decision Path: Choosing Your Pi and GPIO Library

Before wiring a single LED, you need to select the right software stack. The Node.js GPIO ecosystem is fragmented, and picking the wrong library for your OS version will cost you hours of debugging.

Node.js GPIO Library Comparison for Raspberry Pi OS Bookworm
Library Underlying Mechanism Pi 4 Compatibility Pi 5 (RP1 Chip) Compatibility Best For
onoff sysfs (Kernel interface) Excellent (Legacy OS) Fails (sysfs deprecated) Older Pi 4 projects on Bullseye
rpio /dev/mem mmap Excellent Poor (RP1 addressing breaks mmap) High-speed bit-banging on Pi 4
pigpio C-bindings / pigpiod daemon Excellent Excellent (Updated for RP1) PWM, servos, and reliable digital I/O
node-libgpiod libgpiod (Modern Kernel API) Good Good (Steep learning curve) Strict enterprise Linux compliance

Decision Tree: Which stack should you build?

  • IF you need hardware PWM for servos or LED dimming AND you are using a Pi 5 ➔ Use pigpio.
  • IF you are maintaining a legacy Pi 4 project on an older OS ➔ Use onoff.
  • IF you are building a commercial product requiring strict POSIX compliance ➔ Use node-libgpiod.
The Concrete Pick: For 95% of hobbyist and prototyping projects in 2026, build with the Raspberry Pi 5 (4GB) and the pigpio npm package. It handles the RP1 chip gracefully, provides microsecond timing, and abstracts the daemon complexity.

Hardware Parts List and Pin Mapping

The Pi 5 draws more peak current than the Pi 4, especially during boot and when driving external loads. Do not use old phone chargers; brownouts will cause the RP1 chip to reset your GPIO states randomly.

Spec Sheet & Parts List

Component Exact Variant / Spec Estimated Cost (2026)
Microcontroller Raspberry Pi 5 (4GB RAM) $60.00
Power Supply Official 27W USB-C PD Power Supply (5V/5A) $12.00
Cooling Official Active Cooler (Required for sustained GPIO/CPU load) $5.00
Storage 64GB microSD (A2 rated, e.g., SanDisk Extreme) $12.00
Components 5mm LED, 330Ω resistor, half-size breadboard, jumper wires $4.00

Pin Mapping Table

The Pi 5 uses the RP1 southbridge, but the physical header remains backward-compatible. We use BCM GPIO numbering in Node.js, not physical pin numbers.

Physical Header Pin BCM GPIO Number (Used in Code) RP1 Internal Name Hardware PWM Support?
Pin 11 GPIO 17 RP1_GPIO17 No (Digital I/O only)
Pin 12 GPIO 18 RP1_GPIO18 Yes (PWM0_6)
Pin 13 GPIO 27 RP1_GPIO27 No (Digital I/O only)
Pin 32 GPIO 12 RP1_GPIO12 Yes (PWM0_0)

Step-by-Step: OS Prep and Node.js Installation

Before writing code, we must prepare the Bookworm OS environment and compile the C++ bindings required by pigpio.

  1. Flash the OS: Use Raspberry Pi Imager to flash Raspberry Pi OS (64-bit, Bookworm) to your microSD. Enable SSH and set your username/password in the advanced settings.
  2. Update the System: SSH into the Pi and run:
    sudo apt update && sudo apt upgrade -y
  3. Install Build Tools: The pigpio npm package requires compiling C code via node-gyp.
    sudo apt install -y build-essential python3 pigpio
  4. Install Node.js 20 LTS: Use the official NodeSource setup script.
    curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
    sudo apt install -y nodejs
  5. Enable the pigpiod Daemon: While the npm package can talk directly to memory, using the daemon prevents memory-access crashes if your script throws an unhandled exception.
    sudo systemctl enable pigpiod
    sudo systemctl start pigpiod
  6. Initialize Project:
    mkdir pi5-node-gpio && cd pi5-node-gpio
    npm init -y
    npm install pigpio

The Code: Digital I/O with Error Handling

This script targets BCM GPIO 17 (Physical Pin 11). It blinks an LED for 10 seconds, then gracefully cleans up the pin state on exit. It includes robust error handling to catch initialization failures.

// pi5-blink.js
'use strict';

const pigpio = require('pigpio');
const Gpio = pigpio.Gpio;

// --- PIN DEFINITIONS ---
// Always use BCM numbering, not physical header numbers
const LED_PIN = 17; 

// --- HARDWARE INITIALIZATION ---
let led;
try {
  // Initialize pigpio library (connects to pigpiod daemon)
  pigpio.configureClock(10, pigpio.CLOCK_PCM);
  
  // Define the pin as an output, initially LOW
  led = new Gpio(LED_PIN, {
    mode: Gpio.OUTPUT,
    pullUpDown: Gpio.PUD_DOWN,
    alert: false
  });
  
  console.log(`[SUCCESS] GPIO ${LED_PIN} initialized.`);
} catch (err) {
  console.error(`[FATAL] Failed to initialize GPIO ${LED_PIN}:`, err.message);
  console.error('Check if pigpiod is running: sudo systemctl status pigpiod');
  process.exit(1);
}

// --- MAIN LOOP ---
let state = false;
const intervalId = setInterval(() => {
  try {
    state = !state;
    led.digitalWrite(state ? 1 : 0);
    console.log(`LED State: ${state ? 'ON' : 'OFF'}`);
  } catch (err) {
    console.error('[ERROR] Write failed:', err.message);
    clearInterval(intervalId);
    cleanup();
  }
}, 500);

// Stop after 10 seconds
setTimeout(() => {
  clearInterval(intervalId);
  cleanup();
}, 10000);

// --- GRACEFUL CLEANUP ---
function cleanup() {
  console.log('[INFO] Cleaning up GPIO states...');
  try {
    if (led) {
      led.digitalWrite(0);
      // Release the pin back to the system
      led.reset(); 
    }
  } catch (err) {
    console.error('[WARN] Cleanup error:', err.message);
  }
  process.exit(0);
}

// Handle Ctrl+C (SIGINT) to prevent pin lockups
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);

Run the script with node pi5-blink.js. If you used the daemon approach, you do not need sudo, which is a massive security win for IoT deployments.

Debugging: Exact Error Strings and the First 3 Checks

When hardware code fails, it rarely fails silently. Here are the exact error strings you will see, ranked by likelihood, and how to fix them.

1. Error: "Error: pigpio error -8, GPIO operation not permitted"

  • Cause: You are trying to access a pin that is currently reserved by the system (like the UART TX/RX pins on GPIO 14/15) or the pigpiod daemon lacks permissions.
  • Fix: Verify you are using a standard GPIO pin (like 17, 27, or 22). If using direct memory mapping instead of the daemon, you must run the script with sudo.

2. Error: "Error: Cannot find module 'pigpio'" or "node-gyp rebuild" fails

  • Cause: The C++ bindings failed to compile during npm install. This happens if build-essential or python3 is missing, or if you are on an unsupported Node.js version (e.g., Node 21 odd-release).
  • Fix: Run sudo apt install build-essential python3, delete node_modules, and run npm install pigpio again. Stick to Node 20 LTS.

3. Error: "Error: pigpio error -2003, bad socket port"

  • Cause: The Node.js script cannot communicate with the pigpiod daemon over the local socket.
  • Fix: The daemon crashed or isn't running. Restart it with sudo systemctl restart pigpiod.

The First 3 Things to Check When It Fails

  1. Is the daemon actually running? Run sudo systemctl status pigpiod. It must show active (running).
  2. Are you using BCM or Physical numbering? Physical Pin 11 is BCM GPIO 17. If you pass 11 into the Gpio constructor, you are actually targeting BCM 11 (Physical Pin 23), which might be floating or connected to something else.
  3. Is the Pi 5 power supply sufficient? If the Pi 5 detects a voltage drop below 4.8V, it will throttle the CPU and occasionally drop USB/GPIO interrupts. Check for the lightning bolt icon on the display or run vcgencmd get_throttled.

Extending or Simplifying the Build

Once you have digital I/O working, you will inevitably want to scale the project. Here is how to adapt this stack.

How to Simplify (Downgrade to Pi 4)

If the Pi 5's $60 price point and 27W power requirement are overkill for a simple sensor node, downgrade to a Raspberry Pi 4 Model B (2GB). You can keep the exact same pigpio code, but you gain the option to use the much simpler onoff library if you flash the older Bullseye OS. For a battery-powered, single-sensor node, a Raspberry Pi Pico W ($6) running MicroPython is a vastly superior, lower-power choice than a full Linux SBC.

How to Extend (Add I2C Sensors and PWM)

To add an environmental sensor like the BME280, do not use pigpio for I2C. Instead, install the i2c-bus npm package. The Pi 5's RP1 chip handles I2C beautifully at 400kHz.

npm install i2c-bus

For hardware PWM (e.g., driving a 5V PC fan via a MOSFET), pigpio shines. Replace the digitalWrite loop with:

// Hardware PWM on GPIO 18 (Physical Pin 12)
const fanPin = new Gpio(18, {mode: Gpio.OUTPUT});
// Set frequency to 25kHz, duty cycle to 50% (500,000 microseconds out of 1,000,000)
fanPin.hardwarePwm(25000, 500000); 

By anchoring your stack to the Raspberry Pi 5 and the pigpio daemon, you bypass the deprecated kernel interfaces and secure a reliable, high-performance foundation for any Node.js hardware project you build this year.