UNPKG

13.5 kBJavaScriptView Raw
1var Board = require("./board");
2var Collection = require("./mixins/collection");
3var EVS = require("./evshield");
4var Fn = require("./fn");
5var within = require("./mixins/within");
6var Emitter = require("events").EventEmitter;
7var util = require("util");
8var Pins = Board.Pins;
9
10var toFixed = Fn.toFixed;
11
12var priv = new Map();
13
14function analogHandler(opts, dataHandler) {
15 this.io.pinMode(this.pin, this.io.MODES.ANALOG);
16 this.io.analogRead(this.pin, function(data) {
17 dataHandler.call(this, data);
18 }.bind(this));
19}
20
21var Controllers = {
22 GP2Y0A21YK: {
23 // https://www.sparkfun.com/products/242
24 initialize: {
25 value: analogHandler
26 },
27 toCm: {
28 value: function(raw) {
29 return toFixed(12343.85 * Math.pow(raw, -1.15), 2);
30 }
31 }
32 },
33 GP2D120XJ00F: {
34 // https://www.sparkfun.com/products/8959
35 initialize: {
36 value: analogHandler
37 },
38 toCm: {
39 value: function(raw) {
40 return toFixed((2914 / (raw + 5)) - 1, 2);
41 }
42 }
43 },
44 GP2Y0A02YK0F: {
45 // https://www.sparkfun.com/products/8958
46 // 15cm - 150cm
47 initialize: {
48 value: analogHandler
49 },
50 toCm: {
51 value: function(raw) {
52 return toFixed(10650.08 * Math.pow(raw, -0.935) - 10, 2);
53 }
54 }
55 },
56 GP2Y0A41SK0F: {
57 // https://www.sparkfun.com/products/12728
58 // 4cm - 30cm
59 initialize: {
60 value: analogHandler
61 },
62 toCm: {
63 value: function(raw) {
64 return toFixed(2076 / (raw - 11), 2);
65 }
66 }
67 },
68 GP2Y0A710K0F: {
69 // https://www.adafruit.com/products/1568
70 // 100cm - 500cm
71 initialize: {
72 value: analogHandler
73 },
74 toCm: {
75 value: function(raw) {
76 return toFixed(3.8631e8 * Math.pow(raw, -2.463343), 0);
77 }
78 }
79 },
80 SRF10: {
81 initialize: {
82 value: function(opts, dataHandler) {
83
84 var address = opts.address || 0x70;
85 var msUntilNextRead = 65;
86
87 opts.address = address;
88
89 // Set up I2C data connection
90 this.io.i2cConfig(opts);
91
92 // Startup parameter
93 this.io.i2cWrite(address, [0x01, 16]);
94 this.io.i2cWrite(address, [0x02, 255]);
95
96 function read() {
97 this.io.i2cWrite(address, [0x02]);
98 this.io.i2cReadOnce(address, 2, function(data) {
99 dataHandler((data[0] << 8) | data[1]);
100 }.bind(this));
101
102 prime.call(this);
103 }
104
105 function prime() {
106 // 0x51 result in cm (centimeters)
107 this.io.i2cWrite(address, [0x00, 0x51]);
108
109 setTimeout(read.bind(this), msUntilNextRead);
110 }
111
112 prime.call(this);
113 }
114 },
115 toCm: {
116 value: function(raw) {
117 return raw;
118 }
119 }
120 },
121 // LV-MaxSonar-EZ
122 // LV-MaxSonar-EZ0
123 // LV-MaxSonar-EZ1
124 MB1000: {
125 initialize: {
126 value: analogHandler
127 },
128 toCm: {
129 value: function(raw) {
130 // From http://www.maxbotix.com/articles/032.htm
131 // ADC -> inches -> cm
132 //
133 //
134 // From intro in page 1
135 // 'The LV-MaxSonar-EZ detects objects
136 // from 0-inches to 254-inches (6.45-meters) and provides sonar range information from 6-
137 // inches out to 254-inches with 1-inch resolution.'
138 // 1inch = 2.54cm
139 return toFixed((raw / 2) * 2.54, 2);
140 }
141 }
142 },
143 // HRLV-MaxSonar-EZ0
144 MB1003: {
145 initialize: {
146 value: analogHandler
147 },
148 toCm: {
149 value: function(raw) {
150 // http://www.maxbotix.com/articles/032.htm
151 //
152 //
153 // From intro in page 1
154 // 'This sensor line features 1-mm resolution, .....'
155 return toFixed(raw / 2, 1);
156 }
157 }
158 },
159 // XL-MaxSonar-EZ3
160 MB1230: {
161 initialize: {
162 value: analogHandler
163 },
164 toCm: {
165 value: function(raw) {
166 // From http://www.maxbotix.com/articles/016.htm
167 // Using a Standard Range XL-MaxSonar with an ADC (Analog Digital Converter)
168 // When using a standard XL-MaxSonar with an ADC, verify that the sensor
169 // and micro-controller are referencing the same power supply and ground.
170 // This also assumes that the ADC being used is perfectly accurate.
171 // When reading the sensor's output with the scaling in centimeters with a
172 // 10-bit ADC, the range can be read directly off the ADC.
173 // If the ADC output reads 700 the range in centimeters is 700 centimeters.
174 //
175 // ADC -> cm
176 //
177 //
178 // From intro on page 1
179 // 'The MB1200 and MB1300 sensor series detects objects from 0-cm1
180 // to 765-cm (25.1 feet) or 1068cm (35 feet) (select models) and
181 // provide sonar range information from 20-cm2
182 // out to765-cm or 1068-cm (select models) with 1-cm resolution...'
183 return raw >> 0;
184 }
185 }
186 },
187 HCSR04: {
188 initialize: {
189 value: function(opts, dataHandler) {
190 var pinValue = opts.pinValue;
191 var msToNextRead = 65;
192
193 if (Pins.isFirmata(this)) {
194 if (typeof pinValue === "string" && pinValue[0] === "A") {
195 pinValue = this.io.analogPins[+pinValue.slice(1)];
196 }
197
198 pinValue = +pinValue;
199
200 if (this.io.analogPins.includes(pinValue)) {
201 opts.pin = pinValue;
202 }
203
204 this.pin = opts.pin;
205 }
206
207 // Private settings object
208 var settings = {
209 pin: opts.pin,
210 value: this.io.HIGH,
211 pulseOut: 5,
212 };
213
214 var read = function() {
215 this.io.pingRead(settings, function(microseconds) {
216 dataHandler(microseconds);
217 setTimeout(read, msToNextRead);
218 });
219 }.bind(this);
220
221 read();
222 }
223 },
224 toCm: {
225 value: function(raw) {
226 // https://www.sparkfun.com/products/13959
227 //
228 //
229 // From `Product features` paragraph at page 1
230 // 'Ultrasonic ranging module HC - SR04 provides 2cm - 400cm non-contact
231 // measurement function, the ranging accuracy can reach to 3mm'
232 return toFixed(raw / 29.1 / 2, 1);
233 }
234 }
235 },
236 HCSR04I2CBACKPACK: {
237 initialize: {
238 value: function(opts, datahandler) {
239 var address = opts.address || 0x27;
240 var msToNextRead = 90;
241
242 opts.address = address;
243
244 // set up IO connection:
245 this.io.i2cConfig(opts);
246
247 if (typeof opts.pin === "undefined") {
248 this.pin = 8;
249 }
250
251 var read = function() {
252 // Read the 2 data bytes from the "register" for the pin.
253 // When firmware is complete, update to:
254 // this.io.i2cReadOnce(address, this.pin, 2, function(data) {
255 this.io.i2cReadOnce(address, 2, function(data) {
256 datahandler((data[0] << 8) + data[1]);
257 setTimeout(read, msToNextRead);
258 });
259 }.bind(this);
260
261 read();
262 }
263 },
264 toCm: {
265 value: function(raw) {
266 return toFixed(raw / 29.1 / 2, 1);
267 }
268 }
269 },
270 LIDARLITE: {
271 REGISTER: {
272 value: {
273 ENABLE: 0x00,
274 READ: 0x8F,
275 }
276 },
277 initialize: {
278 value: function(opts, dataHandler) {
279 var address = 0x62;
280
281 opts.address = address;
282
283 this.io.i2cConfig(opts);
284
285 var read = function() {
286 this.io.i2cWrite(address, this.REGISTER.ENABLE, 0x04);
287 setTimeout(function() {
288 this.io.i2cReadOnce(address, this.REGISTER.READ, 2, function(bytes) {
289 // Step 5 of Quick Start Guide
290 dataHandler((bytes[0] << 8) + bytes[1]);
291 read();
292 });
293 }.bind(this), 20);
294 }.bind(this);
295
296 read();
297 }
298 },
299 toCm: {
300 value: function(raw) {
301
302 //
303 // From `Technology` paragraph at page 11
304 // 'Our patented, high"accuracy"
305 // measurement"technique"enables"distance"measurement"accuracy down"to 1cm..'
306 return raw >> 0;
307 }
308 }
309 },
310 EVS_EV3_IR: {
311 initialize: {
312 value: function(opts, dataHandler) {
313 var state = priv.get(this);
314
315 state.shield = EVS.shieldPort(opts.pin);
316
317 state.ev3 = new EVS(Object.assign(opts, {
318 io: this.io
319 }));
320 state.ev3.setup(state.shield, EVS.Type_EV3);
321 state.ev3.read(state.shield, EVS.Proximity, EVS.Proximity_Bytes, function(data) {
322 var value = data[0] | (data[1] << 8);
323
324 dataHandler(value);
325 });
326 }
327 },
328 toCm: {
329 value: function(raw) {
330 return raw;
331 }
332 }
333 },
334 EVS_EV3_US: {
335 initialize: {
336 value: function(opts, dataHandler) {
337 var state = priv.get(this);
338
339 state.shield = EVS.shieldPort(opts.pin);
340
341 state.ev3 = new EVS(Object.assign(opts, {
342 io: this.io
343 }));
344 state.ev3.setup(state.shield, EVS.Type_EV3);
345 state.ev3.read(state.shield, EVS.Proximity, EVS.Proximity_Bytes, function(data) {
346 var value = data[0] | (data[1] << 8);
347 dataHandler(value);
348 });
349 }
350 },
351 toCm: {
352 value: function(raw) {
353 return raw / 10;
354 }
355 }
356 },
357};
358
359// Sensor aliases
360// IR
361Controllers["2Y0A21"] = Controllers.GP2Y0A21YK;
362Controllers["2D120X"] = Controllers.GP2D120XJ00F;
363Controllers["2Y0A02"] = Controllers.GP2Y0A02YK0F;
364Controllers["0A41"] = Controllers.GP2Y0A41SK0F;
365Controllers["0A21"] = Controllers.GP2Y0A21YK;
366Controllers["0A02"] = Controllers.GP2Y0A02YK0F;
367Controllers["41SK0F"] = Controllers.GP2Y0A41SK0F;
368Controllers["21YK"] = Controllers.GP2Y0A21YK;
369Controllers["2YK0F"] = Controllers.GP2Y0A02YK0F;
370
371// Sonar
372Controllers.MB1010 = Controllers.MB1000;
373
374Controllers["LV-MaxSonar-EZ"] = Controllers.MB1000;
375Controllers["LV-MaxSonar-EZ0"] = Controllers.MB1000;
376Controllers["LV-MaxSonar-EZ1"] = Controllers.MB1010;
377Controllers["HRLV-MaxSonar-EZ0"] = Controllers.MB1003;
378Controllers["XL-MaxSonar-EZ3"] = Controllers.MB1230;
379
380// Ping
381[
382 "HC-SR04",
383 "SR04",
384 "SRF05",
385 "SRF06",
386 "PARALLAXPING",
387 "SEEEDPING",
388 "GROVEPING",
389 "PING_PULSE_IN",
390 "ULTRASONIC_PING",
391].forEach(function(alias) {
392 Controllers[alias] = Controllers.HCSR04;
393});
394
395// Ping/HCSR04 I2C Backpack
396[
397 "HCSR04-I2C-BACKPACK",
398 "HC-SR04-I2C-BACKPACK",
399 "SR04-I2C-BACKPACK",
400 "SR04I2CBACKPACK",
401 "PINGI2CBACKPACK",
402 "PING-I2C-BACKPACK",
403 "HCSR04_I2C_BACKPACK",
404 "HC_SR04_I2C_BACKPACK",
405 "SR04_I2C_BACKPACK",
406 "SR04I2CBACKPACK",
407 "PINGI2CBACKPACK",
408 "PING_I2C_BACKPACK",
409].forEach(function(alias) {
410 Controllers[alias] = Controllers.HCSR04I2CBACKPACK;
411});
412
413
414// LIDAR Lite
415Controllers["LIDAR-Lite"] = Controllers.LIDARLITE;
416
417
418/**
419 * Proximity
420 * @constructor
421 *
422 * five.Proximity("A0");
423 *
424 * five.Proximity({
425 * controller: "GP2Y0A41SK0F",
426 * pin: "A0",
427 * freq: 100
428 * });
429 *
430 *
431 * @param {Object} opts [description]
432 *
433 */
434
435function Proximity(opts) {
436
437 if (!(this instanceof Proximity)) {
438 return new Proximity(opts);
439 }
440
441 var controller = null;
442 var state = {};
443 var raw = 0;
444 var freq = opts.freq || 25;
445 var last = 0;
446 var pinValue = typeof opts === "object" ? opts.pin : opts;
447
448 Board.Component.call(
449 this, opts = Board.Options(opts)
450 );
451
452 if (typeof opts.controller === "string") {
453 controller = Controllers[opts.controller];
454 } else {
455 controller = opts.controller || Controllers["GP2Y0A21YK"];
456 }
457
458 Board.Controller.call(this, controller, opts);
459
460 if (!this.toCm) {
461 this.toCm = opts.toCm || function(x) {
462 return x;
463 };
464 }
465
466 priv.set(this, state);
467
468 Object.defineProperties(this, {
469 /**
470 * [read-only] Calculated centimeter value
471 * @property centimeters
472 * @type Number
473 */
474 centimeters: {
475 get: function() {
476 return this.toCm(raw);
477 }
478 },
479 cm: {
480 get: function() {
481 return this.centimeters;
482 }
483 },
484 /**
485 * [read-only] Calculated inch value
486 * @property inches
487 * @type Number
488 */
489 inches: {
490 get: function() {
491 return toFixed(this.centimeters * 0.39, 2);
492 }
493 },
494 in: {
495 get: function() {
496 return this.inches;
497 }
498 },
499 });
500
501 if (typeof this.initialize === "function") {
502 opts.pinValue = pinValue;
503 this.initialize(opts, function(data) {
504 raw = data;
505 });
506 }
507
508 setInterval(function() {
509 if (raw === undefined) {
510 return;
511 }
512
513 var data = {
514 cm: this.cm,
515 centimeters: this.centimeters,
516 in: this.in,
517 inches: this.inches
518 };
519
520 this.emit("data", data);
521
522 if (raw !== last) {
523 last = raw;
524 this.emit("change", data);
525 }
526 }.bind(this), freq);
527}
528
529Proximity.Controllers = [
530 "2Y0A21", "GP2Y0A21YK",
531 "2D120X", "GP2D120XJ00F",
532 "2Y0A02", "GP2Y0A02YK0F",
533 "OA41SK", "GP2Y0A41SK0F",
534 "0A21", "GP2Y0A21YK",
535 "0A02", "GP2Y0A02YK0F",
536];
537
538util.inherits(Proximity, Emitter);
539
540Object.assign(Proximity.prototype, within);
541
542
543/**
544 * new Proximity.Collection();
545 */
546
547Proximity.Collection = function(numsOrObjects) {
548 if (!(this instanceof Proximity.Collection)) {
549 return new Proximity.Collection(numsOrObjects);
550 }
551
552 Object.defineProperty(this, "type", {
553 value: Proximity
554 });
555
556 Collection.Emitter.call(this, numsOrObjects);
557};
558
559util.inherits(Proximity.Collection, Collection.Emitter);
560
561Collection.installMethodForwarding(
562 Proximity.Collection.prototype, Proximity.prototype
563);
564
565/* istanbul ignore else */
566if (!!process.env.IS_TEST_MODE) {
567 Proximity.Controllers = Controllers;
568 Proximity.purge = function() {
569 priv.clear();
570 };
571}
572
573module.exports = Proximity;