pigpio

repository·master·Indexed 21 days ago

https://github.com/fivdi/pigpio

A high-performance Node.js wrapper for the pigpio C library, providing low-latency GPIO control on Raspberry Pi. It supports PWM, servo control, high-frequency digital I/O, microsecond-accurate waveform generation, and interrupt handling. Version 3.3.1 requires root/sudo privileges and can only be used by a single running process at a time.

Tokens
11.1K
Snippets
35
Records
44
Agent score
74%

What's inside pigpio

  1. Handle GPIO Interrupts and Alerts

    master

    The Gpio class is an EventEmitter that provides two ways to monitor state changes:

    Interrupts

    Best for: Low latency.

    • enableInterrupt(edge[, timeout]): Enables interrupts. edge can be RISING_EDGE, FALLING_EDGE, or EITHER_EDGE. timeout is optional (ms).
    • disableInterrupt(): Disables interrupts.
    • Event: 'interrupt' is emitted. The level argument is the level read at the time of the interrupt. If a timeout expires, level is TIMEOUT (2).

    Alerts

    Best for: High frequency/detecting more changes.

    • enableAlert(): Enables alerts. An event is emitted every time the state changes.
    • disableAlert(): Disables alerts.
    • Event: 'alert' is emitted. The level is the state (0 or 1) and tick is the microsecond timestamp since boot.
    • Note: Alerts have higher latency than interrupts as they are queued and fired once per millisecond.

    Glitch Filter

    • glitchFilter(steady): Sets a filter that ignores level changes shorter than steady microseconds. This only affects 'alert' events.
    const { Gpio } = require('pigpio');
    const gpio = new Gpio(4, { mode: 'INPUT', edge: 'EITHER_EDGE' });
    
    gpio.on('interrupt', (level) => {
      console.log(`Interrupt detected! Level: ${level}`);
    });
    
    // Using alerts with a glitch filter
    gpio.glitchFilter(50); // Ignore changes shorter than 50us
    gpio.enableAlert();
    gpio.on('alert', (level, tick) => {
      console.log(`Alert! Level: ${level} at tick: ${tick}`);
    });
  2. Use the Notifier class to monitor GPIO state changes

    master

    The Notifier class provides a high-performance stream of notifications regarding state changes on GPIOs 0 through 31. It can handle over 100,000 notifications per second, with timing accuracy down to a few microseconds.

    To use it, instantiate Notifier with an optional bits bitmask to specify which GPIOs to monitor immediately, or call the start(bits) method later. The notifications are accessed via a Readable stream obtained through the stream() method.

    // Example: Monitor GPIO 0 and GPIO 1
    const notifier = new Notifier({ bits: 0b0011 });
    
    notifier.stream().on('data', (notification) => {
      // Handle notification
    });
  3. Understand pigpio limitations and requirements

    master

    When using the pigpio Node.js package, be aware of the following constraints:

    • Single Process Limit: Because pigpio is a wrapper for the pigpio C library, it can only be used by a single running process at a time. Multiple processes cannot access the library simultaneously.
    • Privileges: Accessing hardware peripherals requires root/sudo privileges. You must run your Node.js process with elevated permissions to interact with the hardware.
  4. Handle tick wrap-around in Alert/Interrupt events

    master

    The tick value provided in 'alert' and 'interrupt' events is an unsigned 32-bit integer representing microseconds since boot. It wraps around approximately every 1 hour and 12 minutes.

    To correctly calculate the difference between two ticks (e.g., endTick - startTick) without issues caused by wrap-around, use the JavaScript sign-propagating right shift operator >> 0 to treat the values as unsigned 32-bit integers before subtraction.

    Correct way to calculate duration:

    const duration = (endTick >> 0) - (startTick >> 0);
    // Example of handling wrap-around
    const startTick = 0xffffffff; // Max 32-bit unsigned
    const endTick = 1;
    
    // WRONG: returns -4294967294
    console.log(endTick - startTick);
    
    // CORRECT: returns 2
    console.log((endTick >> 0) - (startTick >> 0));
  5. Use the GpioBank class for bulk GPIO operations

    master

    The GpioBank class allows you to read or write up to 32 GPIOs in a single operation. This is useful for managing groups of pins simultaneously.

    GPIO Mapping:

    • Bank 1 (BANK1): GPIO0 through GPIO31. Most user-safe GPIOs are located here, though safety depends on your specific board type.
    • Bank 2 (BANK2): GPIO32 through GPIO53.

    To use it, instantiate a GpioBank with either BANK1 or BANK2. You can then use bitmasks to set, clear, or read the state of all pins in that bank at once.

    // Example: Setting and clearing bits in Bank 1
    const bank1 = new GpioBank(BANK1);
    
    // Set GPIO0 and GPIO2 to 1
    bank1.set(0b101);
    
    // Clear GPIO0 (set to 0)
    bank1.clear(0b001);
    
    // Read the current state of all pins in the bank
    const currentState = bank1.read();
  6. Transmit a chain of waveforms with modifiers

    master

    The waveChain(chain) function allows you to transmit an ordered list of wave_ids combined with command codes to create complex sequences, loops, and delays.

    Supported command codes (using 255 as the prefix):

    • Loop Start (255 0): Identifies the start of a wave block for looping.
    • Loop Repeat (255 1 x y): Loops the block x + y * 256 times.
    • Delay (255 2 x y): Delays for x + y * 256 microseconds.
    • Loop Forever (255 3): Loops the block indefinitely. This must be the last entry in the chain.

    Note: Any hardware PWM started by hardwarePwmWrite will be cancelled when starting a chain.

    const pigpio = require('pigpio');
    const Gpio = pigpio.Gpio;
    
    const outPin = 17;
    const output = new Gpio(outPin, { mode: Gpio.OUTPUT });
    
    let firstWaveForm =   [{ gpioOn: outPin, gpioOff: 0, usDelay: 10 }, { gpioOn: 0, gpioOff: outPin, usDelay: 10 }];
    let secondWaveForm =  [{ gpioOn: outPin, gpioOff: 0, usDelay: 20 }, { gpioOn: 0, gpioOff: outPin, usDelay: 20 }];
    let thirdWaveForm =   [{ gpioOn: outPin, gpioOff: 0, usDelay: 30 }, { gpioOn: 0, gpioOff: outPin, usDelay: 30 }];
    let fourthWaveForm =  [{ gpioOn: outPin, gpioOff: 0, usDelay: 40 }, { gpioOn: 0, gpioOff: outPin, usDelay: 40 }];
    
    pigpio.waveClear();
    pigpio.waveAddGeneric(firstWaveForm);
    let firstWaveId = pigpio.waveCreate();
    
    pigpio.waveAddGeneric(secondWaveForm);
    let secondWaveId = pigpio.waveCreate();
    
    pigpio.waveAddGeneric(thirdWaveForm);
    let thirdWaveId = pigpio.waveCreate();
    
    pigpio.waveAddGeneric(fourthWaveForm);
    let fourthWaveId = pigpio.waveCreate();
    
    let chain = [
      firstWaveId,      // transmits firstWaveId
      secondWaveId,     // transmits secondWaveId
      firstWaveId,      // transmits again firstWaveId
      255, 2, 136, 19,  // delay for 5000 microseconds (136 + 19 * 256 = 5000)
      255, 0,           // marks the beginning of a new wave
      thirdWaveId,      // transmits thirdWaveId
      255, 1, 30, 0,    // repeats the waves since the last beginning mark 30 times (30 + 0 * 256 = 30)
      255, 0,           // marks the beginning of a new wave
      fourthWaveId,     // transmits fourthWaveId
      255, 3            // loops forever until waveTxStop is called
    ];
    
    pigpio.waveChain(chain);
    while (pigpio.waveTxBusy()) {}
  7. Handle signal events with initialize() and terminate()

    master

    By default, pigpio initializes and terminates the C library automatically when Gpio objects are created. However, if your Node.js application uses process.on() to register signal event handlers (like SIGINT), the automatic initialization will overwrite your handlers because Linux only allows one handler per signal.

    To prevent this, you must manually manage the lifecycle:

    1. Call pigpio.initialize() before registering any process.on signal handlers.
    2. Call pigpio.terminate() inside your signal handler to ensure the library shuts down correctly.

    Note: After calling terminate(), any existing pigpio objects can no longer be used.

    const pigpio = require('pigpio');
    const Gpio = pigpio.Gpio;
    
    let led;
    let iv;
    
    // 1. Manually initialize before registering signal handlers
    pigpio.initialize(); 
    
    process.on('SIGINT', () => {
      // 2. Use terminate() inside the handler
      led.digitalWrite(0);
      pigpio.terminate(); 
      clearInterval(iv);
      console.log('Terminating...');
    });
    
    led = new Gpio(17, {mode: Gpio.OUTPUT});
    
    iv = setInterval(() => {
      led.digitalWrite(led.digitalRead() ^ 1);
    }, 1000);
  8. Install the pigpio C library prerequisite

    master

    The pigpio Node.js module requires the pigpio C library to be installed on your Raspberry Pi.

    1. Check your current version:
      pigpiod -v
    2. Requirements:
      • Raspberry Pi Zero, 1, 2, or 3: Requires pigpio V41 or higher.
      • Raspberry Pi 4: Requires pigpio V69 or higher.
    3. If the library is missing or outdated, install it using:
      sudo apt-get update
      sudo apt-get install pigpio

    Warning: Do not use the pigpiod daemon utility; the Node.js package uses the C library directly.

    sudo apt-get update
    sudo apt-get install pigpio
  9. Install the pigpio Node.js package

    master

    Once the C library is installed, you can install the Node.js wrapper via npm:

    npm install pigpio
    npm install pigpio
  10. Create and transmit a basic waveform

    master

    Waveforms are created by adding a series of pulses to the current waveform buffer and then calling waveCreate().

    1. Call waveClear() to reset the buffer.
    2. Use waveAddGeneric(pulses) to add an array of pulse objects. Each pulse object must have:
      • gpioOn: GPIO number to turn on (use 0 to not change state).
      • gpioOff: GPIO number to turn off (use 0 to not change state).
      • usDelay: Pulse length in microseconds.
    3. Call waveCreate() to get a waveId.
    4. Use waveTxSend(waveId, waveMode) to transmit the waveform.
    5. Use waveTxBusy() to poll for completion.
    6. Call waveDelete(waveId) to clean up.
    const pigpio = require('pigpio');
    const Gpio = pigpio.Gpio;
    
    const outPin = 17;
    const output = new Gpio(outPin, { mode: Gpio.OUTPUT });
      
    let waveform = [];
    
    // Example: alternating pulses
    for (let x = 0; x < 20; x++) {
      if (x % 2 == 1) {
        waveform.push({ gpioOn: outPin, gpioOff: 0, usDelay: x + 1 });
      } else {
        waveform.push({ gpioOn: 0, gpioOff: outPin, usDelay: x + 1 });
      }
    }
    
    pigpio.waveClear();
    pigpio.waveAddGeneric(waveform);
    
    let waveId = pigpio.waveCreate();
    
    if (waveId >= 0) {
      pigpio.waveTxSend(waveId, pigpio.WAVE_MODE_ONE_SHOT);
    }
    
    while (pigpio.waveTxBusy()) {}
    
    pigpio.waveDelete(waveId);
  11. Fix sound card or microphone issues caused by PWM

    master

    Using PWM (Pulse Width Modulation) can sometimes interfere with sound cards, causing audio streams to break or ALSA to throw errors.

    To resolve this, you can force the pigpio clock to use the PWM hardware by calling pigpio.configureClock(1, pigpio.CLOCK_PWM) before you instantiate any Gpio objects.

    const pigpio = require('pigpio');
    const Gpio = pigpio.Gpio;
    
    // Call configureClock before creating Gpio objects
    pigpio.configureClock(1, pigpio.CLOCK_PWM);
    
    const led = new Gpio(25, { mode: Gpio.OUTPUT });