johnny-five
Version:
The JavaScript Arduino Programming Framework.
286 lines (225 loc) • 5.68 kB
JavaScript
var Board = require("../lib/board.js"),
Descriptor = require("descriptor"),
__ = require("../lib/fn.js"),
events = require("events"),
util = require("util");
var priv = new Map(),
modes;
modes = {
INPUT: 0x00,
OUTPUT: 0x01,
ANALOG: 0x02,
PWM: 0x03,
SERVO: 0x04
};
/**
* Pin
* @constructor
*
* @description Direct Pin access objects
*
* @param {Object} opts Options: pin, freq, range
*/
function Pin(opts) {
var isAnalogInput, isDTOA, isPin, pinValue, addr, type, highLow, value, state;
if (!(this instanceof Pin)) {
return new Pin(opts);
}
pinValue = typeof opts === "object" ? (opts.addr || opts.pin || 0) : opts;
isAnalogInput = Pin.isAnalog(opts);
isDTOA = false;
// Initialize a Device instance on a Board
Board.Device.call(
this, opts = Board.Options(opts)
);
opts.addr = opts.addr || opts.pin;
if (this.io.analogPins.indexOf(pinValue) !== -1) {
isDTOA = true;
isAnalogInput = false;
}
isPin = typeof opts !== "object";
addr = isDTOA ? pinValue : (isPin ? opts : opts.addr);
type = opts.type || (isAnalogInput ? "analog" : "digital");
value = 0;
// Create a private side table
state = {
mode: null,
last: null,
value: 0
};
priv.set(this, state);
// Create read-only "addr(address)" property
Object.defineProperties(this, {
type: new Descriptor(type),
addr: new Descriptor(addr, "!writable"),
value: {
get: function() {
return state.value;
}
},
mode: {
set: function(mode) {
var state = priv.get(this);
// set mode
// TODO: if setting to PWM, check if this pin is capable of PWM
// log error if not capable
if (state.mode !== mode) {
state.mode = mode;
this.io.pinMode(this.addr, mode);
}
},
get: function() {
return priv.get(this).mode;
}
}
});
this.mode = this.as || opts.mode || (isAnalogInput ? 0x00 : 0x01);
highLow = function(state) {
return function(data) {
var privs, isNot, emit;
privs = priv.get(this);
// Update the value closure
privs.value = data;
isNot = state ? "low" : "high";
emit = state ? "high" : "low";
if (privs.mode === modes.INPUT) {
if (privs.last === null) {
privs.last = isNot;
}
if (data === state && privs.last === isNot) {
privs.last = emit;
this.emit(emit);
}
}
// Emit a firehose "data" event
this.emit("data");
}.bind(this);
}.bind(this);
// Debounced for noise reduction: more accurately
// detect HIGH state.
this.io[type + "Read"](
this.addr, __.debounce(highLow(1), 50)
);
// No debounce to read the constant stream
// (very noisy, only care about 0)
this.io[type + "Read"](
this.addr, highLow(0)
);
}
util.inherits(Pin, events.EventEmitter);
/**
* Pin.@@MODE
*
* Read-only constants
* Pin.INPUT = 0x00
* Pin.OUTPUT = 0x01
* Pin.ANALOG = 0x02
* Pin.PWM = 0x03
* Pin.SERVO = 0x04
*
*/
Object.keys(modes).forEach(function(mode) {
Object.defineProperty(Pin, mode, {
value: modes[mode]
});
});
Pin.isAnalog = function(opts) {
if (typeof opts === "string" && Pin.isPrefixed(opts, ["I", "A"])) {
return true;
}
if (typeof opts === "object") {
return Pin.isAnalog(
typeof opts.addr !== "undefined" ? opts.addr : opts.pin
);
}
};
Pin.isPrefixed = function(value, prefixes) {
value = value[0];
return prefixes.reduce(function(resolution, prefix) {
if (!resolution) {
return prefix === value;
}
return resolution;
}, false);
};
Pin.write = function(pin, val) {
var state = priv.get(pin);
state.value = val;
// Set the correct mode (OUTPUT)
// This will only set if it needs to be set, otherwise a no-op
pin.mode = modes.OUTPUT;
// Create the correct type of write command
pin.io[pin.type + "Write"](pin.addr, val);
pin.emit("write", null, val);
};
Pin.read = function(pin, callback) {
// Set the correct mode (INPUT)
// This will only set if it needs to be set, otherwise a no-op
pin.mode = modes.INPUT;
pin.io[pin.type + "Read"](pin.addr, function() {
callback.apply(pin, arguments);
});
};
// Pin.prototype.isDigital = function() {
// return this.addr > 1;
// };
// Pin.prototype.isAnalog = function() {
// return this.board > 1;
// };
// Pin.prototype.isPWM = function() {
// };
// Pin.prototype.isServo = function() {
// };
// Pin.prototype.isI2C = function() {
// };
// Pin.prototype.isSerial = function() {
// };
// Pin.prototype.isInterrupt = function() {
// };
// Pin.prototype.isVersion = function() {
// };
Pin.prototype.query = function(callback) {
var index = this.addr;
if (this.type === "analog") {
index = this.io.analogPins[this.addr];
}
function handler() {
callback(this.io.pins[index]);
}
this.io.queryPinState(index, handler.bind(this));
return this;
};
/**
* high Write high/1 to the pin
* @return {Pin}
*/
Pin.prototype.high = function() {
var value = this.type === "analog" ? 255 : 1;
Pin.write(this, value);
this.emit("high");
return this;
};
/**
* low Write low/0 to the pin
* @return {Pin}
*/
Pin.prototype.low = function() {
Pin.write(this, 0);
this.emit("low");
return this;
};
/**
* read Read from the pin, value is passed to callback continuation
* @return {Pin}
*/
/**
* write Write to a pin
* @return {Pin}
*/
["read", "write"].forEach(function(state) {
Pin.prototype[state] = function(valOrCallback) {
Pin[state](this, valOrCallback);
return this;
};
});
module.exports = Pin;