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!
296 lines (247 loc) • 8.04 kB
JavaScript
const Board = require("./board");
const EVS = require("./evshield");
const Emitter = require("./mixins/emitter");
const Fn = require("./fn");
const priv = new Map();
const Controllers = {
EVS_EV3: {
initialize: {
value(options, callback) {
const state = priv.get(this);
if (options.mode) {
options.mode = options.mode.toUpperCase();
}
state.mode = options.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(options.pin);
state.ev3 = new EVS(Object.assign(options, {
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, data => {
let value = "";
if (state.bytes === 2) {
value += String((data[0] | (data[1] << 8)) || 1);
} else {
for (let i = 0; i < 3; i++) {
value += data[i * 2].toString(16).padStart(2, "0");
}
}
callback(value);
});
}
},
toRGB: {
value(raw) {
const 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((zero, index) => parseInt(raw.slice(index * 2, index * 2 + 2), 16));
}
}
}
},
EVS_NXT: {
initialize: {
value(options, callback) {
const state = priv.get(this);
if (options.mode) {
options.mode = options.mode.toUpperCase();
}
state.mode = options.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(options.pin);
state.ev3 = new EVS(Object.assign(options, {
io: this.io
}));
state.ev3.setup(state.shield, EVS.Type_NXT_COLOR);
state.ev3.read(state.shield, 0x70 + state.shield.offset, state.bytes, data => {
let 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) {
callback(value);
// }
});
}
},
toRGB: {
value(raw) {
const 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((zero, index) => parseInt(raw.slice(index * 2, index * 2 + 2), 16));
}
}
}
},
ISL29125: {
ADDRESSES: {
value: [0x44]
},
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(options, callback) {
const { Drivers } = require("./sip");
const address = Drivers.addressResolver(this, options);
this.io.i2cConfig(options);
// 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, data => {
let value = "";
// Register order: GLSB, GMSB, RLSB, RMSB, BLSB, BMSB
const g = (data[1] << 8) | data[0];
const r = (data[3] << 8) | data[2];
const b = (data[5] << 8) | data[4];
const rgb = [r >> 2, g >> 2, b >> 2].map(value => Fn.constrain(value, 0, 255));
for (let i = 0; i < 3; i++) {
value += rgb[i].toString(16).padStart(2, "0");
}
callback(value);
});
}
},
toRGB: {
value(raw) {
raw = String(raw);
return [0, 0, 0].map((zero, index) => parseInt(raw.slice(index * 2, index * 2 + 2), 16));
}
}
},
};
const colorNames = ["red", "green", "blue"];
/**
* Color
* @constructor
*
*/
class Color extends Emitter {
constructor(options) {
super();
Board.Component.call(
this, options = Board.Options(options)
);
Board.Controller.call(this, Controllers, options);
const state = {};
const freq = options.freq || 25;
let raw = 0;
let last = null;
priv.set(this, state);
if (!this.toRGB) {
this.toRGB = options.toRGB || (x => x);
}
Object.defineProperties(this, {
value: {
get() {
return raw;
}
},
rgb: {
get() {
return this.toRGB(raw).reduce((accum, value, index) => {
accum[colorNames[index]] = value;
return accum;
}, {});
}
}
});
if (typeof this.initialize === "function") {
this.initialize(options, data => {
raw = data;
});
}
setInterval(() => {
if (raw === undefined) {
return;
}
const data = {
rgb: this.rgb,
};
this.emit("data", data);
if (raw !== last) {
last = raw;
this.emit("change", data);
}
}, freq);
}
static hexCode(rgb) {
if (rgb.red === undefined || rgb.green === undefined || rgb.blue === undefined) {
return null;
}
return rgb.length === 0 ? "unknown" : colorNames.reduce((accum, name) => accum += rgb[name].toString(16).padStart(2, "0"), "");
}
}
/* istanbul ignore else */
if (!!process.env.IS_TEST_MODE) {
Color.Controllers = Controllers;
Color.purge = () => {
priv.clear();
};
}
module.exports = Color;