1 | var Board = require("./board");
|
2 | var EVS = require("./evshield");
|
3 | var Emitter = require("events").EventEmitter;
|
4 | var util = require("util");
|
5 | var Fn = require("./fn");
|
6 | var priv = new Map();
|
7 |
|
8 |
|
9 | function pad(value, length) {
|
10 | return Array(length - String(value).length + 1).join("0") + value;
|
11 | }
|
12 |
|
13 | var 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 |
|
27 |
|
28 |
|
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 |
|
92 |
|
93 |
|
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 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | }
|
130 |
|
131 |
|
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 |
|
158 | CONFIG1: 0x01,
|
159 |
|
160 | CONFIG2: 0x02,
|
161 |
|
162 | CONFIG3: 0x03,
|
163 |
|
164 | READ: 0x09
|
165 | }
|
166 | },
|
167 | initialize: {
|
168 | value: function(opts, dataHandler) {
|
169 |
|
170 | var address = opts.address || 0x44;
|
171 |
|
172 |
|
173 |
|
174 | opts.address = address;
|
175 |
|
176 | this.io.i2cConfig();
|
177 |
|
178 |
|
179 | this.io.i2cWriteReg(address, this.REGISTER.RESET, 0x46);
|
180 |
|
181 |
|
182 | this.io.i2cWriteReg(address, this.REGISTER.CONFIG1, 0x05 | 0x08 | 0x00);
|
183 |
|
184 |
|
185 | this.io.i2cWriteReg(address, this.REGISTER.CONFIG2, 0x3F);
|
186 |
|
187 |
|
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 |
|
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 |
|
222 | var colorNames = ["red", "green", "blue"];
|
223 |
|
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 | function 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 |
|
307 | util.inherits(Color, Emitter);
|
308 |
|
309 | Color.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 |
|
320 | if (!!process.env.IS_TEST_MODE) {
|
321 | Color.Controllers = Controllers;
|
322 | Color.purge = function() {
|
323 | priv.clear();
|
324 | };
|
325 | }
|
326 |
|
327 | module.exports = Color;
|