Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

rpi-gpio

Package Overview
Dependencies
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rpi-gpio - npm Package Compare versions

Comparing version 0.5.2 to 0.6.0

5

package.json
{
"name": "rpi-gpio",
"version": "0.5.2",
"version": "0.6.0",
"description": "Control Raspberry Pi GPIO pins with node.js",

@@ -33,4 +33,5 @@ "main": "rpi-gpio.js",

"async": "~0.8.0",
"debug": "~0.8.1"
"debug": "~0.8.1",
"epoll": "^0.1.13"
}
}

24

README.md

@@ -10,3 +10,3 @@ rpi-gpio.js

## Setup
See this guide on how to get [node.js running on Raspberry Pi](http://joshondesign.com/2013/10/23/noderpi).
See this guide on how to get [node.js running on Raspberry Pi](https://learn.adafruit.com/node-embedded-development/installing-node-dot-js).

@@ -31,7 +31,9 @@ This module can then be installed with npm:

#### setup(channel [, direction], callback)
#### setup(channel [, direction, edge], callback)
Sets up a channel for read or write. Must be done before the channel can be used.
* channel: Reference to the pin in the current mode's schema.
* direction: The pin direction, pass either DIR_IN for read mode or DIR_OUT for write mode. Defaults to DIR_OUT.
* callback: Provides Error as the first argument if an error occured.
* edge: Interrupt generating GPIO chip setting, pass in EDGE_NONE for no interrupts, EDGE_RISING for interrupts on rising values, EDGE_FALLING for interrupts on falling values or EDGE_BOTH for all interrupts.
Defaults to EDGE_NONE.
* callback: Provides Error as the first argument if an error occurred.

@@ -53,6 +55,2 @@ #### read(channel, callback)

#### setPollFrequency(value)
Sets the poll frequency for checking whether pin values have changed.
* value: The polling frequency in milliseconds, defaults to 5007.
#### input()

@@ -122,3 +120,3 @@ Alias of read().

});
gpio.setup(7, gpio.DIR_IN);
gpio.setup(7, gpio.DIR_IN, gpio.EDGE_BOTH);
```

@@ -145,3 +143,2 @@

console.log('All pins unexported');
return process.exit(0);
});

@@ -162,5 +159,2 @@ }

gpio.on('change', function(channel, value) {
console.log('Channel ' + channel + ' value is now ' + value);
});
gpio.setup(pin, gpio.DIR_OUT, on);

@@ -172,3 +166,2 @@

console.log('Closed pins, now exit');
return process.exit(0);
});

@@ -197,6 +190,2 @@ return;

gpio.on('change', function(channel, value) {
console.log('Channel ' + channel + ' value is now ' + value);
});
async.parallel([

@@ -242,3 +231,2 @@ function(callback) {

console.log('Closed pins, now exit');
return process.exit(0);
});

@@ -245,0 +233,0 @@ }, 500);

@@ -6,4 +6,6 @@ var fs = require('fs');

var debug = require('debug')('rpi-gpio');
var Epoll = require('epoll').Epoll;
var PATH = '/sys/class/gpio';
var POLLERS = [];
var PINS = {

@@ -86,6 +88,5 @@ v1: {

var currentPins;
var exportedInputPins = {};
var exportedInputPins = {};
var exportedOutputPins = {};
var getPinForCurrentMode = getPinRpi;
var pollFrequency = 5007;

@@ -97,2 +98,7 @@ this.DIR_IN = 'in';

this.EDGE_NONE = 'none';
this.EDGE_RISING = 'rising';
this.EDGE_FALLING = 'falling';
this.EDGE_BOTH = 'both';
/**

@@ -116,13 +122,2 @@ * Set pin reference mode. Defaults to 'mode_rpi'.

/**
* Set a custom polling frequency for watching pin changes
*
* @param {number} value The frequency to poll at, in milliseconds
*/
this.setPollFrequency = function(value) {
if (typeof value === 'number') {
pollFrequency = value;
}
};
/**
* Setup a channel for use as an input or output

@@ -132,11 +127,17 @@ *

* @param {string} direction The pin direction, either 'in' or 'out'
* @param edge edge Informs the GPIO chip if it needs to generate interrupts. Either 'none', 'rising', 'falling' or 'both'. Defaults to 'none'
* @param {function} onSetup Optional callback
*/
this.setup = function(channel, direction, onSetup /*err*/) {
this.setup = function(channel, direction, edge, onSetup /*err*/) {
if (arguments.length === 2 && typeof direction == 'function') {
onSetup = direction;
direction = this.DIR_OUT;
edge = this.EDGE_NONE;
} else if (arguments.length === 3 && typeof edge == 'function') {
onSetup = edge;
edge = this.EDGE_NONE;
}
direction = direction || this.DIR_OUT;
edge = edge || this.EDGE_NONE;
onSetup = onSetup || function() {};

@@ -156,2 +157,8 @@

if (edge !== this.EDGE_NONE && edge !== this.EDGE_RISING && edge !== this.EDGE_FALLING && edge !== this.EDGE_BOTH) {
return process.nextTick(function() {
onSetup(new Error('Cannot set invalid edge'));
});
}
var pinForSetup;

@@ -178,4 +185,6 @@ async.waterfall([

function(next) {
setEdge(pinForSetup, edge, next);
},
function(next) {
this.emit('export', channel);
createListener.call(this, channel, pinForSetup);

@@ -189,2 +198,5 @@ if (direction === this.DIR_IN) {

setDirection(pinForSetup, direction, next);
}.bind(this),
function(next) {
this.listen(channel, next);
}.bind(this)

@@ -201,3 +213,3 @@ ], onSetup);

*/
this.write = this.output = function(channel, value, cb /*err*/ ) {
this.write = this.output = function(channel, value, cb /*err*/) {
var pin = getPinForCurrentMode(channel);

@@ -219,3 +231,3 @@

value = (!!value && value !== '0') ? '1' : '0';
fs.writeFile(PATH + '/gpio' + pin + '/value', value, cb || function () {});
fs.writeFile(PATH + '/gpio' + pin + '/value', value, cb || function() {});
};

@@ -245,2 +257,58 @@

/**
* Listen for interrupts on a channel
*
* @param {number} channel The channel to watch
* @param {function} cb Callback which receives the channel's err
*/
this.listen = function(channel, cb /*err*/) {
var _this = this;
var pin = getPinForCurrentMode(channel);
if (!exportedInputPins[pin] && !exportedOutputPins[pin]) {
return process.nextTick(function() {
cb(new Error('Pin has not been exported'));
});
}
POLLERS.forEach(function(map) {
if (map.pin == pin) {
return process.nextTick(function() {
cb(new Error('Already watching that pin!'));
});
}
});
createListener(channel, pin, function(readChannel) {
_this.read(readChannel, function(err, value) {
debug(
'failed to read value after a change on channel %d',
readChannel
);
_this.emit('change', readChannel, value);
});
});
return cb(null)
};
/**
* Stop listening for interrupts on a channel
*
* @param {number} channel The channel to stop watching
* @param {function} cb Callback which receives the channel's err
*/
this.stopListening = function(channel, cb /*err*/) {
pin = getPinForCurrentMode(channel);
cb = cb || function() {};
if (!exportedInputPins[pin] && !exportedOutputPins[pin]) {
return process.nextTick(function() {
cb(new Error('Pin has not been exported'));
});
}
removeListener(pin, cb)
};
/**
* Unexport any pins setup by this module

@@ -251,2 +319,3 @@ *

this.destroy = function(cb) {
var _this = this;
var tasks = Object.keys(exportedOutputPins)

@@ -256,2 +325,3 @@ .concat(Object.keys(exportedInputPins))

return function(done) {
removeListener(pin, function() { })
unexportPin(pin, done);

@@ -269,3 +339,3 @@ }

exportedOutputPins = {};
exportedInputPins = {};
exportedInputPins = {};
this.removeAllListeners();

@@ -275,3 +345,3 @@

getPinForCurrentMode = getPinRpi;
pollFrequency = 5007;
POLLERS = []
};

@@ -345,25 +415,12 @@

};
function createListener(channel, pin) {
debug('listen for pin %d', pin);
var Gpio = this;
fs.watchFile(
PATH + '/gpio' + pin + '/value',
{ persistent: true, interval: pollFrequency },
function(current, previous) {
if (current.mtime > previous.mtime) {
Gpio.read(channel, function(err, value) {
debug(
'failed to read value after a change on channel %d',
channel
);
Gpio.emit('change', channel, value);
});
}
}
);
};
}
util.inherits(Gpio, EventEmitter);
function setEdge(pin, edge, cb) {
debug('set edge %s on pin %d', edge.toUpperCase(), pin);
fs.writeFile(PATH + '/gpio' + pin + '/edge', edge, function(err) {
if (cb) return cb(err);
});
}
function setDirection(pin, direction, cb) {

@@ -385,3 +442,2 @@ debug('set direction %s on pin %d', direction.toUpperCase(), pin);

debug('unexport pin %d', pin);
fs.unwatchFile(PATH + '/gpio' + pin + '/value');
fs.writeFile(PATH + '/unexport', pin, function(err) {

@@ -398,2 +454,31 @@ if (cb) return cb(err);

function createListener(channel, pin, cb) {
debug('listen for pin %d', pin);
var fd = fs.openSync(PATH + '/gpio' + pin + '/value', 'r+');
var poller = new Epoll(function(err, fd, events) {
clearInterrupt(fd);
cb(channel);
});
clearInterrupt(fd);
poller.add(fd, Epoll.EPOLLPRI);
POLLERS.push({pin: pin, poller: poller, fd: fd});
}
function removeListener(pin, cb) {
POLLERS.forEach(function(map, index) {
if (map.pin == pin) {
map.poller.remove(map.fd).close();
POLLERS.splice(index, 1);
return cb(null);
}
});
}
function clearInterrupt(fd) {
fs.readSync(fd, new Buffer(1), 0, 1, 0);
}
module.exports = new Gpio;

@@ -5,4 +5,25 @@ var assert = require('assert');

var sinon = require('sinon');
var gpio = require('../rpi-gpio.js');
var sandbox;
// Store current listeners
var listeners = []
// Stub epoll module
epoll = {}
require('epoll').Epoll = function(callback) {
callback(null, 'fakeFd2')
var listener = {
add: sandbox.spy(),
remove: sandbox.stub().returnsThis(),
close: sandbox.stub()
}
listeners.push(listener)
return listener
}
// Only load module after Epoll is stubbed
var gpio = require('../rpi-gpio.js');
var PATH = '/sys/class/gpio';

@@ -17,21 +38,20 @@

before(function() {
sinon.stub(fs, 'writeFile').yieldsAsync();
sinon.stub(fs, 'exists').yieldsAsync(false);
sinon.stub(fs, 'watchFile').yieldsAsync({ mtime: 2000 }, { mtime: 1000 });
sinon.stub(fs, 'readFile')
beforeEach(function() {
sandbox = sinon.sandbox.create()
sandbox.stub(fs, 'writeFile').yieldsAsync();
sandbox.stub(fs, 'exists').yieldsAsync(false);
sandbox.stub(fs, 'openSync').returns('fakeFd')
sandbox.stub(fs, 'readSync')
sandbox.stub(fs, 'readFile')
.withArgs('/proc/cpuinfo').yieldsAsync(null, getCpuInfo());
sinon.spy(fs, 'unwatchFile');
});
beforeEach(function() {
gpio.reset();
gpio.setMode(gpio.MODE_BCM);
gpio.version = 1;
});
fs.writeFile.reset();
fs.exists.reset();
fs.watchFile.reset();
fs.readFile.reset();
fs.unwatchFile.reset();
afterEach(function() {
sandbox.restore()
listeners = []
});

@@ -44,3 +64,3 @@

beforeEach(function() {
listener = sinon.spy();
listener = sandbox.spy();
gpio.on('modeChange', listener);

@@ -61,3 +81,3 @@

beforeEach(function() {
listener = sinon.spy();
listener = sandbox.spy();
gpio.on('modeChange', listener);

@@ -89,21 +109,2 @@

describe('setPollFrequency()', function() {
context('when poll frequency is set to 1000', function() {
beforeEach(function() {
gpio.setPollFrequency(1000);
});
context('and a channel is set up', function() {
beforeEach(function(done) {
gpio.setup(7, null, done);
});
it('should set up a file watcher with the specified poll frequency', function() {
var args = fs.watchFile.lastCall.args;
assert.deepEqual(args[1], { persistent: true, interval: 1000 });
});
});
});
});
describe('setup()', function() {

@@ -114,3 +115,3 @@ context('when given an invalid channel', function() {

beforeEach(function(done) {
callback = sinon.spy(onSetupComplete);
callback = sandbox.spy(onSetupComplete);
function onSetupComplete() {

@@ -133,3 +134,3 @@ done();

beforeEach(function(done) {
callback = sinon.spy(onSetupComplete);
callback = sandbox.spy(onSetupComplete);
function onSetupComplete() {

@@ -152,3 +153,3 @@ done();

beforeEach(function(done) {
callback = sinon.spy(onSetupComplete);
callback = sandbox.spy(onSetupComplete);
function onSetupComplete() {

@@ -167,2 +168,20 @@ done();

context('when given an invalid edge', function() {
var callback;
beforeEach(function(done) {
callback = sandbox.spy(onSetupComplete);
function onSetupComplete() {
done();
}
gpio.setup(7, gpio.DIR_IN, 'foo', callback);
});
it('should run the callback with an error', function() {
sinon.assert.calledOnce(callback);
assert.ok(callback.getCall(0).args[0]);
});
});
context('when the channel is already exported', function() {

@@ -199,6 +218,6 @@ beforeEach(function(done) {

beforeEach(function(done) {
listener = sinon.spy();
listener = sandbox.spy();
gpio.on('export', listener);
onSetup = sinon.spy(done);
onSetup = sandbox.spy(done);
gpio.setup(7, null, onSetup);

@@ -224,12 +243,28 @@ });

it('should set the channel direction to out by default', function() {
it('should set the channel edge to none by default', function() {
sinon.assert.called(fs.writeFile);
var args1 = fs.writeFile.getCall(1).args;
assert.equal(args1[0], PATH + '/gpio7/direction');
assert.equal(args1[1], 'out');
assert.equal(args1[0], PATH + '/gpio7/edge');
assert.equal(args1[1], 'none');
});
it('should set up a file watcher for the value', function() {
var args = fs.watchFile.lastCall.args;
assert.equal(args[0], PATH + '/gpio7/value');
it('should set the channel direction to out by default', function() {
sinon.assert.called(fs.writeFile);
var args2 = fs.writeFile.getCall(2).args;
assert.equal(args2[0], PATH + '/gpio7/direction');
assert.equal(args2[1], 'out');
});
it('should set up a listener', function() {
assert.equal(listeners.length, 1)
var listener = listeners[0]
sinon.assert.calledWith(listener.add, 'fakeFd')
});
it('should clear the interupt twice', function() {
sinon.assert.calledTwice(fs.readSync)
});
});

@@ -261,2 +296,20 @@

var edge_modes = ['none', 'rising', 'falling', 'both']
edge_modes.forEach(function(edge_mode) {
var edgeConstant = 'EDGE_' + edge_mode.toUpperCase()
context('and the edge is specified as ' + edge_mode, function() {
beforeEach(function(done) {
gpio.setup(7, gpio.DIR_OUT, gpio[edgeConstant], done);
});
it('should set the channel edge to ' + edge_mode, function() {
sinon.assert.called(fs.writeFile);
var args1 = fs.writeFile.getCall(1).args;
assert.equal(args1[0], PATH + '/gpio7/edge');
assert.equal(args1[1], edge_mode);
});
});
});
context('and callback is specified', function() {

@@ -266,3 +319,3 @@ var callback;

beforeEach(function(done) {
callback = sinon.spy(done);
callback = sandbox.spy(done);
gpio.setup(7, callback);

@@ -285,3 +338,3 @@ });

beforeEach(function(done) {
onSetup = sinon.spy(done);
onSetup = sandbox.spy(done);
gpio.setup(7, gpio.DIR_OUT, onSetup);

@@ -366,3 +419,3 @@ });

}
onWrite = sinon.spy(write);
onWrite = sandbox.spy(write);
gpio.write(3, true, onWrite);

@@ -383,3 +436,3 @@ });

beforeEach(function(done) {
onSetup = sinon.spy(done);
onSetup = sandbox.spy(done);
gpio.setup(7, gpio.DIR_IN, onSetup);

@@ -392,4 +445,4 @@ });

done();
}
onWrite = sinon.spy(callback);
};
onWrite = sandbox.spy(callback);
gpio.write(7, true, onWrite);

@@ -423,3 +476,3 @@ });

beforeEach(function(done) {
callback = sinon.spy(done);
callback = sandbox.spy(done);
gpio.read(7, callback);

@@ -446,3 +499,3 @@ });

beforeEach(function(done) {
callback = sinon.spy(done);
callback = sandbox.spy(done);
gpio.read(7, callback);

@@ -467,3 +520,3 @@ });

}
callback = sinon.spy(onRead);
callback = sandbox.spy(onRead);
gpio.read(3, callback);

@@ -493,3 +546,3 @@ });

beforeEach(function(done) {
callback = sinon.spy(done);
callback = sandbox.spy(done);
gpio.read(7, callback);

@@ -510,2 +563,39 @@ });

describe('stopListening', function() {
context('when pin 7 is setup for input', function() {
beforeEach(function(done) {
gpio.setup(7, gpio.DIR_IN, done);
});
context('and stopListening is called with pin 7', function() {
beforeEach(function(done) {
gpio.stopListening(7, done);
});
it('should stop listening to pin 7', function() {
var listener = listeners[0]
sinon.assert.calledOnce(listener.remove)
sinon.assert.calledWith(listener.remove, 'fakeFd')
});
});
});
context('when stopListening is called on a non-exported pin', function() {
var onStopListening
beforeEach(function(done) {
var callback = function() {
done();
};
onStopListening = sandbox.spy(callback);
gpio.stopListening(7, onStopListening);
});
it('should run the callback with an error', function() {
sinon.assert.calledOnce(onStopListening);
assert.ok(onStopListening.getCall(0).args[0]);
});
});
});
describe('destroy', function() {

@@ -520,35 +610,52 @@ context('when pins 7, 8 and 10 have been exported', function() {

if (--i === 0) {
onSetupComplete();
done()
}
});
});
});
function onSetupComplete() {
it('should have created 3 listeners', function() {
assert.equal(listeners.length, 3)
});
context('and destroy() is run', function() {
beforeEach(function(done) {
fs.writeFile.reset();
gpio.destroy(done);
}
});
})
it('should unexport pin 7', function() {
sinon.assert.calledWith(fs.writeFile, unexportPath, '7');
});
it('should unexport pin 7', function() {
sinon.assert.calledWith(fs.writeFile, unexportPath, '7');
});
it('should unexport pin 8', function() {
sinon.assert.calledWith(fs.writeFile, unexportPath, '8');
});
it('should unexport pin 8', function() {
sinon.assert.calledWith(fs.writeFile, unexportPath, '8');
});
it('should unexport pin 10', function() {
sinon.assert.calledWith(fs.writeFile, unexportPath, '10');
});
it('should unexport pin 10', function() {
sinon.assert.calledWith(fs.writeFile, unexportPath, '10');
});
it('should unwatch pin 7', function() {
sinon.assert.calledWith(fs.unwatchFile, PATH + '/gpio7/value');
});
it('should unwatch pin 7', function() {
var listener = listeners[0]
sinon.assert.calledOnce(listener.remove)
sinon.assert.calledWith(listener.remove, 'fakeFd')
sinon.assert.calledOnce(listener.close)
});
it('should unwatch pin 8', function() {
sinon.assert.calledWith(fs.unwatchFile, PATH + '/gpio8/value');
});
it('should unwatch pin 8', function() {
var listener = listeners[1]
sinon.assert.calledOnce(listener.remove)
sinon.assert.calledWith(listener.remove, 'fakeFd')
sinon.assert.calledOnce(listener.close)
});
it('should unwatch pin 10', function() {
sinon.assert.calledWith(fs.unwatchFile, PATH + '/gpio10/value');
it('should unwatch pin 9', function() {
var listener = listeners[2]
sinon.assert.calledOnce(listener.remove)
sinon.assert.calledWith(listener.remove, 'fakeFd')
sinon.assert.calledOnce(listener.close)
});
});

@@ -566,6 +673,4 @@

// Remove previous stub so that we can control when watchFile triggers
fs.watchFile.restore();
sinon.stub(fs, 'watchFile');
listener = sinon.spy();
listener = sandbox.spy();
gpio.on('change', listener);

@@ -578,17 +683,6 @@

beforeEach(function(done) {
fs.readFile.yieldsAsync(null, '1');
gpio.on('change', function() {
done();
});
// Trigger voltage change
var cb = fs.watchFile.lastCall.args[2];
cb({ mtime: 2000 }, { mtime: 1000 });
// this is erroring out due to watchFile not working as expected. Please fix
});
it('should emit a change event', function() {
sinon.assert.calledWith(listener, 7, true);
});
});

@@ -602,5 +696,2 @@ });

// Remove previous stub so that we can control when watchFile triggers
fs.watchFile.restore();
sinon.stub(fs, 'watchFile');
gpio.setup(7, gpio.DIR_IN);

@@ -607,0 +698,0 @@ });

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc