1 | var Board = require("./board");
|
2 | var EVS = require("./evshield");
|
3 | var within = require("./mixins/within");
|
4 | var Fn = require("./fn");
|
5 | var Emitter = require("events").EventEmitter;
|
6 | var util = require("util");
|
7 | var priv = new Map();
|
8 |
|
9 | var uint16 = Fn.uint16;
|
10 | var toFixed = Fn.toFixed;
|
11 |
|
12 | var Controllers = {
|
13 | DEFAULT: {
|
14 | initialize: {
|
15 | value: function(opts, dataHandler) {
|
16 | this.io.pinMode(this.pin, this.io.MODES.ANALOG);
|
17 | this.io.analogRead(this.pin, dataHandler);
|
18 | },
|
19 | },
|
20 | toIntensityLevel: {
|
21 | value: function(raw) {
|
22 | return toFixed(Fn.scale(raw, 0, 1023, 0, 100) / 100, 2);
|
23 | }
|
24 | }
|
25 | },
|
26 | EVS_EV3: {
|
27 | initialize: {
|
28 | value: function(opts, dataHandler) {
|
29 | var state = priv.get(this);
|
30 |
|
31 | if (opts.mode) {
|
32 | opts.mode = opts.mode.toUpperCase();
|
33 | }
|
34 |
|
35 | state.mode = opts.mode === "REFLECTED" ? EVS.Type_EV3_LIGHT_REFLECTED : EVS.Type_EV3_LIGHT;
|
36 |
|
37 | state.shield = EVS.shieldPort(opts.pin);
|
38 | state.ev3 = new EVS(Object.assign(opts, {
|
39 | io: this.io
|
40 | }));
|
41 | state.ev3.setup(state.shield, EVS.Type_EV3);
|
42 | state.ev3.write(state.shield, 0x81 + state.shield.offset, state.mode);
|
43 | state.ev3.read(state.shield, EVS.Light, EVS.Light_Bytes, function(data) {
|
44 | var value = data[0] | (data[1] << 8);
|
45 | dataHandler(value);
|
46 | });
|
47 | }
|
48 | },
|
49 | toIntensityLevel: {
|
50 | value: function(raw) {
|
51 | return toFixed(raw / 100, 2);
|
52 | }
|
53 | }
|
54 | },
|
55 | EVS_NXT: {
|
56 | initialize: {
|
57 | value: function(opts, dataHandler) {
|
58 | var state = priv.get(this);
|
59 |
|
60 | if (opts.mode) {
|
61 | opts.mode = opts.mode.toUpperCase();
|
62 | }
|
63 |
|
64 | state.mode = opts.mode === "REFLECTED" ? EVS.Type_NXT_LIGHT_REFLECTED : EVS.Type_NXT_LIGHT;
|
65 |
|
66 | state.shield = EVS.shieldPort(opts.pin);
|
67 | state.ev3 = new EVS(Object.assign(opts, {
|
68 | io: this.io
|
69 | }));
|
70 | state.ev3.setup(state.shield, state.mode);
|
71 | state.ev3.read(state.shield, state.shield.analog, EVS.Analog_Bytes, function(data) {
|
72 | var value = data[0] | (data[1] << 8);
|
73 | dataHandler(value);
|
74 | });
|
75 | }
|
76 | },
|
77 | toIntensityLevel: {
|
78 | value: function(raw) {
|
79 | return toFixed(Fn.scale(raw, 0, 1023, 100, 0) / 100, 2);
|
80 | }
|
81 | }
|
82 | },
|
83 |
|
84 | TSL2561: {
|
85 | ADDRESSES: {
|
86 | value: [0x29, 0x39, 0x49]
|
87 | },
|
88 | REGISTER: {
|
89 | value: {
|
90 | CONTROL: 0x00,
|
91 | TIMING: 0x01,
|
92 | READ: 0x2C,
|
93 | },
|
94 | },
|
95 |
|
96 | initialize: {
|
97 | value: function(opts, dataHandler) {
|
98 | var address = opts.address || 0x39;
|
99 | var command = function(byte) {
|
100 |
|
101 | return byte | 0x80;
|
102 | };
|
103 |
|
104 | opts.address = address;
|
105 |
|
106 | this.io.i2cConfig(opts);
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 | this.io.i2cWriteReg(address, command(this.REGISTER.CONTROL), 0x03);
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | var LUX_SCALE = 14;
|
127 | var RATIO_SCALE = 9;
|
128 |
|
129 |
|
130 |
|
131 | var K1T = 0x0040;
|
132 | var B1T = 0x01F2;
|
133 | var M1T = 0x01BE;
|
134 | var K2T = 0x0080;
|
135 | var B2T = 0x0214;
|
136 | var M2T = 0x02D1;
|
137 | var K3T = 0x00C0;
|
138 | var B3T = 0x023F;
|
139 | var M3T = 0x037B;
|
140 | var K4T = 0x0100;
|
141 | var B4T = 0x0270;
|
142 | var M4T = 0x03FE;
|
143 | var K5T = 0x0138;
|
144 | var B5T = 0x016F;
|
145 | var M5T = 0x01FC;
|
146 | var K6T = 0x019A;
|
147 | var B6T = 0x00D2;
|
148 | var M6T = 0x00FB;
|
149 | var K7T = 0x029A;
|
150 | var B7T = 0x0018;
|
151 | var M7T = 0x0012;
|
152 | var K8T = 0x029A;
|
153 | var B8T = 0x0000;
|
154 | var M8T = 0x0000;
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 | var GAIN_1X = 0x00;
|
193 | var GAIN_16X = 0x10;
|
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 | var TintMs = [
|
200 |
|
201 | 13,
|
202 |
|
203 | 101,
|
204 |
|
205 | 402,
|
206 | ];
|
207 |
|
208 | var TintDelayMs = [
|
209 |
|
210 | 15,
|
211 |
|
212 | 120,
|
213 |
|
214 | 450,
|
215 | ];
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 | var chScales = [
|
224 |
|
225 | 0x07517,
|
226 |
|
227 | 0x00FE7,
|
228 |
|
229 | 0x10000,
|
230 | ];
|
231 |
|
232 |
|
233 | var gain = GAIN_16X;
|
234 | var TintIndex = 0;
|
235 | var Tint = TintMs[TintIndex];
|
236 | var lux = 0;
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 | Object.defineProperties(this, {
|
251 | gain: {
|
252 | get: function() {
|
253 | return gain;
|
254 | },
|
255 | set: function(value) {
|
256 | if (value !== GAIN_1X && value !== GAIN_16X) {
|
257 | throw new RangeError("Invalid gain. Expected one of: 0, 16");
|
258 | }
|
259 | gain = value;
|
260 |
|
261 | this.io.i2cWriteReg(address, command(this.REGISTER.TIMING), TintIndex | gain);
|
262 | }
|
263 | },
|
264 | integration: {
|
265 | get: function() {
|
266 | return Tint;
|
267 | },
|
268 | set: function(value) {
|
269 | TintIndex = TintMs.indexOf(value);
|
270 |
|
271 | if (TintIndex === -1) {
|
272 | throw new RangeError("Invalid integration. Expected one of: 13, 101, 402");
|
273 | }
|
274 |
|
275 | Tint = value;
|
276 |
|
277 | this.io.i2cWriteReg(address, command(this.REGISTER.TIMING), TintIndex | gain);
|
278 | }
|
279 | },
|
280 | lux: {
|
281 | get: function() {
|
282 | return lux;
|
283 | }
|
284 | }
|
285 | });
|
286 |
|
287 |
|
288 |
|
289 |
|
290 | this.gain = gain;
|
291 | this.integration = Tint;
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 | var read = function() {
|
303 | setTimeout(function() {
|
304 |
|
305 |
|
306 | this.io.i2cReadOnce(address, command(this.REGISTER.READ), 4, function(data) {
|
307 |
|
308 |
|
309 | var ch0 = uint16(data[1], data[0]);
|
310 | var ch1 = uint16(data[3], data[2]);
|
311 | var b = 0;
|
312 | var m = 0;
|
313 |
|
314 |
|
315 |
|
316 | var chScale = chScales[TintIndex];
|
317 |
|
318 |
|
319 | if (!gain) {
|
320 | chScale = chScale << 4;
|
321 | }
|
322 |
|
323 |
|
324 |
|
325 | ch0 = (ch0 * chScale) >> 10;
|
326 | ch1 = (ch1 * chScale) >> 10;
|
327 |
|
328 | var ratio1 = 0;
|
329 |
|
330 | if (ch0) {
|
331 | ratio1 = (ch1 << (RATIO_SCALE + 1)) / ch0;
|
332 | }
|
333 |
|
334 | ratio1 = Math.round(ratio1);
|
335 |
|
336 | var ratio = (ratio1 + 1) >> 1;
|
337 |
|
338 | if (ratio >= 0 && ratio <= K1T) {
|
339 | b = B1T;
|
340 | m = M1T;
|
341 | } else if (ratio <= K2T) {
|
342 | b = B2T;
|
343 | m = M2T;
|
344 | } else if (ratio <= K3T) {
|
345 | b = B3T;
|
346 | m = M3T;
|
347 | } else if (ratio <= K4T) {
|
348 | b = B4T;
|
349 | m = M4T;
|
350 | } else if (ratio <= K5T) {
|
351 | b = B5T;
|
352 | m = M5T;
|
353 | } else if (ratio <= K6T) {
|
354 | b = B6T;
|
355 | m = M6T;
|
356 | } else if (ratio <= K7T) {
|
357 | b = B7T;
|
358 | m = M7T;
|
359 | } else if (ratio > K8T) {
|
360 | b = B8T;
|
361 | m = M8T;
|
362 | }
|
363 |
|
364 |
|
365 | var temp = (ch0 * b) - (ch1 * m);
|
366 |
|
367 | if (temp < 0) {
|
368 | temp = 0;
|
369 | }
|
370 |
|
371 | temp += 1 << (LUX_SCALE - 1);
|
372 |
|
373 |
|
374 |
|
375 | lux = temp >>> LUX_SCALE;
|
376 |
|
377 | dataHandler(lux);
|
378 | read();
|
379 | });
|
380 | }.bind(this), TintDelayMs[TintIndex]);
|
381 | }.bind(this);
|
382 |
|
383 | read();
|
384 | }
|
385 | },
|
386 | toLux: {
|
387 | value: function(raw) {
|
388 | return raw;
|
389 | },
|
390 | },
|
391 | toIntensityLevel: {
|
392 | value: function(raw) {
|
393 | return toFixed(Fn.scale(raw, 0, 17000, 0, 100) / 100, 2);
|
394 | },
|
395 | },
|
396 | },
|
397 | BH1750: {
|
398 |
|
399 |
|
400 | ADDRESSES: {
|
401 | value: [0x23, 0x5C]
|
402 | },
|
403 | initialize: {
|
404 | value: function(opts, dataHandler) {
|
405 | var address = opts.address || 0x23;
|
406 | var mode = opts.mode || 0x10;
|
407 | opts.address = address;
|
408 | this.io.i2cConfig(opts);
|
409 | this.io.i2cWrite(address, mode);
|
410 | var read = function() {
|
411 | setTimeout(function() {
|
412 | this.io.i2cReadOnce(address, 2, function(data) {
|
413 | var raw = data[0];
|
414 | raw <<= 8;
|
415 | raw |= data[1];
|
416 | dataHandler(raw);
|
417 | read();
|
418 | });
|
419 | }.bind(this), 120);
|
420 | }.bind(this);
|
421 | read();
|
422 | },
|
423 | },
|
424 | toLux: {
|
425 | value: function(raw) {
|
426 |
|
427 |
|
428 | return Math.round(raw / 1.2);
|
429 | },
|
430 | },
|
431 | toIntensityLevel: {
|
432 | value: function(raw) {
|
433 | return toFixed(Fn.scale(raw / 1.2, 0, 65535, 0, 100) / 100, 2);
|
434 | },
|
435 | },
|
436 | },
|
437 | };
|
438 |
|
439 | Controllers.ALSPT19 = Controllers["ALS-PT19"] = Controllers.DEFAULT;
|
440 |
|
441 |
|
442 |
|
443 |
|
444 |
|
445 |
|
446 |
|
447 |
|
448 | function Light(opts) {
|
449 |
|
450 | if (!(this instanceof Light)) {
|
451 | return new Light(opts);
|
452 | }
|
453 |
|
454 | var controller = null;
|
455 | var state = {};
|
456 | var raw = 0;
|
457 | var last = 0;
|
458 | var freq = opts.freq || 25;
|
459 |
|
460 | Board.Component.call(
|
461 | this, opts = Board.Options(opts)
|
462 | );
|
463 |
|
464 | if (typeof opts.controller === "string") {
|
465 | controller = Controllers[opts.controller];
|
466 | } else {
|
467 | controller = opts.controller || Controllers.DEFAULT;
|
468 | }
|
469 |
|
470 | Board.Controller.call(this, controller, opts);
|
471 |
|
472 | if (!this.toIntensityLevel) {
|
473 | this.toIntensityLevel = opts.toIntensityLevel || function(x) {
|
474 | return x;
|
475 | };
|
476 | }
|
477 |
|
478 | if (!this.toLux) {
|
479 | this.toLux = opts.toLux || function(x) {
|
480 | return x;
|
481 | };
|
482 | }
|
483 |
|
484 | Object.defineProperties(this, {
|
485 | value: {
|
486 | get: function() {
|
487 | return raw;
|
488 | },
|
489 | },
|
490 | level: {
|
491 | get: function() {
|
492 | return this.toIntensityLevel(raw);
|
493 | },
|
494 | },
|
495 | });
|
496 |
|
497 | priv.set(this, state);
|
498 |
|
499 |
|
500 | if (typeof this.initialize === "function") {
|
501 | this.initialize(opts, function(data) {
|
502 | raw = data;
|
503 | });
|
504 | }
|
505 |
|
506 | if (typeof this.lux === "undefined") {
|
507 | Object.defineProperty(this, "lux", {
|
508 | get: function() {
|
509 | return this.toLux(raw);
|
510 | },
|
511 | });
|
512 | }
|
513 |
|
514 | var data = {
|
515 | level: 0,
|
516 | lux: 0,
|
517 | };
|
518 |
|
519 | setInterval(function() {
|
520 | data.level = this.level;
|
521 | data.lux = this.lux;
|
522 |
|
523 | this.emit("data", data);
|
524 |
|
525 | if (raw !== last) {
|
526 | last = raw;
|
527 | this.emit("change", data);
|
528 | }
|
529 | }.bind(this), freq);
|
530 | }
|
531 |
|
532 | util.inherits(Light, Emitter);
|
533 |
|
534 | Object.assign(Light.prototype, within);
|
535 |
|
536 |
|
537 |
|
538 | if (!!process.env.IS_TEST_MODE) {
|
539 | Light.Controllers = Controllers;
|
540 | Light.purge = function() {
|
541 | priv.clear();
|
542 | };
|
543 | }
|
544 |
|
545 | module.exports = Light;
|
546 |
|