- Notifications
You must be signed in to change notification settings - Fork0
GPIO access and interrupt detection with Node.js
License
vorno-crole/onoff
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
GPIO access and interrupt detection withNode.js on Linux boards like theRaspberry Pi or BeagleBone.
onoff supports Node.js versions 10, 12, 14, 15 and 16.
- Installation
- Usage
- API
- How Does onoff Work?
- Configuring Pullup and Pulldown Resistors
- Benchmarks
- Related Packages
- Additional Information
npm install onoff
Note that although it's possible to install onoff on non-Linux systems thefunctionality offered by onoff is only available on Linux systems.
Assume that there's an LED connected to GPIO17 and a momentary push buttonconnected to GPIO4.
When the button is pressed the LED should turn on, when it's released the LEDshould turn off. This can be achieved with the following code:
constGpio=require('onoff').Gpio;constled=newGpio(17,'out');constbutton=newGpio(4,'in','both');button.watch((err,value)=>led.writeSync(value));
Here two Gpio objects are being created. One called led for the LED connectedto GPIO17 which is an output, and one called button for the momentary pushbutton connected to GPIO4 which is an input. In addition to specifying thatthe button is an input, the constructors optional third argument is used tospecify that 'both' rising and falling interrupt edges should be configuredfor the button GPIO as both button presses and releases should be handled.
After everything has been setup correctly, the buttons watch method is used tospecify a callback function to execute every time the button is pressed orreleased. The value argument passed to the callback function represents thestate of the button which will be 1 for pressed and 0 for released. This valueis used by the callback to turn the LED on or off using its writeSync method.
When the above program is running it can be terminated with ctrl-c. However,it doesn't free its resources. It also ignores the err argument passed tothe callback. Here's a slightly modified variant of the program that handlesctrl-c gracefully and bails out on error. The resources used by the led andbutton Gpio objects are released by invoking their unexport method.
constGpio=require('onoff').Gpio;constled=newGpio(17,'out');constbutton=newGpio(4,'in','both');button.watch((err,value)=>{if(err){throwerr;}led.writeSync(value);});process.on('SIGINT',_=>{led.unexport();button.unexport();});
When working with buttons there will often be button bounce issues whichresult in the hardware thinking that a button was pressed several timesalthough it was only pressed once. onoff provides a software debouncingsolution for resolving bounce issues.
Assume again that there's an LED connected to GPIO17 and a momentary pushbutton connected to GPIO4.
When the button is pressed the LED should toggle its state. This is a typicalexample of a situation where there will be button bounce issues. The issue canbe resolved by using the debounceTimeout option when creating the Gpio objectfor the button. In the below program the debounceTimeout is set to 10milliseconds. This delays invoking the watch callback for the button while thebutton is bouncing. The watch callback will not be invoked until the buttonstops bouncing and has been in a stable state for 10 milliseconds.
constGpio=require('onoff').Gpio;constled=newGpio(17,'out');constbutton=newGpio(4,'in','rising',{debounceTimeout:10});button.watch((err,value)=>{if(err){throwerr;}led.writeSync(led.readSync()^1);});process.on('SIGINT',_=>{led.unexport();button.unexport();});
Blink an LED connected to GPIO17 for 5 seconds using the synchronous readSyncand writeSync methods.
constGpio=require('../onoff').Gpio;// Gpio classconstled=newGpio(17,'out');// Export GPIO17 as an output// Toggle the state of the LED connected to GPIO17 every 200msconstiv=setInterval(_=>led.writeSync(led.readSync()^1),200);// Stop blinking the LED after 5 secondssetTimeout(_=>{clearInterval(iv);// Stop blinkingled.unexport();// Unexport GPIO and free resources},5000);
Blink an LED connected to GPIO17 for 5 seconds using the asynchronous read andwrite methods and completion callbacks.
constGpio=require('../onoff').Gpio;// Gpio classconstled=newGpio(17,'out');// Export GPIO17 as an outputletstopBlinking=false;// Toggle the state of the LED connected to GPIO17 every 200msconstblinkLed=_=>{if(stopBlinking){returnled.unexport();}led.read((err,value)=>{// Asynchronous readif(err){throwerr;}led.write(value^1,err=>{// Asynchronous writeif(err){throwerr;}});});setTimeout(blinkLed,200);};blinkLed();// Stop blinking the LED after 5 secondssetTimeout(_=>stopBlinking=true,5000);
Blink an LED connected to GPIO17 for 5 seconds using the asynchronous read andwrite methods and Promises.
constGpio=require('../onoff').Gpio;// Gpio classconstled=newGpio(17,'out');// Export GPIO17 as an outputletstopBlinking=false;// Toggle the state of the LED connected to GPIO17 every 200msconstblinkLed=_=>{if(stopBlinking){returnled.unexport();}led.read().then(value=>led.write(value^1)).then(_=>setTimeout(blinkLed,200)).catch(err=>console.log(err));};blinkLed();// Stop blinking the LED after 5 secondssetTimeout(_=>stopBlinking=true,5000);
Sometimes it may be necessary to determine if the current system supportsGPIOs programmatically and mock functionality if it doesn't.Gpio.accessible
can be used to achieve this.
constGpio=require('onoff').Gpio;constuseLed=(led,value)=>led.writeSync(value);letled;if(Gpio.accessible){led=newGpio(17,'out');// more real code here}else{led={writeSync:value=>{console.log('virtual led now uses value: '+value);}};}useLed(led,1);
- Gpio(gpio, direction [, edge] [, options]) - Constructor
- read([callback]) - Read GPIO value asynchronously
- readSync() - Read GPIO value synchronously
- write(value[, callback]) - Write GPIO value asynchronously
- writeSync(value) - Write GPIO value synchronously
- watch(callback) - Watch for hardware interrupts on the GPIO
- unwatch([callback]) - Stop watching for hardware interrupts on the GPIO
- unwatchAll() - Remove all watchers for the GPIO
- direction() - Get GPIO direction
- setDirection(direction) - Set GPIO direction
- edge() - Get GPIO interrupt generating edge
- setEdge(edge) - Set GPIO interrupt generating edge
- activeLow() - Get GPIO activeLow setting
- setActiveLow(invert) - Set GPIO activeLow setting
- unexport() - Reverse the effect of exporting the GPIO to userspace
- static accessible - Determine whether or not GPIO access is possible
- HIGH / LOW - Constants used when reading or writing a GPIO value
- gpio - An unsigned integer specifying the GPIO number.
- direction - A string specifying whether the GPIO should be configured as aninput or output. The valid values are: 'in', 'out', 'high', and 'low'. If 'out'is specified the GPIO will be configured as an output and the value of the GPIOwill be set to 0. 'high' and 'low' are variants of 'out' that configure theGPIO as an output with an initial level of 1 or 0 respectively.
- [edge] - An optional string specifying the interrupt generating edge oredges for an input GPIO. The valid values are: 'none', 'rising', 'falling' or'both'. The default value is 'none' indicating that the GPIO will not generateinterrupts. Whether or not interrupts are supported by an input GPIO is GPIOspecific. If interrupts are not supported by a GPIO the edge argument shouldnot be specified. The edge argument is ignored for output GPIOs.
- [options] - An optional options object.
Configures the GPIO based on the passed arguments and returns a new Gpioobject that can be used to access the GPIO.
The following options are supported:
- debounceTimeout - An unsigned integer specifying a millisecond delay. Delaysinvoking the watch callback for an interrupt generating input GPIO while theinput is bouncing. The watch callback will not be invoked until the inputstops bouncing and has been in a stable state for debounceTimeoutmilliseconds. Optional, if unspecified the input GPIO will not be debounced.
- activeLow - A boolean value specifying whether the values read from orwritten to the GPIO should be inverted. The interrupt generating edge for theGPIO also follow this this setting. The valid values for activeLow are trueand false. Setting activeLow to true inverts. Optional, the default value isfalse.
- reconfigureDirection - A boolean value specifying whether the direction forthe GPIO should be reconfigured even though the direction is alreadyconfigured correctly. When an application starts, the direction of a GPIO usedby that application may already be configured correctly, for example, from aprevious run of the application. Reconfiguring the direction of that GPIO canresult in unwanted side effects. For example, if a GPIO is already configuredas an output and it is reconfigured as an output by passing 'out' to theconstructor, the value of that output will be set to 0. In some applicationsthis is not desirable and the value of the output should not be modified. ThereconfigureDirection option can help here. If reconfigureDirection is set tofalse the direction of a GPIO that is already correctly configured will not bereconfigured. Optional, the default value is true.
GPIOs on Linux are identified by unsigned integers. These are the numbers thatshould be passed to the onoff Gpio constructor when exporting GPIOs touserspace. For example, pin 11 on the Raspberry Pi expansion headercorresponds to GPIO17 in Raspbian Linux. 17 is therefore the number to passto the onoff Gpio constructor when using pin 11 on the expansion header.
- [callback] - An optional completion callback that gets two arguments (err,value), where err is reserved for an Error object and value is the number 0or 1 and represents the state of the GPIO.
Read GPIO value asynchronously. If no completion callback is specified readreturns a Promise which resolves to the value of the GPIO on success or rejectswith an Error object on failure.
Note that most systems support readback of GPIOs configured as outputs. Theread method can therefore be invoked for any GPIO, irrespective of whether itwas configured as an input or an output. The Raspberry Pi and BeagleBone areexamples of such systems.
Read GPIO value synchronously. Returns the number 0 or 1 to represent thestate of the GPIO.
Note that most systems support readback of GPIOs configured as outputs. ThereadSync method can therefore be invoked for any GPIO, irrespective of whetherit was configured as an input or an output. The Raspberry Pi and BeagleBoneare examples of such systems.
- value - The number 0 or 1.
- [callback] - An optional completion callback that gets one argument (err),where err is reserved for an error object.
Write GPIO value asynchronously. If no completion callback is specified writereturns a Promise that resolves with no value on success or rejects with anError object on failure.
Note that on most systems invoking write for a GPIO configured as an inputwill result in an EPERM error indicating that the operation is not permitted.The Raspberry Pi and BeagleBone are examples of such systems.
- value - The number 0 or 1.
Write GPIO value synchronously.
Note that on most systems invoking writeSync for a GPIO configured as an inputwill result in an EPERM error indicating that the operation is not permitted.The Raspberry Pi and BeagleBone are examples of such systems.
- callback - A callback that gets two arguments (err, value), where err isreserved for an error object and value is the number 0 or 1 and represents thestate of the GPIO. The value can also be used to determine whether theinterrupt occurred on a rising or falling edge. A value of 0 implies a fallingedge interrupt and a value of 1 implies a rising edge interrupt.
Watch for hardware interrupts on the GPIO. The edge argument that was passedto the constructor determines which hardware interrupts to watch for.
- [callback] - The callback to remove.
Stop watching for hardware interrupts on the GPIO. If callback is specified,only that particular callback is removed. Otherwise all callbacks are removed.
Remove all hardware interrupt watchers for the GPIO.
Returns the string 'in' or 'out' indicating whether the GPIO is an input oroutput.
- direction - A string specifying whether the GPIO should be configured as aninput or output. The valid values are 'in', 'out', 'high', and 'low'. If 'out'is specified the GPIO will be configured as an output and the value of the GPIOwill be set to 0. 'high' and 'low' are variants of 'out' that configure theGPIO as an output with an initial level of 1 or 0 respectively.
Set GPIO direction.
Returns the string 'none', 'falling', 'rising', or 'both' indicating theinterrupt generating edge or edges for the GPIO. Whether or not interrupts aresupported by an input GPIO is GPIO specific. If interrupts are not supportedthe edge method should not be used. Interrupts are not supported by outputGPIOs.
- edge - A string specifying the interrupt generating edge or edges for aninput GPIO. The valid values are: 'none', 'rising', 'falling' or 'both'.Whether or not interrupts are supported by an input GPIO is GPIO specific. Ifinterrupts are not supported the setEdge method should not be used. Interruptsare not supported by output GPIOs.
Set GPIO interrupt generating edge.
Returns true or false indicating whether or not the values read from or writtento the GPIO are inverted.
- invert - A boolean value specifying whether the values read from or writtento the GPIO should be inverted. The interrupt generating edge for the GPIO alsofollow this this setting. The valid values for invert are true and false.Setting activeLow to true inverts.
Set GPIO activeLow setting.
Reverse the effect of exporting the GPIO to userspace. A Gpio object should notbe used after invoking its unexport method.
Determine whether or not GPIO access is possible. true if the current processhas the permissions required to export GPIOs to userspace. false otherwise.Loosely speaking, if this property is true it should be possible for thecurrent process to create Gpio objects.
It is notable that while this property may be false indicating that thecurrent process does not have the permissions required to export GPIOs touserspace, existing exported GPIOs may still be accessible.
This property is useful for mocking functionality on computers used fordevelopment that do not provide access to GPIOs.
This is a static property and should be accessed asGpio.accessible
.
Constants used when reading or writing a GPIO value. Gpio.HIGH and Gpio.LOWcan be used in place of the numeric constants 1 and 0.
Internally onoff uses sysfs files located at /sys/class/gpio to access GPIOsand theepoll package to detect hardwareinterrupts. The Linux GPIO sysfs interface for userspace is documentedhere.It's a relatively simple interface which can be used to ask the Linux kernelto export control of a GPIO to userspace. After control of a GPIO has beenexported to userspace, the GPIO can be configured as an input or output.Thereafter, the state of an input can be read, and the state of an output canbe written. Some systems will also allow the state of a output to be read.The GPIO sysfs interface can also be used for interrupt detection. onoff candetect several thousand interrupts per second on both the BeagleBone and theRaspberry Pi.
As mentioned in sectionHow Does onoff Work the sysfsinterface is used to access GPIOs. The sysfs interface doesn't offer supportfor configuring pullup and pulldown resistors on GPIOs.
There are however many platform specific mechanisms for configuring pullup andpulldown resistors that are compatible with onoff. onoff itself doesn't usethese mechanisms as one of the goals of onoff is to be platform independent.
Here we'll take a look at two mechanisms available on the Raspberry Pi forconfiguring pullup and pulldown resistors.
The first point to be aware of is that most GPIOs on a Raspberry Pi haveeither their pullup or pulldown resistor activated by default. The defaultscan be seen in Table 6-31 on pages 102 and 103 of theBCM2835 ARM Peripheralsdocumentation.
On Raspbian 2018-04-18 or later thegpio
configuration command can be usedin/boot/config.txt
to configure pullup and pulldown resistors. Furtherinformation is available atNew "gpio" config command.
Device tree overlays can also be used to configure pullup and pulldownresistors. The Wiki pageEnabling Pullup and Pulldown Resistors on The Raspberry Pidescribes this mechanism in more detail.
Three of the onoff tests are used to monitor performance.
- performance-async.js - determine max. no. of write ops per seconds
- performance-sync.js - determine max. no. of writeSync ops per second
- performance-interrupt.js - determine max. no. of interrupts per second
The results of these tests are shown in the following tables.
Raspberry Pi 4 B, 1.5GHz, Raspberry Pi OS (March 4th 2021, Buster 10.8)
node | onoff | kernel | write / sec | writeSync / sec | interrupts / sec |
---|---|---|---|---|---|
v16.0.0 | v6.0.3 | 5.10.17-v7l+ | 25124 | 280417 | 20240 |
v15.14.0 | v6.0.3 | 5.10.17-v7l+ | 24055 | 271149 | 20488 |
v14.16.1 | v6.0.3 | 5.10.17-v7l+ | 21669 | 254705 | 19703 |
v12.22.1 | v6.0.3 | 5.10.17-v7l+ | 22618 | 318417 | 21122 |
v10.24.1 | v6.0.3 | 5.10.17-v7l+ | 22405 | 329927 | 19583 |
Raspberry Pi 3 B, 1.2GHz, Raspbian Buster 10.1
node | onoff | kernel | write / sec | writeSync / sec | interrupts / sec |
---|---|---|---|---|---|
v12.14.0 | v5.0.0 | 4.19.75-v7l+ | 21670 | 207222 | 18328 |
v10.18.0 | v5.0.0 | 4.19.75-v7l+ | 23661 | 225758 | 20741 |
Raspberry Pi 2 B, 900MHz, Raspbian Buster 10.1
node | onoff | kernel | write / sec | writeSync / sec | interrupts / sec |
---|---|---|---|---|---|
v12.14.0 | v5.0.0 | 4.19.75-v7l+ | 10769 | 113107 | 10373 |
v10.18.0 | v5.0.0 | 4.19.75-v7l+ | 11843 | 129086 | 10536 |
Raspberry Pi 1 B, 700MHz, Raspbian Buster 10.1
node | onoff | kernel | write / sec | writeSync / sec | interrupts / sec |
---|---|---|---|---|---|
v12.14.0 | v5.0.0 | 4.19.75+ | 2316 | 26696 | 2112 |
v10.18.0 | v5.0.0 | 4.19.75+ | 2613 | 33129 | 2225 |
BeagleBone Black, 1GHz, Debian Buster 10.2
node | onoff | kernel | write / sec | writeSync / sec | interrupts / sec |
---|---|---|---|---|---|
v12.14.0 | v5.0.0 | 4.19.79-ti-r30 | 6855 | 70535 | 5911 |
v10.18.0 | v5.0.0 | 4.19.79-ti-r30 | 7564 | 79133 | 5920 |
BeagleBone, 720MHz, Debian Buster 10.2
node | onoff | kernel | write / sec | writeSync / sec | interrupts / sec |
---|---|---|---|---|---|
v12.14.0 | v5.0.0 | 4.19.79-ti-r30 | 5013 | 49741 | 4297 |
v10.18.0 | v5.0.0 | 4.19.79-ti-r30 | 5400 | 57157 | 4371 |
Here are a few links to other hardware specific Node.js packages that may beof interest.
- pigpio - Fast GPIO, PWM, servo control, state change notification and interrupt handling on the Raspberry Pi
- i2c-bus - I2C serial bus access
- spi-device - SPI serial bus access
- mcp-spi-adc - Analog to digital conversion with the MCP3002/4/8, MCP3202/4/8 and MCP3304
onoff was tested on the following platforms:
- Raspberry Pi 1, 2, 3 and 4
- Raspbian or Raspberry Pi OS
- BeagleBone, BeagleBone Black and PocketBeagle
- Debian
The suitability of onoff for a particular Linux board is highly dependent onhow GPIO interfaces are made available on that board. TheGPIO interfacesdocumentation describes GPIO access conventions rather than standards that mustbe followed so GPIO can vary from platform to platform. For example, onoffrelies on sysfs files located at /sys/class/gpio being available. However,these sysfs files for userspace GPIO are optional and may not be available on aparticular platform.