johnny-five-electron
Version:
Temporary fork to support Electron (to be deprecated)
330 lines (275 loc) • 8.81 kB
JavaScript
var Board = require("../lib/board");
var EVS = require("../lib/evshield");
var Emitter = require("events").EventEmitter;
var util = require("util");
var __ = require("../lib/fn");
var priv = new Map();
function analogHandler(opts, dataHandler) {
this.io.pinMode(this.pin, this.io.MODES.ANALOG);
this.io.analogRead(this.pin, function(data) {
dataHandler.call(this, data);
}.bind(this));
}
function pad(value, length) {
return Array(length - String(value).length + 1).join("0") + value;
}
var Controllers = {
// This is a placeholder...
DEFAULT: {
initialize: {
value: analogHandler
},
toRGB: {
value: function(raw) {
return raw;
}
}
},
EVS_EV3: {
initialize: {
value: function(opts, dataHandler) {
var state = priv.get(this);
if (opts.mode) {
opts.mode = opts.mode.toUpperCase();
}
state.mode = opts.mode === "RAW" ? EVS.Type_EV3_COLOR_RGBRAW : EVS.Type_EV3_COLOR;
state.bytes = state.mode === EVS.Type_EV3_COLOR_RGBRAW ? 6 : 2;
// Do not change the order of these items. They are listed such that the
// index corresponds to the color code produced by the EV3 color sensor.
// The range is very limited.
state.colors = [
[],
[0, 0, 0],
[0, 0, 255],
[0, 128, 0],
[255, 255, 0],
[255, 0, 0],
[255, 255, 255],
[139, 69, 19],
];
state.shield = EVS.shieldPort(opts.pin);
state.ev3 = new EVS(Object.assign(opts, { io: this.io }));
state.ev3.setup(state.shield, EVS.Type_EV3);
state.ev3.write(state.shield, 0x81 + state.shield.offset, state.mode);
state.ev3.read(state.shield, EVS.ColorMeasure, state.bytes, function(data) {
var value = "";
if (state.bytes === 2) {
value += String((data[0] | (data[1] << 8)) || 1);
} else {
for (var i = 0; i < 3; i++) {
value += pad(data[i * 2].toString(16), 2);
}
}
dataHandler(value);
});
}
},
toRGB: {
value: function(raw) {
var state = priv.get(this);
if (state.mode === EVS.Type_EV3_COLOR) {
return raw > 0 && raw < 8 ? state.colors[raw] : state.colors[0];
} else {
raw = String(raw);
return [0, 0, 0].map(function(zero, index) {
return parseInt(raw.slice(index * 2, index * 2 + 2), 16);
});
}
}
}
},
EVS_NXT: {
initialize: {
value: function(opts, dataHandler) {
var state = priv.get(this);
if (opts.mode) {
opts.mode = opts.mode.toUpperCase();
}
state.mode = opts.mode === "RAW" ? EVS.Type_NXT_COLOR_RGBRAW : EVS.Type_NXT_COLOR;
state.bytes = state.mode === EVS.Type_NXT_COLOR_RGBRAW ? 10 : 1;
if (state.mode === EVS.Type_NXT_COLOR_RGBRAW) {
throw new Error("Raw RGB is not currently supported for the NXT.");
}
// Do not change the order of these items. They are listed such that the
// index corresponds to the color code produced by the EV3 color sensor.
// The range is very limited.
state.colors = [
[],
[0, 0, 0],
[0, 0, 255],
[0, 128, 0],
[255, 255, 0],
[255, 0, 0],
[255, 255, 255],
];
state.shield = EVS.shieldPort(opts.pin);
state.ev3 = new EVS(Object.assign(opts, { io: this.io }));
state.ev3.setup(state.shield, EVS.Type_NXT_COLOR);
state.ev3.read(state.shield, 0x70 + state.shield.offset, state.bytes, function(data) {
var value = "";
if (state.bytes === 1) {
value += String(data[0]);
} else {
// One day I'll figure this out :|
// There is a lot of documentation that
// claims this is possible, but I couldn't
// figure out how to make sense of the
// data that's returned.
//
// http://www.mathworks.com/help/supportpkg/legomindstormsnxt/ref/legomindstormsnxtcolorsensor.html#zmw57dd0e700
// https://msdn.microsoft.com/en-us/library/ff631052.aspx
// http://www.lejos.org/nxt/nxj/api/lejos/nxt/ColorSensor.html
// http://www.robotc.net/forums/viewtopic.php?f=52&t=6939
// http://code.metager.de/source/xref/lejos/classes/src/lejos/nxt/SensorPort.java#calData
// http://code.metager.de/source/xref/lejos/classes/src/lejos/nxt/SensorPort.java#SP_MODE_INPUT
// http://code.metager.de/source/xref/lejos/classes/src/lejos/nxt/SensorPort.java#416
}
// if (data[4] !== 0) {
dataHandler(value);
// }
});
}
},
toRGB: {
value: function(raw) {
var state = priv.get(this);
if (state.mode === EVS.Type_NXT_COLOR) {
return raw > 0 && raw < 7 ? state.colors[raw] : state.colors[0];
} else {
raw = String(raw);
return [0, 0, 0].map(function(zero, index) {
return parseInt(raw.slice(index * 2, index * 2 + 2), 16);
});
}
}
}
},
ISL29125: {
// http://www.intersil.com/content/dam/Intersil/documents/isl2/isl29125.pdf
REGISTER: {
value: {
RESET: 0x00,
// mode/lux range
CONFIG1: 0x01,
// ir adjust/filtering
CONFIG2: 0x02,
// interrupt control
CONFIG3: 0x03,
// Same as "GREEN DATA - LOW BYTE"
READ: 0x09
}
},
initialize: {
value: function(opts, dataHandler) {
// Cannot change address, so all values const/closed.
var address = 0x44;
// TODO: make configs user "definable"
this.io.i2cConfig();
// Reset chip
this.io.i2cWriteReg(address, this.REGISTER.RESET, 0x46);
// RGB | 10K Lux | 12bits
this.io.i2cWriteReg(address, this.REGISTER.CONFIG1, 0x05 | 0x08 | 0x00);
// High adjust
this.io.i2cWriteReg(address, this.REGISTER.CONFIG2, 0x3F);
// No Interrupts
this.io.i2cWriteReg(address, this.REGISTER.CONFIG3, 0x00);
this.io.i2cRead(address, this.REGISTER.READ, 6, function(data) {
var value = "";
// Register order: GLSB, GMSB, RLSB, RMSB, BLSB, BMSB
var g = (data[1] << 8) | data[0];
var r = (data[3] << 8) | data[2];
var b = (data[5] << 8) | data[4];
var rgb = [r >> 2, g >> 2, b >> 2].map(function(value) {
return __.constrain(value, 0, 255);
});
for (var i = 0; i < 3; i++) {
value += pad(rgb[i].toString(16), 2);
}
dataHandler(value);
});
}
},
toRGB: {
value: function(raw) {
raw = String(raw);
return [0, 0, 0].map(function(zero, index) {
return parseInt(raw.slice(index * 2, index * 2 + 2), 16);
});
}
}
},
};
var colorNames = ["red", "green", "blue"];
/**
* Color
* @constructor
*
*/
function Color(opts) {
if (!(this instanceof Color)) {
return new Color(opts);
}
var controller = null;
var state = {};
var freq = opts.freq || 25;
var raw = 0;
var last = null;
Board.Device.call(
this, opts = Board.Options(opts)
);
if (typeof opts.controller === "string") {
controller = Controllers[opts.controller];
} else {
controller = opts.controller || Controllers.DEFAULT;
}
Object.defineProperties(this, controller);
if (!this.toRGB) {
this.toRGB = opts.toRGB || function(x) {
return x;
};
}
priv.set(this, state);
Object.defineProperties(this, {
value: {
get: function() {
return raw;
}
},
rgb: {
get: function() {
return this.toRGB(raw).reduce(function(accum, value, index) {
accum[colorNames[index]] = value;
return accum;
}, {});
}
}
});
if (typeof this.initialize === "function") {
this.initialize(opts, function(data) {
raw = data;
});
}
setInterval(function() {
if (raw === undefined) {
return;
}
var data = {
rgb: this.rgb,
};
this.emit("data", data);
if (raw !== last) {
last = raw;
this.emit("change", data);
}
}.bind(this), freq);
}
util.inherits(Color, Emitter);
Color.hexCode = function(rgb) {
if (rgb.red === undefined || rgb.green === undefined || rgb.blue === undefined) {
return null;
}
return rgb.length === 0 ? "unknown" : colorNames.reduce(function(accum, name) {
return accum += pad(rgb[name].toString(16), 2);
}, "");
};
module.exports = Color;