Skip to content

Commit

Permalink
emit 'rise' and 'fall' events along with 'change' event (#168)
Browse files Browse the repository at this point in the history
* jd-change-rise-fall-events

* Fixed how to handle multiple pin event listeners

* Clean up code per feedback, fix unit test issues

* more code clean up, double quotes within strings

* remove erroneous | (0<<3)
  • Loading branch information
dudleyjosh authored and rwaldron committed Apr 29, 2016
1 parent 09ae1e9 commit 9dd0ef3
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 32 deletions.
48 changes: 27 additions & 21 deletions node/tessel-export.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,23 +240,22 @@ Tessel.Port = function(name, socketPath, board) {
var pin = this.pin[(byte - REPLY.ASYNC_PIN_CHANGE_N) & ~(1 << 3)];
// Get the mode change
var mode = pin.interruptMode;
// Get the pin value
var pinValue = (byte >> 3) & 1;

// If this was a one-time mode
// For one-time 'low' or 'high' event
if (mode === 'low' || mode === 'high') {
pin.emit(mode);
// Reset the pin interrupt state (prevent constant interrupts)
pin.interruptMode = null;
// Decrement the number of tasks waiting on the socket
this.unref();
}

// Emit the change
if (mode === 'change') {
// "change" is otherwise ambiguous.
pin.emit('change', (byte >> 3) & 1);
} else {
// high, low, rise & fall are _not_ ambiguous
pin.emit(mode);
// Emit the change and rise or fall
pin.emit('change', pinValue);
pin.emit(pinValue ? 'rise' : 'fall');
}

} else {
// Some other async event
this.emit('async-event', byte);
Expand Down Expand Up @@ -585,33 +584,40 @@ Tessel.Pin.prototype.removeAllListeners = function(event) {
};

Tessel.Pin.prototype.addListener = function(mode, callback) {
// Check for valid pin event mode
if (typeof Tessel.Pin.interruptModes[mode] !== 'undefined') {
if (!this.interruptSupported) {
throw new Error(`Interrupts are not supported on pin ${this.pin}. Pins 2, 5, 6, and 7 on either port support interrupts.`);
}

if ((mode === 'high' || mode === 'low') && !callback.listener) {
// For one-time 'low' or 'high' event
if ((mode === 'low' || mode === 'high') && !callback.listener) {
throw new Error('Cannot use "on" with level interrupts. You can only use "once".');
}

if (this.interruptMode !== mode) {
if (this.interruptMode) {
throw new Error(`Cannot set pin interrupt mode to ${mode}; already listening for ${this.interruptMode}`);
// Can't set multiple listeners when using 'low' or 'high'
if (this.interruptMode) {
var singleEventModes = ['low', 'high'];
if (singleEventModes.some(value => mode === value || this.interruptMode === value)) {
throw new Error(`Cannot set pin interrupt mode to "${mode}"; already listening for "${this.interruptMode}". Can only set multiple listeners with "change", "rise" & "fall".`);
}
// Set the socket reference so the script doesn't exit
this._port.ref();

this._setInterruptMode(mode);
}
}

// Add the event listener
Tessel.Pin.super_.prototype.on.call(this, mode, callback);
// Set the socket reference so the script doesn't exit
this._port.ref();
this._setInterruptMode(mode);

// Add the event listener
Tessel.Pin.super_.prototype.on.call(this, mode, callback);
} else {
throw new Error(`Invalid pin event mode "${mode}". Valid modes are "change", "rise", "fall", "high" and "low".`);
}
};
Tessel.Pin.prototype.on = Tessel.Pin.prototype.addListener;

Tessel.Pin.prototype._setInterruptMode = function(mode) {
this.interruptMode = mode;
// rise and fall events will be emitted by change event
this.interruptMode = (mode === 'rise' || mode === 'fall') ? 'change' : mode;
var bits = mode ? Tessel.Pin.interruptModes[mode] << 4 : 0;
this._port._simple_cmd([CMD.GPIO_INT, this.pin | bits]);
};
Expand Down
38 changes: 27 additions & 11 deletions node/test/unit/tessel.js
Original file line number Diff line number Diff line change
Expand Up @@ -1204,22 +1204,38 @@ exports['Tessel.Pin'] = {
test.done();
},

interruptErroMessages: function(test) {
test.expect(2);
interruptErrorMessages: function(test) {
test.expect(4);

var spy = sandbox.spy();

try {
this.a.pin[0].once('test', spy);
} catch (error) {
test.equal(error.message, 'Invalid pin event mode "test". Valid modes are "change", "rise", "fall", "high" and "low".');
}

try {
this.a.pin[0].once('low', spy);
} catch (error) {
test.equal(error.message, 'Interrupts are not supported on pin 0. Pins 2, 5, 6, and 7 on either port support interrupts.');
}

try {
this.a.pin[2].once('rise', spy);
this.a.pin[2].once('fall', spy);
this.a.pin[2].on('low', spy);
} catch (error) {
test.equal(error.message, 'Cannot use "on" with level interrupts. You can only use "once".');
}

// Set 'change', 'fall' and 'rise' before setting 'low' to verify that it allows these to be set simultaneously.
// It will fail the test on the error message match if it doesn't allow them to be set simultaneously the way it should
try {
this.a.pin[2].on('change', spy);
this.a.pin[2].on('fall', spy);
this.a.pin[2].on('rise', spy);
this.a.pin[2].once('low', spy);
} catch (error) {
test.equal(error.message, 'Cannot set pin interrupt mode to fall; already listening for rise');
test.equal(error.message, 'Cannot set pin interrupt mode to "low"; already listening for "change". Can only set multiple listeners with "change", "rise" & "fall".');
}

test.done();
Expand Down Expand Up @@ -1323,14 +1339,14 @@ exports['Tessel.Pin'] = {
this.a.pin[pinIndex].on('rise', spy);
this.b.pin[pinIndex].on('rise', spy);

test.equal(this.a.pin[pinIndex].interruptMode, 'rise');
test.equal(this.b.pin[pinIndex].interruptMode, 'rise');
test.equal(this.a.pin[pinIndex].interruptMode, 'change');
test.equal(this.b.pin[pinIndex].interruptMode, 'change');

// Simulate receipt of pin state changes
this.a.sock.read.returns(new Buffer([REPLY.ASYNC_PIN_CHANGE_N + pinIndex]));
this.a.sock.read.returns(new Buffer([(REPLY.ASYNC_PIN_CHANGE_N + pinIndex) | (1 << 3)]));
this.a.sock.emit('readable');

this.b.sock.read.returns(new Buffer([REPLY.ASYNC_PIN_CHANGE_N + pinIndex]));
this.b.sock.read.returns(new Buffer([(REPLY.ASYNC_PIN_CHANGE_N + pinIndex) | (1 << 3)]));
this.b.sock.emit('readable');
}, this);

Expand All @@ -1347,8 +1363,8 @@ exports['Tessel.Pin'] = {
this.a.pin[pinIndex].on('fall', spy);
this.b.pin[pinIndex].on('fall', spy);

test.equal(this.a.pin[pinIndex].interruptMode, 'fall');
test.equal(this.b.pin[pinIndex].interruptMode, 'fall');
test.equal(this.a.pin[pinIndex].interruptMode, 'change');
test.equal(this.b.pin[pinIndex].interruptMode, 'change');

// Simulate receipt of pin state changes
this.a.sock.read.returns(new Buffer([REPLY.ASYNC_PIN_CHANGE_N + pinIndex]));
Expand Down

0 comments on commit 9dd0ef3

Please sign in to comment.