diff --git a/firmware/port.c b/firmware/port.c index 6519b83..7e8a161 100644 --- a/firmware/port.c +++ b/firmware/port.c @@ -734,7 +734,16 @@ void port_handle_extint(PortData *p, u32 flags) { if (port_pin_supports_interrupt(p, pin)) { Pin sys_pin = p->port->gpio[pin]; if (flags & (1 << pin_extint(sys_pin))) { - port_send_status(p, REPLY_ASYNC_PIN_CHANGE_N + pin); + u8 response = REPLY_ASYNC_PIN_CHANGE_N + pin; + + // If the pin's state is high, set bit 3 + // of the response byte. This will be used to + // inform "change" listeners of the present state + // at the moment of the interrupt. + if (pin_read(port_selected_pin(p))) { + response |= 1 << 3; + } + port_send_status(p, response); if (eic_read_config(sys_pin) & EIC_CONFIG_SENSE_LEVEL) { // Async level interrupts only trigger once eic_config(sys_pin, EIC_CONFIG_SENSE_NONE); diff --git a/node/tessel-export.js b/node/tessel-export.js index 016b2bc..91f3a35 100644 --- a/node/tessel-export.js +++ b/node/tessel-export.js @@ -235,9 +235,9 @@ Tessel.Port = function(name, socketPath, board) { // This is some other async transaction } else if (byte >= REPLY.MIN_ASYNC) { // If this is a pin change - if (byte >= REPLY.ASYNC_PIN_CHANGE_N && byte < REPLY.ASYNC_PIN_CHANGE_N + 8) { - // Pull out which pin it is - var pin = this.pin[byte - REPLY.ASYNC_PIN_CHANGE_N]; + if (byte >= REPLY.ASYNC_PIN_CHANGE_N && byte < REPLY.ASYNC_PIN_CHANGE_N + 16) { + // Pull out the pin number (requires clearing the value bit) + var pin = this.pin[(byte - REPLY.ASYNC_PIN_CHANGE_N) & ~(1 << 3)]; // Get the mode change var mode = pin.interruptMode; @@ -250,7 +250,13 @@ Tessel.Port = function(name, socketPath, board) { } // Emit the change - pin.emit(mode); + if (mode === 'change') { + // "change" is otherwise ambiguous. + pin.emit('change', (byte >> 3) & 1); + } else { + // high, low, rise & fall are _not_ ambiguous + pin.emit(mode); + } } else { // Some other async event this.emit('async-event', byte); diff --git a/node/test/unit/tessel.js b/node/test/unit/tessel.js index 25432b9..39cc436 100644 --- a/node/test/unit/tessel.js +++ b/node/test/unit/tessel.js @@ -1262,6 +1262,64 @@ exports['Tessel.Pin'] = { test.done(); }, + interruptChangeStateLow: function(test) { + test.expect(17); + + var spy = sandbox.spy(); + + [2, 5, 6, 7].forEach(function(pinIndex) { + this.a.pin[pinIndex].on('change', spy); + this.b.pin[pinIndex].on('change', spy); + + 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.emit('readable'); + + this.b.sock.read.returns(new Buffer([REPLY.ASYNC_PIN_CHANGE_N + pinIndex])); + this.b.sock.emit('readable'); + }, this); + + test.equal(spy.callCount, 8); + + for (var i = 0; i < 8; i++) { + test.equal(spy.getCall(i).args[0], 0); + } + + test.done(); + }, + + interruptChangeStateHigh: function(test) { + test.expect(17); + + var spy = sandbox.spy(); + + [2, 5, 6, 7].forEach(function(pinIndex) { + this.a.pin[pinIndex].on('change', spy); + this.b.pin[pinIndex].on('change', spy); + + 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) | (1 << 3)])); + this.a.sock.emit('readable'); + + this.b.sock.read.returns(new Buffer([(REPLY.ASYNC_PIN_CHANGE_N + pinIndex) | (1 << 3)])); + this.b.sock.emit('readable'); + }, this); + + test.equal(spy.callCount, 8); + + for (var i = 0; i < 8; i++) { + test.equal(spy.getCall(i).args[0], 1); + } + + test.done(); + }, + removeListener: function(test) { test.expect(14);