johnny-five
Version:
The JavaScript Robotics and Hardware Programming Framework. Use with: Arduino (all models), Electric Imp, Beagle Bone, Intel Galileo & Edison, Linino One, Pinoccio, pcDuino3, Raspberry Pi, Particle/Spark Core & Photon, Tessel 2, TI Launchpad and more!
1,225 lines (1,002 loc) • 30 kB
JavaScript
var IS_TEST_MODE = !!process.env.IS_TEST_MODE;
var Board = require("../lib/board");
var Emitter = require("events").EventEmitter;
var util = require("util");
var nanosleep = require("../lib/sleep").nano;
var __ = require("../lib/fn");
var priv = new Map();
var active = new Map();
function Base() {
Emitter.call(this);
this.HIGH = 1;
this.LOW = 0;
this.isReady = false;
this.MODES = {};
this.pins = [];
this.analogPins = [];
}
util.inherits(Base, Emitter);
var Controllers = {
// http://www.adafruit.com/datasheets/mcp23017.pdf
MCP23017: {
REGISTER: {
value: {
ADDRESS: 0x20,
// IO A
IODIRA: 0x00,
GPPUA: 0x0C,
GPIOA: 0x12,
OLATA: 0x14,
// IO B
IODIRB: 0x01,
GPPUB: 0x0D,
GPIOB: 0x13,
OLATB: 0x15,
}
},
initialize: {
value: function(opts) {
var state = priv.get(this);
state.iodir = [ 0xff, 0xff ];
state.olat = [ 0xff, 0xff ];
state.gpio = [ 0xff, 0xff ];
state.gppu = [ 0x00, 0x00 ];
this.address = opts.address || this.REGISTER.ADDRESS;
opts.address = this.address;
this.io.i2cConfig(opts);
this.io.i2cWrite(this.address, [ this.REGISTER.IODIRA, state.iodir[this.REGISTER.IODIRA] ]);
this.io.i2cWrite(this.address, [ this.REGISTER.IODIRB, state.iodir[this.REGISTER.IODIRB] ]);
Object.assign(this.MODES, this.io.MODES);
for (var i = 0; i < 16; i++) {
this.pins.push({
supportedModes: [
this.MODES.INPUT,
this.MODES.OUTPUT
],
mode: 0,
value: 0,
report: 0,
analogChannel: 127
});
this.pinMode(i, this.MODES.OUTPUT);
this.digitalWrite(i, this.LOW);
}
this.name = "MCP23017";
this.isReady = true;
this.emit("connect");
this.emit("ready");
}
},
normalize: {
value: function(pin) {
return pin;
}
},
// 1.6.1 I/O DIRECTION REGISTER
pinMode: {
value: function(pin, mode) {
var state = priv.get(this);
var pinIndex = pin;
var port = 0;
var iodir = null;
if (pin < 8) {
port = this.REGISTER.IODIRA;
} else {
port = this.REGISTER.IODIRB;
pin -= 8;
}
iodir = state.iodir[port];
if (mode === this.io.MODES.INPUT) {
iodir |= 1 << pin;
} else {
iodir &= ~(1 << pin);
}
this.pins[pinIndex].mode = mode;
this.io.i2cWrite(this.address, [ port, iodir ]);
state.iodir[port] = iodir;
}
},
// 1.6.10 PORT REGISTER
digitalWrite: {
value: function(pin, value) {
var state = priv.get(this);
var pinIndex = pin;
var port = 0;
var gpio = 0;
// var olataddr = 0;
var gpioaddr = 0;
if (pin < 8) {
port = this.REGISTER.IODIRA;
// olataddr = this.REGISTER.OLATA;
gpioaddr = this.REGISTER.GPIOA;
} else {
port = this.REGISTER.IODIRB;
// olataddr = this.REGISTER.OLATB;
gpioaddr = this.REGISTER.GPIOB;
pin -= 8;
}
gpio = state.olat[port];
if (value === this.io.HIGH) {
gpio |= 1 << pin;
} else {
gpio &= ~(1 << pin);
}
this.pins[pinIndex].report = 0;
this.pins[pinIndex].value = value;
this.io.i2cWrite(this.address, [ gpioaddr, gpio ]);
state.olat[port] = gpio;
state.gpio[port] = gpio;
}
},
// 1.6.7 PULL-UP RESISTOR
// CONFIGURATION REGISTER
pullUp: {
value: function(pin, value) {
var state = priv.get(this);
var port = 0;
var gppu = 0;
var gppuaddr = 0;
if (pin < 8) {
port = this.REGISTER.IODIRA;
gppuaddr = this.REGISTER.GPPUA;
} else {
port = this.REGISTER.IODIRB;
gppuaddr = this.REGISTER.GPPUB;
pin -= 8;
}
gppu = state.gppu[port];
if (value === this.io.HIGH) {
gppu |= 1 << pin;
} else {
gppu &= ~(1 << pin);
}
this.io.i2cWrite(this.address, [ gppuaddr, gppu ]);
state.gppu[port] = gppu;
}
},
digitalRead: {
value: function(pin, callback) {
var pinIndex = pin;
var gpioaddr = 0;
if (pin < 8) {
gpioaddr = this.REGISTER.GPIOA;
} else {
gpioaddr = this.REGISTER.GPIOB;
pin -= 8;
}
this.pins[pinIndex].report = 1;
this.on("digital-read-" + pin, callback);
this.io.i2cRead(this.address, gpioaddr, 1, function(data) {
var byte = data[0];
var value = byte >> pin & 0x01;
this.pins[pinIndex].value = value;
this.emit("digital-read-" + pin, value);
}.bind(this));
}
},
},
MCP23008: {
REGISTER: {
value: {
ADDRESS: 0x20,
IODIR: 0x00,
GPPU: 0x06,
GPIO: 0x09,
OLAT: 0x0A,
}
},
initialize: {
value: function(opts) {
var state = priv.get(this);
state.iodir = [ 0xff ];
state.olat = [ 0xff ];
state.gpio = [ 0xff ];
state.gppu = [ 0x00 ];
this.address = opts.address || this.REGISTER.ADDRESS;
opts.address = this.address;
this.io.i2cConfig(opts);
this.io.i2cWrite(this.address, [ this.REGISTER.IODIR, state.iodir[this.REGISTER.IODIR] ]);
Object.assign(this.MODES, this.io.MODES);
for (var i = 0; i < 8; i++) {
this.pins.push({
supportedModes: [
this.MODES.INPUT,
this.MODES.OUTPUT
],
mode: 0,
value: 0,
report: 0,
analogChannel: 127
});
this.pinMode(i, this.MODES.OUTPUT);
this.digitalWrite(i, this.LOW);
}
this.name = "MCP23008";
this.isReady = true;
this.emit("connect");
this.emit("ready");
}
},
normalize: {
value: function(pin) {
return pin;
}
},
// 1.6.1 I/O DIRECTION REGISTER
pinMode: {
value: function(pin, mode) {
var state = priv.get(this);
var pinIndex = pin;
var port = this.REGISTER.IODIR;
var iodir = state.iodir[port];
if (mode === this.io.MODES.INPUT) {
iodir |= 1 << pin;
} else {
iodir &= ~(1 << pin);
}
this.pins[pinIndex].mode = mode;
this.io.i2cWrite(this.address, [ port, iodir ]);
state.iodir[port] = iodir;
}
},
// 1.6.10 PORT REGISTER
digitalWrite: {
value: function(pin, value) {
var state = priv.get(this);
var pinIndex = pin;
var port = this.REGISTER.IODIR;
var gpioaddr = this.REGISTER.GPIO;
var gpio = state.olat[port];
if (value === this.io.HIGH) {
gpio |= 1 << pin;
} else {
gpio &= ~(1 << pin);
}
this.pins[pinIndex].report = 0;
this.pins[pinIndex].value = value;
this.io.i2cWrite(this.address, [ gpioaddr, gpio ]);
state.olat[port] = gpio;
state.gpio[port] = gpio;
}
},
// 1.6.7 PULL-UP RESISTOR
// CONFIGURATION REGISTER
pullUp: {
value: function(pin, value) {
var state = priv.get(this);
var port = this.REGISTER.IODIR;
var gppuaddr = this.REGISTER.GPPU;
var gppu = state.gppu[port];
if (value === this.io.HIGH) {
gppu |= 1 << pin;
} else {
gppu &= ~(1 << pin);
}
this.io.i2cWrite(this.address, [ gppuaddr, gppu ]);
state.gppu[port] = gppu;
}
},
digitalRead: {
value: function(pin, callback) {
var pinIndex = pin;
var gpioaddr = this.REGISTER.GPIO;
this.pins[pinIndex].report = 1;
this.on("digital-read-" + pin, callback);
this.io.i2cRead(this.address, gpioaddr, 1, function(data) {
var byte = data[0];
var value = byte >> pin & 0x01;
this.pins[pinIndex].value = value;
this.emit("digital-read-" + pin, value);
}.bind(this));
}
},
},
PCF8574: {
REGISTER: {
value: {
ADDRESS: 0x20,
}
},
initialize: {
value: function(opts) {
var state = priv.get(this);
state.port = 0x00;
state.ddr = 0x00;
state.pins = 0x00;
this.address = opts.address || this.REGISTER.ADDRESS;
opts.address = this.address;
this.io.i2cConfig(opts);
Object.assign(this.MODES, this.io.MODES);
for (var i = 0; i < 8; i++) {
this.pins.push({
supportedModes: [
this.MODES.INPUT,
this.MODES.OUTPUT
],
mode: 1,
value: 0,
report: 0,
analogChannel: 127
});
this.pinMode(i, this.MODES.OUTPUT);
this.digitalWrite(i, this.LOW);
}
this.name = "PCF8574";
this.isReady = true;
this.emit("connect");
this.emit("ready");
}
},
normalize: {
value: function(pin) {
return pin;
}
},
pinMode: {
value: function(pin, mode) {
var state = priv.get(this);
var pinIndex = pin;
var port = state.port;
var ddr = state.ddr;
var pins = state.pins;
if (mode === this.MODES.INPUT) {
ddr &= ~(1 << pin);
port &= ~(1 << pin);
} else {
ddr |= (1 << pin);
port &= ~(1 << pin);
}
this.pins[pinIndex].mode = mode;
state.port = port;
state.ddr = ddr;
this.io.i2cWrite(this.address, (pins & ~ddr) | port);
}
},
digitalWrite: {
value: function(pin, value) {
var state = priv.get(this);
var pinIndex = pin;
var port = state.port;
var ddr = state.ddr;
var pins = state.pins;
if (value) {
port |= 1 << pin;
} else {
port &= ~(1 << pin);
}
this.pins[pinIndex].report = 0;
this.pins[pinIndex].value = value;
state.port = port;
this.io.i2cWrite(this.address, (pins & ~ddr) | port);
}
},
digitalRead: {
value: function(pin, callback) {
var state = priv.get(this);
var pinIndex = pin;
this.pins[pinIndex].report = 1;
this.on("digital-read-" + pin, callback);
this.io.i2cRead(this.address, 1, function(data) {
var byte = data[0];
var value = byte >> pin & 0x01;
state.pins = byte;
this.pins[pinIndex].value = value;
this.emit("digital-read-" + pin, value);
}.bind(this));
}
},
},
PCF8575: {
REGISTER: {
value: {
ADDRESS: 0x20,
}
},
initialize: {
value: function(opts) {
var state = priv.get(this);
state.port = [0x00, 0x01];
state.gpio = [0x00, 0x00];
this.address = opts.address || this.REGISTER.ADDRESS;
opts.address = this.address;
this.io.i2cConfig(opts);
Object.assign(this.MODES, this.io.MODES);
for (var i = 0; i < 16; i++) {
this.pins.push({
supportedModes: [
this.MODES.INPUT,
this.MODES.OUTPUT
],
mode: 1,
value: 0,
report: 0,
analogChannel: 127
});
this.pinMode(i, this.MODES.OUTPUT);
this.digitalWrite(i, this.LOW);
}
// Set all pins low on initialization
this.io.i2cWrite(this.address, state.gpio);
this.name = "PCF8575";
this.isReady = true;
this.emit("connect");
this.emit("ready");
}
},
normalize: {
value: function(pin) {
return pin;
}
},
pinMode: {
value: function(pin, mode) {
var pinIndex = pin;
this.pins[pinIndex].mode = mode;
}
},
digitalWrite: {
value: function(pin, value) {
var state = priv.get(this);
var pinIndex = pin;
var port;
if (pin < 8) {
port = 0;
} else {
port = 1;
pin -= 8;
}
if (value === this.io.HIGH) {
state.gpio[port] |= 1 << pin;
} else {
state.gpio[port] &= ~(1 << pin);
}
this.pins[pinIndex].report = 0;
this.pins[pinIndex].value = value;
this.io.i2cWrite(this.address, state.gpio);
}
},
digitalRead: {
value: function(pin, callback) {
var pinIndex = pin;
var port;
if (pin < 8) {
port = 0;
} else {
port = 1;
pin -= 8;
}
this.pins[pinIndex].report = 1;
this.on("digital-read-" + pin, callback);
this.io.i2cRead(this.address, 2, function(data) {
var byte = data[port];
var value = byte >> pin & 0x01;
this.pins[pinIndex].value = value;
this.emit("digital-read-" + pin, value);
}.bind(this));
}
},
},
PCA9685: {
REGISTER: {
value: {
ADDRESS: 0x40,
MODE1: 0x00,
PRESCALE: 0xFE,
BASE: 0x06
}
},
initialize: {
value: function(opts) {
var state = priv.get(this);
// 7.3.5 PWM frequency PRE_SCALE
//
state.frequency = Board.constrain(opts.frequency || 1526, 24, 1526) * 0.9;
this.address = opts.address || this.REGISTER.ADDRESS;
this.pwmRange = opts.pwmRange || [0, 4095];
Object.defineProperties(this, {
prescale: {
get: function() {
// PCA9685 has an on-board 25MHz clock source
// 7.3.5 PWM frequency PRE_SCALE
return Math.round(25000000 / (4096 * state.frequency)) - 1;
}
},
frequency: {
get: function() {
return state.frequency;
}
}
});
opts.address = this.address;
this.io.i2cConfig(opts);
// Reset
this.io.i2cWriteReg(this.address, this.REGISTER.MODE1, 0x00);
// Sleep
this.io.i2cWriteReg(this.address, this.REGISTER.MODE1, 0x10);
// Set prescalar
this.io.i2cWriteReg(this.address, this.REGISTER.PRESCALE, this.prescale);
// Wake up
this.io.i2cWriteReg(this.address, this.REGISTER.MODE1, 0x00);
// Wait 5 nanoseconds for restart
nanosleep(5);
// Auto-increment
this.io.i2cWriteReg(this.address, this.REGISTER.MODE1, 0xa1);
Object.assign(this.MODES, this.io.MODES);
for (var i = 0; i < 16; i++) {
this.pins.push({
supportedModes: [
this.MODES.OUTPUT,
this.MODES.PWM,
this.MODES.SERVO,
],
mode: 0,
value: 0,
report: 0,
analogChannel: 127
});
this.pinMode(i, this.MODES.OUTPUT);
this.digitalWrite(i, this.LOW);
}
this.name = "PCA9685";
this.isReady = true;
this.emit("connect");
this.emit("ready");
}
},
normalize: {
value: function(pin) {
return this.io.name === "Tessel 2" ? (pin - 1) : pin;
}
},
pinMode: {
value: function(pin, mode) {
if (this.pins[pin] === undefined) {
throw new RangeError("Invalid PCA9685 pin: " + pin);
}
this.pins[pin].mode = mode;
}
},
digitalWrite: {
value: function(pin, value) {
this.pwmWrite(pin, value ? 255 : 0);
}
},
analogWrite: {
value: function(pin, value) {
this.pwmWrite(pin, value);
}
},
servoWrite: {
value: function(pin, value) {
value = Board.constrain(value, 0, 180);
var off = __.map(value, 0, 180, this.pwmRange[0] / 4, this.pwmRange[1] / 4);
this.io.i2cWrite(this.address, [
this.REGISTER.BASE + 4 * pin,
0, 0,
off, off >> 8
]);
}
},
pwmWrite: {
value: function(pin, value) {
if (this.pins[pin] === undefined) {
throw new RangeError("Invalid PCA9685 pin: " + pin);
}
value = Board.constrain(value, 0, 255);
var on = 0;
var off = this.pwmRange[1] * value / 255;
if (value === 0) {
// Special value for signal fully off.
on = 0;
off = 4096;
}
if (value === 255) {
// Special value for signal fully on.
on = 4096;
off = 0;
}
this.io.i2cWrite(this.address, [
this.REGISTER.BASE + 4 * pin,
on, on >> 8,
off, off >> 8
]);
this.pins[pin].value = value;
}
}
},
// http://www.nxp.com/documents/data_sheet/PCF8591.pdf
PCF8591: {
REGISTER: {
value: {
ADDRESS: 0x48,
}
},
initialize: {
value: function(opts) {
var state = priv.get(this);
state.control = 0x45;
state.reading = false;
this.address = opts.address || this.REGISTER.ADDRESS;
opts.address = this.address;
this.io.i2cConfig(opts);
Object.assign(this.MODES, this.io.MODES);
for (var i = 0; i < 4; i++) {
this.pins.push({
supportedModes: [
this.MODES.ANALOG
],
mode: 1,
value: 0,
report: 0,
analogChannel: i
});
}
this.analogPins.push(0, 1, 2, 3);
this.io.i2cWrite(this.address, state.control);
this.name = "PCF8591";
this.isReady = true;
this.emit("connect");
this.emit("ready");
}
},
normalize: {
value: function(pin) {
if (typeof pin === "string" && pin[0] === "A") {
return +pin.slice(1);
}
return pin;
}
},
pinMode: {
value: function(pin, mode) {
this.pins[pin].mode = mode;
}
},
analogRead: {
value: function(pin, callback) {
var state = priv.get(this);
var pinIndex = pin;
this.pins[pinIndex].report = 1;
this.on("analog-read-" + pin, callback);
// Since this operation will read all 4 pins,
// it only needs to be initiated once.
if (!state.reading) {
state.reading = true;
this.io.i2cRead(this.address, 4, function(data) {
var value;
for (var i = 0; i < 4; i++) {
value = data[i] << 2;
this.pins[i].value = value;
if (this.pins[i].report) {
this.emit("analog-read-" + pin, value);
}
}
}.bind(this));
}
}
},
},
MUXSHIELD2: {
initialize: {
value: function() {
var state = priv.get(this);
// _S[\d] (Digital: 2, 4, 6, 7)
state.select = [ 2, 4, 6, 7];
// _IOS[\d] (Digital: 10, 11, 12)
state.ios = [ null, 10, 11, 12 ];
// _IO[\d] (Analog In: "A0", "A1", "A2")
state.io = [ null, 14, 15, 16 ];
state.aio = [ null, 0, 1, 2 ];
state.outMode = 8;
state.pinMap = {};
state.rowReading = [false, false, false];
state.rowMode = [null, null, null];
// Each rowValue is a single uint16
state.rowValues = [ 0, 0, 0 ];
Object.assign(this.MODES, {
INPUT: 0,
OUTPUT: 1,
ANALOG: 2,
});
this.io.pinMode(state.select[0], this.MODES.OUTPUT);
this.io.pinMode(state.select[1], this.MODES.OUTPUT);
this.io.pinMode(state.select[2], this.MODES.OUTPUT);
this.io.pinMode(state.select[3], this.MODES.OUTPUT);
this.io.pinMode(state.outMode, this.MODES.OUTPUT);
this.io.digitalWrite(state.outMode, this.LOW);
this.io.pinMode(state.ios[1], this.MODES.OUTPUT);
this.io.pinMode(state.ios[2], this.MODES.OUTPUT);
this.io.pinMode(state.ios[3], this.MODES.OUTPUT);
var row = 1;
var mask = 16;
var index = 0;
for (var i = 0; i < 48; i++) {
var band = i & mask;
if (band === mask) {
row++;
mask *= 2;
index = 0;
}
state.pinMap["IO" + row + "-" + index] = i;
this.pins.push({
row: row,
index: index,
supportedModes: [
this.MODES.INPUT,
this.MODES.OUTPUT,
this.MODES.ANALOG,
],
mode: 1,
value: 0,
report: 0,
analogChannel: i
});
this.analogPins.push(i);
// TODO: Not sure about this?
// this.io.pinMode(i, this.MODES.OUTPUT);
// this.io.digitalWrite(i, this.LOW);
index++;
}
this.name = "MUXSHIELD2";
this.isReady = true;
this.emit("connect");
this.emit("ready");
}
},
normalize: {
value: function(pin) {
return pin;
}
},
pinMode: {
value: function(pin, mode) {
var state = priv.get(this);
var pinIndex = state.pinMap[pin];
if (pinIndex === undefined) {
throw new Error("MUXSHIELD2: Invalid Pin number or name: " + pin);
}
var row = this.pins[pinIndex].row;
var rowModeIndex = row - 1;
var rowMode = state.rowMode[rowModeIndex];
if (rowMode === mode) {
return this;
}
if (rowMode !== null && rowMode !== mode) {
throw new Error("MUXSHIELD2: Cannot set mixed modes per IO row.");
}
state.rowMode[rowModeIndex] = mode;
// MUXSHIELD2 Disallows mixing modes per row.
// Once a mode is set for a given pin in a given row,
// set all the pins in that row to the same mode.
for (var i = 0; i < 16; i++) {
this.pins[rowModeIndex + i].mode = mode;
}
var IO = state.io[row];
var IOS = state.ios[row];
if (mode === this.MODES.INPUT) {
// Read an analog input as digital
this.io.pinMode(IO, this.MODES.INPUT);
// this.io.digitalWrite(IOS, this.LOW);
}
if (mode === this.MODES.OUTPUT) {
this.io.pinMode(IO, this.MODES.OUTPUT);
this.io.digitalWrite(IOS, this.HIGH);
}
}
},
digitalWrite: {
value: function(pin, value) {
var state = priv.get(this);
var pinIndex = state.pinMap[pin];
if (pinIndex === undefined) {
throw new Error("MUXSHIELD2: Invalid Pin number or name: " + pin);
}
var row = this.pins[pinIndex].row;
var rowValueIndex = row - 1;
var rowValue = state.rowValues[rowValueIndex];
var ioPin = row - 1;
var offset = ioPin * 16;
var channel = pinIndex - offset;
if (value) {
rowValue |= 1 << channel;
} else {
rowValue &= ~(1 << channel);
}
this.io.digitalWrite(state.select[3], this.LOW);
this.io.digitalWrite(state.outMode, this.HIGH);
var S = state.select[row - 1];
var IO = state.io[row];
for (var i = 15; i >= 0; i--) {
this.io.digitalWrite(S, this.LOW);
this.io.digitalWrite(IO, (rowValue >> i) & 1);
this.io.digitalWrite(S, this.HIGH);
}
this.io.digitalWrite(state.select[3], this.HIGH);
this.io.digitalWrite(state.outMode, this.LOW);
this.pins[pinIndex].value = value;
state.rowValues[rowValueIndex] = rowValue;
}
},
digitalRead: {
value: function(pin, callback) {
this.ioRead("digital", pin, callback);
}
},
analogRead: {
value: function(pin, callback) {
this.ioRead("analog", pin, callback);
}
},
ioRead: {
value: function(type, pin, callback) {
var state = priv.get(this);
var pinIndex = state.pinMap[pin];
if (pinIndex === undefined) {
throw new Error("MUXSHIELD2: Invalid Pin number or name: " + pin);
}
this.on(type + "-read-" + pinIndex, callback);
var isAnalog = type === "analog" ? true : false;
var row = this.pins[pinIndex].row;
var rowReadingIndex = row - 1;
var offset = rowReadingIndex * 16;
var channel = pinIndex - offset;
this.pins[pinIndex].report = 1;
this.pins[pinIndex].channel = channel;
this.pins[pinIndex].ioPin = isAnalog ? rowReadingIndex : rowReadingIndex + 14;
var nextPinIndex = function() {
var startAt = nextPinIndex.lastPinIndex + 1;
for (var i = startAt; i < this.pins.length; i++) {
if (this.pins[i].report === 1) {
nextPinIndex.lastPinIndex = i;
return nextPinIndex.lastPinIndex;
}
}
nextPinIndex.lastPinIndex = -1;
return nextPinIndex();
}.bind(this);
nextPinIndex.lastPinIndex = -1;
var handler = function(value) {
var pinIndex = nextPinIndex.lastPinIndex;
var pin = this.pins[pinIndex];
this.emit(type + "-read-" + pinIndex, value);
this.io.removeListener(type + "-read-" + pin.ioPin, handler);
setTimeout(read, 10);
}.bind(this);
var read = function() {
var pinIndex = nextPinIndex();
var pin = this.pins[pinIndex];
this.select(pin.channel);
if (isAnalog) {
this.io.pinMode(pin.ioPin, this.io.MODES.ANALOG);
this.io.analogRead(pin.ioPin, handler);
} else {
this.io.digitalRead(pin.ioPin, handler);
}
}.bind(this);
if (!state.rowReading[rowReadingIndex]) {
state.rowReading[rowReadingIndex] = true;
read();
}
}
},
select: {
value: function(channel) {
var state = priv.get(this);
this.io.digitalWrite(state.outMode, this.LOW);
this.io.digitalWrite(state.select[0], (channel & 1));
this.io.digitalWrite(state.select[1], (channel & 3) >> 1);
this.io.digitalWrite(state.select[2], (channel & 7) >> 2);
this.io.digitalWrite(state.select[3], (channel & 15) >> 3);
}
}
},
};
Controllers.PCF8574A = Object.assign({}, Controllers.PCF8574, {
REGISTER: {
value: {
ADDRESS: 0x38,
}
},
});
var methods = Object.keys(Board.prototype);
Object.keys(Controllers).forEach(function(name) {
methods.forEach(function(key) {
if (Controllers[name][key] === undefined) {
Controllers[name][key] = {
writable: true,
configurable: true,
value: function() {
throw new Error("Expander:" + name + " does not support " + key);
}
};
}
});
});
function Expander(opts) {
if (!(this instanceof Expander)) {
return new Expander(opts);
}
Base.call(this);
var expander = null;
var addressError = "Expander cannot reuse an active address";
var controller = null;
var state = {};
var controllerValue;
if (typeof opts === "string") {
controllerValue = opts;
}
Board.Component.call(
this, opts = Board.Options(opts), { normalizePin: false, requestPin: false }
);
expander = active.get(this.address);
if (expander) {
if (this.bus && (expander.bus !== undefined && expander.bus === this.bus)) {
addressError += " on this bus";
}
throw new Error(addressError);
}
if (typeof opts.controller === "undefined" && controllerValue) {
opts.controller = controllerValue;
}
if (opts.controller && typeof opts.controller === "string") {
controller = Controllers[opts.controller.toUpperCase()];
} else {
controller = opts.controller;
}
if (controller == null) {
throw new Error("Expander expects a valid controller");
}
Board.Controller.call(this, controller, opts);
priv.set(this, state);
if (typeof this.initialize === "function") {
this.initialize(opts);
}
active.set(this.address, this);
}
util.inherits(Expander, Base);
Expander.get = function(required) {
if (!required.address || !required.controller) {
throw new Error("Expander.get(...) requires an address and controller");
}
if (required.address !== undefined) {
required.address = Number(required.address);
}
if (Number.isNaN(required.address)) {
throw new Error("Expander.get(...) expects address to be a number");
}
if (typeof required.controller !== "string") {
throw new Error("Expander.get(...) expects controller name to be a string");
}
// If no address was sent them assume the request wants
// to re-use an active Expander, by controller name.
// if (!required.address) {
// return Expander.byController(required.controller);
// }
var expander = active.get(required.address);
if (expander && (expander.name === required.controller.toUpperCase())) {
return expander;
}
return new Expander(required);
};
Expander.byAddress = function(address) {
return active.get(address);
};
Expander.byController = function(name) {
var controller;
active.forEach(function(value) {
if (value.name === name.toUpperCase()) {
controller = value;
}
});
return controller;
};
Expander.hasController = function(key) {
return Controllers[key] !== undefined;
};
if (IS_TEST_MODE) {
Expander.purge = function() {
priv.clear();
active.clear();
};
}
module.exports = Expander;