UNPKG

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