Skip to content

Commit

Permalink
Button: refactor to controller design.
Browse files Browse the repository at this point in the history
  • Loading branch information
rwaldron committed Jun 1, 2015
1 parent f52b814 commit 3baeaa9
Show file tree
Hide file tree
Showing 2 changed files with 314 additions and 113 deletions.
194 changes: 122 additions & 72 deletions lib/button.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,35 @@ var priv = new Map(),
};


var Controllers = {
DEFAULT: {
initialize: {
value: function(opts, dataHandler) {

if (Pins.isFirmata(this) && typeof opts.pinValue === "string" && opts.pinValue[0] === "A") {
opts.pinValue = this.io.analogPins[+opts.pinValue.slice(1)];
}

this.pin = +opts.pinValue;

this.io.pinMode(this.pin, this.io.MODES.INPUT);

// Enable the pullup resistor after setting pin mode
if (this.pullup) {
this.io.digitalWrite(this.pin, this.io.HIGH);
}

this.io.digitalRead(this.pin, dataHandler);
}
},
toBoolean: {
value: function(raw) {
return raw === this.downValue;
}
}
}
};

/**
* Button
* @constructor
Expand All @@ -28,9 +57,19 @@ var priv = new Map(),
*/

function Button(opts) {
var timeout;
if (!(this instanceof Button)) {
return new Button(opts);
}

var pinValue;
var isFirmata;
var raw;
var invert = false;
var downValue = 1;
var upValue = 0;
var controller = null;
var state = {
timeout: null
};

// Create a 5 ms debounce boundary on event triggers
// this avoids button events firing on
Expand All @@ -41,110 +80,121 @@ function Button(opts) {
}, this);
}, 7);

if (!(this instanceof Button)) {
return new Button(opts);
}

pinValue = typeof opts === "object" ? opts.pin : opts;

Board.Component.call(
this, opts = Board.Options(opts)
);

isFirmata = Pins.isFirmata(this);
opts.pinValue = pinValue;

if (isFirmata && typeof pinValue === "string" && pinValue[0] === "A") {
pinValue = this.io.analogPins[+pinValue.slice(1)];
if (opts.controller && typeof opts.controller === "string") {
controller = Controllers[opts.controller.toUpperCase()];
} else {
controller = opts.controller;
}

pinValue = +pinValue;

// Set the pin to INPUT mode
this.mode = this.io.MODES.INPUT;

// Option to enable the built-in pullup resistor
this.isPullup = opts.isPullup || false;

if (isFirmata && !Number.isNaN(pinValue)) {
this.pin = pinValue;
if (controller == null) {
controller = Controllers.DEFAULT;
}

this.io.pinMode(this.pin, this.mode);
Object.defineProperties(this, controller);

// Enable the pullup resistor after setting pin mode
if (this.isPullup) {
this.io.digitalWrite(this.pin, this.io.HIGH);
}
// `holdtime` is used by a timeout to determine
// if the button has been released within a specified
// time frame, in milliseconds.
this.holdtime = opts.holdtime || 500;

// `opts.isPullup` is included as part of an effort to
// phase out "isFoo" options properties
this.pullup = opts.pullup || opts.isPullup || false;

// Turns out some button circuits will send
// 0 for up and 1 for down, and some the inverse,
// so we can invert our function with this option.
// Default to invert in pullup mode, but use opts.invert
// if explicitly defined (even if false)
this.invert = typeof opts.invert !== "undefined" ?
opts.invert : (this.isPullup || false);
invert = typeof opts.invert !== "undefined" ?
opts.invert : (this.pullup || false);

this.downValue = this.invert ? 0 : 1;
this.upValue = this.invert ? 1 : 0;

// Button instance properties
this.holdtime = opts && opts.holdtime || 500;
if (invert) {
downValue = downValue ^ 1;
upValue = upValue ^ 1;
}

// Create a "state" entry for privately
// storing the state of the button
priv.set(this, {
isDown: false
});

// Analog Read event loop
this.io.digitalRead(this.pin, function(data) {
var err = null;

// data = upValue, this.isDown = true
// indicates that the button has been released
// after previously being pressed
if (data === this.upValue && this.isDown) {
if (timeout) {
clearTimeout(timeout);
}
priv.get(this).isDown = false;

trigger.call(this, "up");
}

// data = downValue, this.isDown = false
// indicates that the button has been pressed
// after previously being released
if (data === this.downValue && !this.isDown) {

// Update private data
priv.get(this).isDown = true;

// Call debounced event trigger for given "key"
// This will trigger all event aliases assigned
// to "key"
trigger.call(this, "down" /* key */ );

timeout = setTimeout(function() {
if (this.isDown) {
this.emit("hold", err);
}
}.bind(this), this.holdtime);
}
}.bind(this));
priv.set(this, state);

Object.defineProperties(this, {
value: {
get: function() {
return Number(this.isDown);
}
},
invert: {
get: function() {
return invert;
},
set: function(value) {
invert = value;
downValue = invert ? 0 : 1;
upValue = invert ? 1 : 0;
}
},
downValue: {
get: function() {
return downValue;
},
set: function(value) {
downValue = value;
upValue = value ^ 1;
invert = value ? true : false;
}
},
upValue: {
get: function() {
return upValue;
},
set: function(value) {
upValue = value;
downValue = value ^ 1;
invert = value ? true : false;
}
},
isDown: {
get: function() {
return priv.get(this).isDown;
return this.toBoolean(raw);
}
}
});

if (typeof this.initialize === "function") {
this.initialize(opts, function(data) {
var err = null;

// Update the raw data value, which
// is used by isDown = toBoolean()
raw = data;

if (!this.isDown) {
if (state.timeout) {
clearTimeout(state.timeout);
}
trigger.call(this, "up");
}

if (this.isDown) {
trigger.call(this, "down");

state.timeout = setTimeout(function() {
if (this.isDown) {
this.emit("hold", err);
}
}.bind(this), this.holdtime);
}
}.bind(this));
}
}

util.inherits(Button, events.EventEmitter);
Expand Down
Loading

0 comments on commit 3baeaa9

Please sign in to comment.