UNPKG

8.64 kBJavaScriptView Raw
1var Board = require("./board");
2var EVS = require("./evshield");
3var Emitter = require("events").EventEmitter;
4var util = require("util");
5var Fn = require("./fn");
6var priv = new Map();
7
8
9function pad(value, length) {
10 return Array(length - String(value).length + 1).join("0") + value;
11}
12
13var Controllers = {
14 EVS_EV3: {
15 initialize: {
16 value: function(opts, dataHandler) {
17 var state = priv.get(this);
18
19 if (opts.mode) {
20 opts.mode = opts.mode.toUpperCase();
21 }
22
23 state.mode = opts.mode === "RAW" ? EVS.Type_EV3_COLOR_RGBRAW : EVS.Type_EV3_COLOR;
24 state.bytes = state.mode === EVS.Type_EV3_COLOR_RGBRAW ? 6 : 2;
25
26 // Do not change the order of these items. They are listed such that the
27 // index corresponds to the color code produced by the EV3 color sensor.
28 // The range is very limited.
29 state.colors = [
30 [],
31 [0, 0, 0],
32 [0, 0, 255],
33 [0, 128, 0],
34 [255, 255, 0],
35 [255, 0, 0],
36 [255, 255, 255],
37 [139, 69, 19],
38 ];
39
40 state.shield = EVS.shieldPort(opts.pin);
41 state.ev3 = new EVS(Object.assign(opts, {
42 io: this.io
43 }));
44
45 state.ev3.setup(state.shield, EVS.Type_EV3);
46 state.ev3.write(state.shield, 0x81 + state.shield.offset, state.mode);
47 state.ev3.read(state.shield, EVS.ColorMeasure, state.bytes, function(data) {
48 var value = "";
49 if (state.bytes === 2) {
50 value += String((data[0] | (data[1] << 8)) || 1);
51 } else {
52 for (var i = 0; i < 3; i++) {
53 value += pad(data[i * 2].toString(16), 2);
54 }
55 }
56 dataHandler(value);
57 });
58 }
59 },
60 toRGB: {
61 value: function(raw) {
62 var state = priv.get(this);
63
64 if (state.mode === EVS.Type_EV3_COLOR) {
65 return raw > 0 && raw < 8 ? state.colors[raw] : state.colors[0];
66 } else {
67 raw = String(raw);
68 return [0, 0, 0].map(function(zero, index) {
69 return parseInt(raw.slice(index * 2, index * 2 + 2), 16);
70 });
71 }
72 }
73 }
74 },
75 EVS_NXT: {
76 initialize: {
77 value: function(opts, dataHandler) {
78 var state = priv.get(this);
79
80 if (opts.mode) {
81 opts.mode = opts.mode.toUpperCase();
82 }
83
84 state.mode = opts.mode === "RAW" ? EVS.Type_NXT_COLOR_RGBRAW : EVS.Type_NXT_COLOR;
85 state.bytes = state.mode === EVS.Type_NXT_COLOR_RGBRAW ? 10 : 1;
86
87 if (state.mode === EVS.Type_NXT_COLOR_RGBRAW) {
88 throw new Error("Raw RGB is not currently supported for the NXT.");
89 }
90
91 // Do not change the order of these items. They are listed such that the
92 // index corresponds to the color code produced by the EV3 color sensor.
93 // The range is very limited.
94 state.colors = [
95 [],
96 [0, 0, 0],
97 [0, 0, 255],
98 [0, 128, 0],
99 [255, 255, 0],
100 [255, 0, 0],
101 [255, 255, 255],
102 ];
103
104 state.shield = EVS.shieldPort(opts.pin);
105 state.ev3 = new EVS(Object.assign(opts, {
106 io: this.io
107 }));
108 state.ev3.setup(state.shield, EVS.Type_NXT_COLOR);
109 state.ev3.read(state.shield, 0x70 + state.shield.offset, state.bytes, function(data) {
110 var value = "";
111
112 if (state.bytes === 1) {
113 value += String(data[0]);
114 } else {
115
116 // One day I'll figure this out :|
117 // There is a lot of documentation that
118 // claims this is possible, but I couldn't
119 // figure out how to make sense of the
120 // data that's returned.
121 //
122 // http://www.mathworks.com/help/supportpkg/legomindstormsnxt/ref/legomindstormsnxtcolorsensor.html#zmw57dd0e700
123 // https://msdn.microsoft.com/en-us/library/ff631052.aspx
124 // http://www.lejos.org/nxt/nxj/api/lejos/nxt/ColorSensor.html
125 // http://www.robotc.net/forums/viewtopic.php?f=52&t=6939
126 // http://code.metager.de/source/xref/lejos/classes/src/lejos/nxt/SensorPort.java#calData
127 // http://code.metager.de/source/xref/lejos/classes/src/lejos/nxt/SensorPort.java#SP_MODE_INPUT
128 // http://code.metager.de/source/xref/lejos/classes/src/lejos/nxt/SensorPort.java#416
129 }
130
131 // if (data[4] !== 0) {
132 dataHandler(value);
133 // }
134 });
135 }
136 },
137 toRGB: {
138 value: function(raw) {
139 var state = priv.get(this);
140
141 if (state.mode === EVS.Type_NXT_COLOR) {
142 return raw > 0 && raw < 7 ? state.colors[raw] : state.colors[0];
143 } else {
144 raw = String(raw);
145 return [0, 0, 0].map(function(zero, index) {
146 return parseInt(raw.slice(index * 2, index * 2 + 2), 16);
147 });
148 }
149 }
150 }
151 },
152 ISL29125: {
153
154 REGISTER: {
155 value: {
156 RESET: 0x00,
157 // mode/lux range
158 CONFIG1: 0x01,
159 // ir adjust/filtering
160 CONFIG2: 0x02,
161 // interrupt control
162 CONFIG3: 0x03,
163 // Same as "GREEN DATA - LOW BYTE"
164 READ: 0x09
165 }
166 },
167 initialize: {
168 value: function(opts, dataHandler) {
169 // Cannot change address, so all values const/closed.
170 var address = opts.address || 0x44;
171
172 // TODO: make configs user "definable"
173
174 opts.address = address;
175
176 this.io.i2cConfig();
177
178 // Reset chip
179 this.io.i2cWriteReg(address, this.REGISTER.RESET, 0x46);
180
181 // RGB | 10K Lux | 12bits
182 this.io.i2cWriteReg(address, this.REGISTER.CONFIG1, 0x05 | 0x08 | 0x00);
183
184 // High adjust
185 this.io.i2cWriteReg(address, this.REGISTER.CONFIG2, 0x3F);
186
187 // No Interrupts
188 this.io.i2cWriteReg(address, this.REGISTER.CONFIG3, 0x00);
189
190 this.io.i2cRead(address, this.REGISTER.READ, 6, function(data) {
191 var value = "";
192
193 // Register order: GLSB, GMSB, RLSB, RMSB, BLSB, BMSB
194 var g = (data[1] << 8) | data[0];
195 var r = (data[3] << 8) | data[2];
196 var b = (data[5] << 8) | data[4];
197
198 var rgb = [r >> 2, g >> 2, b >> 2].map(function(value) {
199 return Fn.constrain(value, 0, 255);
200 });
201
202 for (var i = 0; i < 3; i++) {
203 value += pad(rgb[i].toString(16), 2);
204 }
205
206 dataHandler(value);
207 });
208 }
209 },
210 toRGB: {
211 value: function(raw) {
212 raw = String(raw);
213 return [0, 0, 0].map(function(zero, index) {
214 return parseInt(raw.slice(index * 2, index * 2 + 2), 16);
215 });
216 }
217 }
218 },
219};
220
221
222var colorNames = ["red", "green", "blue"];
223
224
225/**
226 * Color
227 * @constructor
228 *
229 */
230
231function Color(opts) {
232
233 if (!(this instanceof Color)) {
234 return new Color(opts);
235 }
236
237 var controller = null;
238 var state = {};
239 var freq = opts.freq || 25;
240 var raw = 0;
241 var last = null;
242
243 Board.Component.call(
244 this, opts = Board.Options(opts)
245 );
246
247 if (typeof opts.controller === "string") {
248 controller = Controllers[opts.controller];
249 } else {
250 controller = opts.controller;
251 }
252
253 if (controller == null) {
254 throw new Error("Color expects a valid controller");
255 }
256
257 priv.set(this, state);
258
259 Board.Controller.call(this, controller, opts);
260
261 if (!this.toRGB) {
262 this.toRGB = opts.toRGB || function(x) {
263 return x;
264 };
265 }
266
267 Object.defineProperties(this, {
268 value: {
269 get: function() {
270 return raw;
271 }
272 },
273 rgb: {
274 get: function() {
275 return this.toRGB(raw).reduce(function(accum, value, index) {
276 accum[colorNames[index]] = value;
277 return accum;
278 }, {});
279 }
280 }
281 });
282
283 if (typeof this.initialize === "function") {
284 this.initialize(opts, function(data) {
285 raw = data;
286 });
287 }
288
289 setInterval(function() {
290 if (raw === undefined) {
291 return;
292 }
293
294 var data = {
295 rgb: this.rgb,
296 };
297
298 this.emit("data", data);
299
300 if (raw !== last) {
301 last = raw;
302 this.emit("change", data);
303 }
304 }.bind(this), freq);
305}
306
307util.inherits(Color, Emitter);
308
309Color.hexCode = function(rgb) {
310 if (rgb.red === undefined || rgb.green === undefined || rgb.blue === undefined) {
311 return null;
312 }
313 return rgb.length === 0 ? "unknown" : colorNames.reduce(function(accum, name) {
314 return accum += pad(rgb[name].toString(16), 2);
315 }, "");
316};
317
318
319/* istanbul ignore else */
320if (!!process.env.IS_TEST_MODE) {
321 Color.Controllers = Controllers;
322 Color.purge = function() {
323 priv.clear();
324 };
325}
326
327module.exports = Color;