function Gpio(gpio, direction, edge, options) { var valuePath, directionSet = false, tries = 0; if (!(this instanceof Gpio)) { return new Gpio(gpio, direction, edge, options); } if (typeof edge === 'object' && !options) { options = edge; edge = undefined; } options = options || {}; this.gpio = gpio; this.gpioPath = GPIO_ROOT_PATH + 'gpio' + this.gpio + '/'; this.opts = {}; this.opts.debounceTimeout = options.debounceTimeout || 0; this.readBuffer = new Buffer(16); this.listeners = []; valuePath = this.gpioPath + 'value'; if (!fs.existsSync(this.gpioPath)) { // The pin hasn't been exported yet so export it. fs.writeFileSync(GPIO_ROOT_PATH + 'export', this.gpio); // A hack to avoid the issue described here: // https://github.com/raspberrypi/linux/issues/553 // I don't like this solution, but it enables compatibility with older // versions of onoff, i.e., the Gpio constructor was and still is // synchronous. directionSet = false; while (!directionSet) { try { tries += 1; fs.writeFileSync(this.gpioPath + 'direction', direction); directionSet = true; } catch (e) { if (tries === 10000) { throw e; } } } if (edge) { fs.writeFileSync(this.gpioPath + 'edge', edge); } if (!!options.activeLow) { fs.writeFileSync(this.gpioPath + 'active_low', ONE); } } else { // The pin has already been exported, perhaps by onoff itself, perhaps // by quick2wire gpio-admin on the Pi, perhaps by the WiringPi gpio // utility on the Pi, or perhaps by something else. In any case, an // attempt is made to set the direction and edge to the requested // values here. If quick2wire gpio-admin was used for the export, the // user should have access to both direction and edge files. This is // important as gpio-admin sets niether direction nor edge. If the // WiringPi gpio utility was used, the user should have access to edge // file, but not the direction file. This is also ok as the WiringPi // gpio utility can set both direction and edge. If there are any // errors while attempting to perform the modifications, just keep on // truckin'. try { fs.writeFileSync(this.gpioPath + 'direction', direction); } catch (ignore) { } try { if (edge) { fs.writeFileSync(this.gpioPath + 'edge', edge); } try { fs.writeFileSync(this.gpioPath + 'active_low', !!options.activeLow ? ONE : ZERO ); } catch (ignore) { } } catch (ignore) { } } this.valueFd = fs.openSync(valuePath, 'r+'); // Cache fd for performance. // Read current value before polling to prevent unauthentic interrupts. this.readSync(); this.poller = new Epoll(pollerEventHandler.bind(this)); }
n/a
function Gpio(gpio, direction, edge, options) { var valuePath, directionSet = false, tries = 0; if (!(this instanceof Gpio)) { return new Gpio(gpio, direction, edge, options); } if (typeof edge === 'object' && !options) { options = edge; edge = undefined; } options = options || {}; this.gpio = gpio; this.gpioPath = GPIO_ROOT_PATH + 'gpio' + this.gpio + '/'; this.opts = {}; this.opts.debounceTimeout = options.debounceTimeout || 0; this.readBuffer = new Buffer(16); this.listeners = []; valuePath = this.gpioPath + 'value'; if (!fs.existsSync(this.gpioPath)) { // The pin hasn't been exported yet so export it. fs.writeFileSync(GPIO_ROOT_PATH + 'export', this.gpio); // A hack to avoid the issue described here: // https://github.com/raspberrypi/linux/issues/553 // I don't like this solution, but it enables compatibility with older // versions of onoff, i.e., the Gpio constructor was and still is // synchronous. directionSet = false; while (!directionSet) { try { tries += 1; fs.writeFileSync(this.gpioPath + 'direction', direction); directionSet = true; } catch (e) { if (tries === 10000) { throw e; } } } if (edge) { fs.writeFileSync(this.gpioPath + 'edge', edge); } if (!!options.activeLow) { fs.writeFileSync(this.gpioPath + 'active_low', ONE); } } else { // The pin has already been exported, perhaps by onoff itself, perhaps // by quick2wire gpio-admin on the Pi, perhaps by the WiringPi gpio // utility on the Pi, or perhaps by something else. In any case, an // attempt is made to set the direction and edge to the requested // values here. If quick2wire gpio-admin was used for the export, the // user should have access to both direction and edge files. This is // important as gpio-admin sets niether direction nor edge. If the // WiringPi gpio utility was used, the user should have access to edge // file, but not the direction file. This is also ok as the WiringPi // gpio utility can set both direction and edge. If there are any // errors while attempting to perform the modifications, just keep on // truckin'. try { fs.writeFileSync(this.gpioPath + 'direction', direction); } catch (ignore) { } try { if (edge) { fs.writeFileSync(this.gpioPath + 'edge', edge); } try { fs.writeFileSync(this.gpioPath + 'active_low', !!options.activeLow ? ONE : ZERO ); } catch (ignore) { } } catch (ignore) { } } this.valueFd = fs.openSync(valuePath, 'r+'); // Cache fd for performance. // Read current value before polling to prevent unauthentic interrupts. this.readSync(); this.poller = new Epoll(pollerEventHandler.bind(this)); }
n/a
activeLow = function () { return fs.readFileSync( this.gpioPath + 'active_low')[0] === ONE[0] ? true : false; }
...
* resistor and GPIO #8 is wired to the other end of the resistor.
*/
var Gpio = require('../onoff').Gpio,
assert = require('assert'),
input = new Gpio(7, 'in'),
output = new Gpio(8, 'out', {activeLow: true});
assert(input.activeLow() === false);
assert(output.activeLow() === true);
output.writeSync(0);
assert(input.readSync() === 1);
output.writeSync(1);
assert(input.readSync() === 0);
output.setActiveLow(false);
...
direction = function () { return fs.readFileSync(this.gpioPath + 'direction').toString().trim(); }
...
"use strict";
var Gpio = require('../onoff').Gpio,
assert = require('assert'),
input = new Gpio(4, 'in', 'rising');
assert(input.direction() === 'in');
assert(input.edge() === 'rising');
input.unexport();
console.log('ok - ' + __filename);
...
edge = function () { return fs.readFileSync(this.gpioPath + 'edge').toString().trim(); }
...
"use strict";
var Gpio = require('../onoff').Gpio,
assert = require('assert'),
input = new Gpio(4, 'in', 'rising');
assert(input.direction() === 'in');
assert(input.edge() === 'rising');
input.unexport();
console.log('ok - ' + __filename);
...
options = function () { return this.opts; }
...
button = new Gpio(4, 'in', 'rising', {
debounceTimeout : 250
}),
count = 0;
assert(button.direction() === 'in');
assert(button.edge() === 'rising');
assert(button.options().debounceTimeout === 250);
console.info('Please press button attached to GPIO #4 5 times...');
button.watch(function (err, value) {
if (err) {
throw err;
}
...
read = function (callback) { fs.read(this.valueFd, this.readBuffer, 0, 1, 0, function (err, bytes, buf) { if (typeof callback === 'function') { if (err) { return callback(err); } callback(null, buf[0] === ONE[0] ? 1 : 0); } }); }
...
/**
* Read GPIO value asynchronously.
*
* [callback: (err: error, value: number) => {}] // Optional callback
*/
Gpio.prototype.read = function (callback) {
fs.read(this.valueFd, this.readBuffer, 0, 1, 0, function (err, bytes, buf) {
if (typeof callback === 'function') {
if (err) {
return callback(err);
}
callback(null, buf[0] === ONE[0] ? 1 : 0);
}
...
readSync = function () { fs.readSync(this.valueFd, this.readBuffer, 0, 1, 0); return this.readBuffer[0] === ONE[0] ? 1 : 0; }
...
var GPIO_ROOT_PATH = '/sys/class/gpio/',
ZERO = new Buffer('0'),
ONE = new Buffer('1');
exports.version = '1.1.1';
function pollerEventHandler(err, fd, events) {
var value = this.readSync(),
callbacks = this.listeners.slice(0);
if (this.opts.debounceTimeout > 0) {
setTimeout(function () {
if (this.listeners.length > 0) {
// Read current value before polling to prevent unauthentic interrupts.
this.readSync();
...
setActiveLow = function (invert) { fs.writeFileSync(this.gpioPath + 'active_low', !!invert ? ONE : ZERO); }
...
assert(input.activeLow() === false);
assert(output.activeLow() === true);
output.writeSync(0);
assert(input.readSync() === 1);
output.writeSync(1);
assert(input.readSync() === 0);
output.setActiveLow(false);
assert(input.activeLow() === false);
assert(output.activeLow() === false);
output.writeSync(0);
assert(input.readSync() === 0);
output.writeSync(1);
assert(input.readSync() === 1);
...
setDirection = function (direction) { fs.writeFileSync(this.gpioPath + 'direction', direction); }
n/a
setEdge = function (edge) { fs.writeFileSync(this.gpioPath + 'edge', edge); }
n/a
unexport = function () { this.unwatchAll(); fs.closeSync(this.valueFd); try { fs.writeFileSync(GPIO_ROOT_PATH + 'unexport', this.gpio); } catch (ignore) { // Flow of control always arrives here when cape_universal is enabled on // the bbb. } }
...
throw err;
}
led.writeSync(value);
});
process.on('SIGINT', function () {
led.unexport();
button.unexport();
});
```
## How does it work?
Internally onoff uses sysfs files located at /sys/class/gpio to access GPIOs
...
unwatch = function (callback) { if (this.listeners.length > 0) { if (typeof callback !== 'function') { this.listeners = []; } else { this.listeners = this.listeners.filter(function (listener) { return callback !== listener; }); } if (this.listeners.length === 0) { this.poller.remove(this.valueFd); } } }
...
}
};
/**
* Remove all watchers for the GPIO.
*/
Gpio.prototype.unwatchAll = function () {
this.unwatch();
};
/**
* Get GPIO direction.
*
* Returns - string // 'in', or 'out'
*/
...
unwatchAll = function () { this.unwatch(); }
...
};
/**
* Reverse the effect of exporting the GPIO to userspace. The Gpio object
* should not be used after calling this method.
*/
Gpio.prototype.unexport = function () {
this.unwatchAll();
fs.closeSync(this.valueFd);
try {
fs.writeFileSync(GPIO_ROOT_PATH + 'unexport', this.gpio);
} catch (ignore) {
// Flow of control always arrives here when cape_universal is enabled on
// the bbb.
}
...
watch = function (callback) { var events; this.listeners.push(callback); if (this.listeners.length === 1) { events = Epoll.EPOLLPRI; if (this.opts.debounceTimeout > 0) { events |= Epoll.EPOLLONESHOT; } this.poller.add(this.valueFd, events); } }
...
should turn off. This can be achieved with the following code:
```js
var Gpio = require('onoff').Gpio,
led = new Gpio(14, 'out'),
button = new Gpio(4, 'in', 'both');
button.watch(function(err, value) {
led.writeSync(value);
});
```
Here two Gpio objects are being created. One called led for the LED on GPIO #14
which is an output, and one called button for the momentary push button on
GPIO #4 which is an input. In addition to specifying that the button is an
...
write = function (value, callback) { var writeBuffer = value === 1 ? ONE : ZERO; fs.write(this.valueFd, writeBuffer, 0, writeBuffer.length, 0, callback); }
...
* Write GPIO value asynchronously.
*
* value: number // 0 or 1
* [callback: (err: error) => {}] // Optional callback
*/
Gpio.prototype.write = function (value, callback) {
var writeBuffer = value === 1 ? ONE : ZERO;
fs.write(this.valueFd, writeBuffer, 0, writeBuffer.length, 0, callback);
};
/**
* Write GPIO value synchronously.
*
* value: number // 0 or 1
*/
...
writeSync = function (value) { var writeBuffer = value === 1 ? ONE : ZERO; fs.writeSync(this.valueFd, writeBuffer, 0, writeBuffer.length, 0); }
...
/**
* Write GPIO value synchronously.
*
* value: number // 0 or 1
*/
Gpio.prototype.writeSync = function (value) {
var writeBuffer = value === 1 ? ONE : ZERO;
fs.writeSync(this.valueFd, writeBuffer, 0, writeBuffer.length, 0);
};
/**
* Watch for hardware interrupts on the GPIO. Inputs and outputs can be
* watched. The edge argument that was passed to the constructor determines
* which hardware interrupts are watcher for.
*
...