UNPKG

80.2 kBJavaScriptView Raw
1var Board = require("./board");
2var Emitter = require("events").EventEmitter;
3var util = require("util");
4var Fn = require("./fn");
5var int16 = Fn.int16;
6var uint16 = Fn.uint16;
7var uint24 = Fn.uint24;
8var s32 = Fn.s32;
9var u32 = Fn.u32;
10
11var priv = new Map();
12var activeDrivers = new Map();
13
14// TODO: make real const
15var ACCELEROMETER = "accelerometer";
16var ALTIMETER = "altimeter";
17var BAROMETER = "barometer";
18var GYRO = "gyro";
19var HYGROMETER = "hygrometer";
20var MAGNETOMETER = "magnetometer";
21var ORIENTATION = "orientation";
22var THERMOMETER = "thermometer";
23
24
25function Components(controller, options) {
26 var state = priv.get(this);
27 var descriptors = Object.create(null);
28
29 this.components.forEach(function(component) {
30
31 // TODO: Can this be put inside the get accessor?
32 // - Lazy init?
33 state[component] = new Components[component](
34 Object.assign({
35 controller: options.controller || controller,
36 freq: options.freq,
37 board: this.board,
38 }, options)
39 );
40
41 descriptors[component] = {
42 get: function() {
43 return state[component];
44 }
45 };
46
47 if (backwardCompatibilityGarbageHacks[component]) {
48 descriptors[backwardCompatibilityGarbageHacks[component]] = descriptors[component];
49 }
50 });
51
52 Object.defineProperties(this, descriptors);
53}
54
55Components.accelerometer = require("./accelerometer");
56Components.altimeter = require("./altimeter");
57Components.barometer = require("./barometer");
58Components.gyro = require("./gyro");
59Components.hygrometer = require("./hygrometer");
60Components.magnetometer = require("./compass");
61Components.orientation = require("./orientation");
62Components.thermometer = require("./thermometer");
63
64var backwardCompatibilityGarbageHacks = {
65 thermometer: "temperature",
66};
67
68var Drivers = {
69 SHT31D: {
70 ADDRESSES: {
71 value: [0x44]
72 },
73 REGISTER: {
74 value: {
75 // Table 13
76 SOFT_RESET: 0x30A2,
77 // Table 8
78 MEASURE_HIGH_REPEATABILITY: 0x2400,
79 }
80 },
81 initialize: {
82 value: function(board, opts) {
83 var READLENGTH = 6;
84 var io = board.io;
85 var address = opts.address || this.ADDRESSES[0];
86
87 opts.address = address;
88
89 io.i2cConfig(opts);
90
91 io.i2cWrite(address, [
92 // Page 12, Table 13
93 this.REGISTER.SOFT_RESET >> 8,
94 this.REGISTER.SOFT_RESET & 0xFF,
95 ]);
96
97 var computed = {
98 temperature: null,
99 humidity: null,
100 };
101
102 // temp msb, temp lsb, temp CRC, humidity msb, humidity lsb, humidity CRC
103 var readCycle = function() {
104 // Page 10, Table 8
105 // Send high repeatability measurement command
106 io.i2cWrite(address, [
107 this.REGISTER.MEASURE_HIGH_REPEATABILITY >> 8,
108 this.REGISTER.MEASURE_HIGH_REPEATABILITY & 0xFF,
109 ]);
110
111 setTimeout(function() {
112 io.i2cReadOnce(address, READLENGTH, function(data) {
113 computed.temperature = uint16(data[0], data[1]);
114 computed.humidity = uint16(data[3], data[4]);
115 this.emit("data", computed);
116 readCycle();
117 }.bind(this));
118 }.bind(this), 16);
119 }.bind(this);
120
121 readCycle();
122 }
123 },
124 identifier: {
125 value: function(opts) {
126 var address = opts.address || Drivers.SHT31D.ADDRESSES.value[0];
127 return "sht-31d-" + address;
128 }
129 }
130 },
131
132 HTU21D: {
133 ADDRESSES: {
134 value: [0x40]
135 },
136 REGISTER: {
137 value: {
138 HUMIDITY: 0xE5,
139 TEMPERATURE: 0xE3,
140 SOFT_RESET: 0xFE,
141 }
142 },
143 initialize: {
144 value: function(board, opts) {
145 var io = board.io;
146 var address = opts.address || this.ADDRESSES[0];
147
148 opts.address = address;
149
150 // The "no hold" measurement requires waiting
151 // _at least_ 22ms between register write and
152 // register read. Delay is measured in μs:
153 // 22ms = 22000μs; recommend 50ms = 50000μs
154 opts.delay = 50000;
155
156 io.i2cConfig(opts);
157 io.i2cWrite(address, this.REGISTER.SOFT_RESET);
158
159 var computed = {
160 temperature: null,
161 humidity: null,
162 };
163
164 var cycle = 0;
165 var readCycle = function() {
166 // Despite the registers being back to back, the HTU21D
167 // does not like when 5 bytes are requested, so we put
168 // the two data sources on their own read channels.
169 var isTemperatureCycle = cycle === 0;
170 var register = isTemperatureCycle ? this.REGISTER.TEMPERATURE : this.REGISTER.HUMIDITY;
171
172 io.i2cReadOnce(address, register, 2, function(data) {
173 if (isTemperatureCycle) {
174 computed.temperature = uint16(data[0], data[1]);
175 } else {
176 computed.humidity = uint16(data[0], data[1]);
177 }
178
179 if (++cycle === 2) {
180 cycle = 0;
181 this.emit("data", computed);
182 }
183
184 readCycle();
185 }.bind(this));
186 }.bind(this);
187
188 readCycle();
189 }
190 },
191 identifier: {
192 value: function(opts) {
193 var address = opts.address || Drivers.HTU21D.ADDRESSES.value[0];
194 return "htu-s1d-" + address;
195 }
196 }
197 },
198 HIH6130: {
199 ADDRESSES: {
200 value: [0x27]
201 },
202 initialize: {
203 value: function(board, opts) {
204 var io = board.io;
205 var address = opts.address || this.ADDRESSES[0];
206
207 opts.address = address;
208
209 io.i2cConfig(opts);
210
211 var computed = {
212 humidity: null,
213 temperature: null,
214 };
215
216 var delay = 36.65;
217
218 var measureCycle = function() {
219 // The most common use cases involve continuous
220 // sampling of sensor data, so that's what this
221 // controller-driver will provide.
222 io.i2cWrite(address, 0xA0, [0x00, 0x00]);
223
224 setTimeout(function() {
225 io.i2cWrite(address, 0x80, [0x00, 0x00]);
226 io.i2cReadOnce(address, 4, function(data) {
227 // Page 2, Figure 4.
228 // Humidity and Temperature Data Fetch, Four Byte Data Read
229 // B7:6 Contain status bits
230 var status = data[0] >> 6;
231 // Mask out B7:6 status bits from H MSB
232 computed.humidity = int16(data[0] & 0x3F, data[1]);
233 // Shift off B1:0 (which are empty)
234 computed.temperature = int16(data[2], data[3] >> 2);
235
236 // Page 3, 2.6 Status Bits
237 //
238 // 0 0 Normal
239 // 0 1 Stale
240 // 1 0 Command Mode
241 // 1 1 Diagnostic Condition
242 //
243 // When the two status bits read "01", "stale" data is
244 // indicated. This means that the data that already
245 // exists in the sensor's output buffer has already
246 // been fetched by the Master, and has not yet been
247 // updated with the next data from the current measurement
248 // cycle. This can happen when the Master polls the
249 // data quicker than the sensor can update the output buffer.
250 if (status === 0) {
251 delay--;
252 }
253
254 if (status === 1) {
255 delay++;
256 }
257
258 this.emit("data", computed);
259
260 measureCycle();
261 }.bind(this));
262 // Page 3
263 // 3.0 Measurement Cycle
264 // The measurement cycle duration is typically
265 // 36.65 ms for temperature and humidity readings.
266 }.bind(this), delay);
267 }.bind(this);
268
269 measureCycle();
270 }
271 },
272 identifier: {
273 value: function(opts) {
274 var address = opts.address || Drivers.HIH6130.ADDRESSES.value[0];
275 return "hih6130-" + address;
276 }
277 }
278 },
279 DHT_I2C_NANO_BACKPACK: {
280 ADDRESSES: {
281 value: [0x0A]
282 },
283 REGISTER: {
284 value: {
285 READ: 0x00,
286 }
287 },
288 initialize: {
289 value: function(board, opts) {
290 var io = board.io;
291 var address = opts.address || this.ADDRESSES[0];
292 // Correspond to firmware variables
293 var dhtPin = 2;
294 var dhtType = 11;
295
296 opts.address = address;
297
298 io.i2cConfig(opts);
299
300 var dhtVariantExec = /(\d{2})/.exec(opts.controller);
301 var dhtVariant = dhtVariantExec && dhtVariantExec.length && dhtVariantExec[0];
302
303 if (dhtVariant) {
304 dhtType = +dhtVariant;
305
306 if (Number.isNaN(dhtType)) {
307 dhtType = 11;
308 }
309 }
310
311 var computed = {
312 temperature: null,
313 humidity: null,
314 };
315
316 io.i2cWrite(address, [dhtPin, dhtType]);
317 io.i2cRead(address, 4, function(data) {
318 computed.humidity = int16(data[0], data[1]);
319 computed.temperature = int16(data[2], data[3]);
320 this.emit("data", computed);
321 }.bind(this));
322 }
323 },
324 identifier: {
325 value: function(opts) {
326 var address = opts.address || Drivers.DHT_I2C_NANO_BACKPACK.ADDRESSES.value[0];
327 return "dht_i2c_nano_backpack-" + address;
328 }
329 }
330 },
331 MPU6050: {
332 ADDRESSES: {
333 value: [0x68, 0x69]
334 },
335 REGISTER: {
336 value: {
337 SETUP: [0x6B, 0x00],
338 READ: 0x3B
339 }
340 },
341 initialize: {
342 value: function(board, opts) {
343 var READLENGTH = 14;
344 var io = board.io;
345 var address = opts.address || this.ADDRESSES[0];
346
347 opts.address = address;
348
349 var computed = {
350 accelerometer: {},
351 temperature: {},
352 gyro: {}
353 };
354
355 io.i2cConfig(opts);
356 io.i2cWrite(address, this.REGISTER.SETUP);
357
358 io.i2cRead(address, this.REGISTER.READ, READLENGTH, function(data) {
359 computed.accelerometer = {
360 x: int16(data[0], data[1]),
361 y: int16(data[2], data[3]),
362 z: int16(data[4], data[5])
363 };
364
365 computed.temperature = int16(data[6], data[7]);
366
367 computed.gyro = {
368 x: int16(data[8], data[9]),
369 y: int16(data[10], data[11]),
370 z: int16(data[12], data[13])
371 };
372
373 this.emit("data", computed);
374 }.bind(this));
375 },
376 },
377 identifier: {
378 value: function(opts) {
379 var address = opts.address || Drivers.MPU6050.ADDRESSES.value[0];
380 return "mpu-6050-" + address;
381 }
382 }
383 },
384 BNO055: {
385 ADDRESSES: {
386 value: [0x28, 0x29]
387 },
388 REGISTER: {
389 value: {
390 //
391 // 4.2.1 Register map Page 0
392 //
393 READ: {
394 /*
395 All motion data is in the following order:
396 X LSB
397 X MSB
398 Y LSB
399 Y MSB
400 Z LSB
401 Z MSB
402
403 The quarternion data is WXYZ
404 W LSB
405 W MSB
406 X LSB
407 X MSB
408 Y LSB
409 Y MSB
410 Z LSB
411 Z MSB
412
413 */
414
415 // m/s^2 by default
416 ACCEL: 0x08, // X LSB
417
418 // ? by default
419 MAG: 0x0E, // X LSB
420
421 // dps by default
422 GYRO: 0x14, // X LSB
423
424 //euler angles - degrees
425 EULER: 0x1A, // heading LSB
426
427 //quarternion
428 QUARTERNION: 0x20, // W LSB
429
430 // °C by default
431 TEMP: 0x34,
432 },
433
434 LENGTH: {
435 ACCEL: 6,
436 MAG: 6,
437 GYRO: 6,
438 EULER: 6,
439 QUARTERNION: 8,
440 TEMP: 1,
441 },
442
443 OPR_MODE_ADDR: 0x3D,
444 OPR_MODES: {
445 CONFIG: 0x00,
446 ACCONLY: 0x01,
447 MAGONLY: 0x02,
448 GYRONLY: 0x03,
449 ACCMAG: 0x04,
450 ACCGYRO: 0x05,
451 MAGGYRO: 0x06,
452 AMG: 0x07,
453 IMUPLUS: 0x08,
454 COMPASS: 0x09,
455 M4G: 0x0A,
456 NDOF_FMC_OFF: 0x0B,
457 NDOF: 0x0C,
458 },
459
460 PWR_MODE_ADDR: 0x3E,
461 PWR_MODES: {
462 NORMAL: 0x00,
463 LOW: 0x01,
464 SUSPEND: 0x02,
465 },
466
467 PAGE_ID_ADDR: 0x07,
468 PAGE_STATES: {
469 ZERO: 0x00,
470 },
471
472 CALIBRATION: 0x35,
473 SYS_TRIGGER: 0x3F,
474
475 UNIT_SEL_ADDR: 0x3B,
476
477 AXIS_MAP_CONFIG_ADDR: 0x41,
478 AXIS_MAP_SIGN_ADDR: 0x42,
479 }
480 },
481 initialize: {
482 value: function(board, opts) {
483 var io = board.io;
484 var address = opts.address || this.ADDRESSES[0];
485
486 // Page 67 4.3.54
487 // a value for what we use to consider the system calibrated,
488 // 0xC0 represents the just fusion algorithm/system
489 var calibrationMask = opts.calibrationMask || 0xC0;
490
491 opts.address = address;
492
493 var computed = {
494 accelerometer: {
495 x: null,
496 y: null,
497 z: null,
498 },
499 gyro: {
500 x: null,
501 y: null,
502 z: null,
503 },
504 magnetometer: {
505 x: null,
506 y: null,
507 z: null,
508 },
509 orientation: {
510 euler: {
511 heading: null,
512 roll: null,
513 pitch: null,
514 },
515 quarternion: {
516 w: null,
517 x: null,
518 y: null,
519 z: null,
520 },
521 },
522 temperature: null,
523 calibration: null,
524 };
525
526 io.i2cConfig(opts);
527
528 // Put chip into CONFIG operation mode
529 io.i2cWriteReg(address, this.REGISTER.OPR_MODE_ADDR, this.REGISTER.OPR_MODES.CONFIG);
530
531 // Set register page to 0
532 io.i2cWriteReg(address, this.REGISTER.PAGE_ID_ADDR, this.REGISTER.PAGE_STATES.ZERO);
533
534 // Page 70, 4.3.63 SYS_TRIGGER
535 //
536 // RST_SYS (Set to reset system)
537 //
538 // B7 B6 B5 B4 B3 B2 B1 B0
539 // 0 0 1 0 0 0 0 0
540 //
541 io.i2cWriteReg(address, this.REGISTER.SYS_TRIGGER, 0x20);
542
543 var por = new Promise(function(resolve) {
544 setTimeout(function() {
545
546 // Normal power mode
547 io.i2cWriteReg(address, this.REGISTER.PWR_MODE_ADDR, this.REGISTER.PWR_MODES.NORMAL);
548
549 // Page 70, 4.3.63 SYS_TRIGGER
550 //
551 // CLK_SEL:
552 //
553 // B7 B6 B5 B4 B3 B2 B1 B0
554 // 0 0 0 0 0 0 0 0
555 //
556 //io.i2cWriteReg(address, this.REGISTER.SYS_TRIGGER, 0x00);
557 // do we want to enable an external crystal??
558 io.i2cWriteReg(address, this.REGISTER.SYS_TRIGGER, opts.enableExternalCrystal ? 0x80 : 0x00);
559
560 // AF Page 24 3.4, Axis remap
561 //
562 // AXIS_MAP_CONFIG:
563 //
564 // B7 B6 B5 B4 B3 B2 B1 B0
565 // 0 0 0 0 0 0 0 0
566 // - - z z y y x x
567 //
568 // x axis = 00, y axis = 01, z axis = 10
569 //
570 // see also the defaults starting on Page 50
571 //
572 var axisMap = opts.axisMap || 0x24;
573 io.i2cWriteReg(address, this.REGISTER.AXIS_MAP_CONFIG_ADDR, axisMap);
574
575 // AF Page 24 3.4, Axis remap
576 //
577 // AXIS_MAP_CONFIG:
578 //
579 // B7 B6 B5 B4 B3 B2 B1 B0
580 // 0 0 0 0 0 0 0 0
581 // - - - - - x y z
582 //
583 // 0 = positive, 1 = negative
584 //
585 var axisSign = opts.axisSign || 0x00;
586 io.i2cWriteReg(address, this.REGISTER.AXIS_MAP_SIGN_ADDR, axisSign);
587
588 // Set operational mode to "nine degrees of freedom"
589 setTimeout(function() {
590 io.i2cWriteReg(address, this.REGISTER.OPR_MODE_ADDR, this.REGISTER.OPR_MODES.NDOF);
591 resolve();
592 }.bind(this), 10);
593
594 // Page 13, 1.2, OPERATING CONDITIONS BNO055
595 // From reset to config mode
596 }.bind(this), 650);
597 }.bind(this));
598
599 por.then(function() {
600 return new Promise(function(resolve) {
601 var readCalibration = function() {
602 io.i2cReadOnce(address, this.REGISTER.CALIBRATION, 1, function(data) {
603
604 var calibration = data[0];
605 var didCalibrationChange = computed.calibration !== calibration;
606
607
608 computed.calibration = calibration;
609
610 // It is useful, possibly to know when the calibration state changes
611 // some of the calibrations are a little picky to get right, so emitting
612 // the calibration state as it changes is useful.
613 if (didCalibrationChange) {
614 this.emit("calibration", computed.calibration);
615 }
616
617 if ((calibration & calibrationMask) === calibrationMask) {
618
619 // Emit the calibration state so we can work out in our userspace if
620 // we are good to go, and for when we are performing the calibration steps
621 // let everyone know we are calibrated.
622 this.emit("calibrated");
623
624 resolve();
625 } else {
626 readCalibration();
627 }
628
629 }.bind(this));
630 }.bind(this);
631
632 readCalibration();
633
634 }.bind(this));
635 }.bind(this)).then(function() {
636
637 // Temperature requires no calibration, begin reading immediately
638 // here we read out temp, and the calibration state since they are back to back
639 // and the device can, has been observed to go out of calibration and we may want to check
640 io.i2cRead(address, this.REGISTER.READ.TEMP, 2, function(data) {
641 computed.temperature = data[0];
642
643 var didCalibrationChange = computed.calibration !== data[1];
644 computed.calibration = data[1];
645
646 this.emit("data", computed);
647 if (didCalibrationChange) {
648 this.emit("calibration", computed.calibration);
649 }
650 }.bind(this));
651
652
653 // ACCEL, MAG and GYRO are 6 bytes each => 18 bytes total
654 io.i2cRead(address, this.REGISTER.READ.ACCEL, 18, function(data) {
655
656 computed.accelerometer = {
657 x: int16(data[1], data[0]),
658 y: int16(data[3], data[2]),
659 z: int16(data[5], data[4])
660 };
661
662 computed.magnetometer = {
663 x: int16(data[7], data[6]),
664 y: int16(data[9], data[8]),
665 z: int16(data[11], data[10])
666 };
667
668 computed.gyro = {
669 x: int16(data[13], data[12]),
670 y: int16(data[15], data[14]),
671 z: int16(data[17], data[16])
672 };
673
674 this.emit("data", computed);
675 }.bind(this));
676
677 // Moved the ndof/quarternions to their own read.. bytes go missing, lots of 32 byte buffers everywhere
678 io.i2cRead(address, this.REGISTER.READ.EULER, 14, function(data) {
679
680 // raw euler
681 computed.orientation.euler = {
682 heading: int16(data[1], data[0]),
683 roll: int16(data[3], data[2]),
684 pitch: int16(data[5], data[4])
685 };
686
687 // scaled quarternion - unitless
688 computed.orientation.quarternion = {
689 w: int16(data[7], data[6]),
690 x: int16(data[9], data[8]),
691 y: int16(data[11], data[10]),
692 z: int16(data[13], data[12])
693 };
694
695 this.emit("data", computed);
696 }.bind(this));
697
698 }.bind(this));
699 },
700 },
701 identifier: {
702 value: function(opts) {
703 var address = opts.address || Drivers.BNO055.ADDRESSES.value[0];
704 return "bno055-" + address;
705 }
706 }
707 },
708
709 MPL115A2: {
710 ADDRESSES: {
711 value: [0x60]
712 },
713 REGISTER: {
714 value: {
715 // Page 5
716 // Table 2. Device Memory Map
717 COEFFICIENTS: 0x04,
718 PADC_MSB: 0x00,
719 CONVERT: 0x12,
720 }
721 },
722 initialize: {
723 value: function(board, opts) {
724 var io = board.io;
725 var address = opts.address || this.ADDRESSES[0];
726
727 opts.address = address;
728
729 io.i2cConfig(opts);
730
731 var computed = {
732 pressure: null,
733 temperature: null,
734 };
735
736 var cof = {
737 a0: null,
738 b1: null,
739 b2: null,
740 c12: null
741 };
742
743 var handler = function(data) {
744
745 // Page 5
746 // 3.1 Pressure, Temperature and Coefficient Bit-Width Specifications
747 var Padc = uint16(data[0], data[1]) >> 6;
748 var Tadc = uint16(data[2], data[3]) >> 6;
749
750 // Page 6
751 // 3.2 Compensation
752 computed.pressure = cof.a0 + (cof.b1 + cof.c12 * Tadc) * Padc + cof.b2 * Tadc;
753 computed.temperature = Tadc;
754
755 this.emit("data", computed);
756
757 readCycle();
758 }.bind(this);
759
760 var readCycle = function() {
761 io.i2cWriteReg(address, this.REGISTER.CONVERT, 0x00);
762 // Page 5
763 // Table 2. Device Memory Map
764 // Starting from PADC_MSB, read 4 bytes:
765 //
766 // Padc_MSB
767 // Padc_LSB
768 // Tadc_MSB
769 // Tadc_LSB
770 //
771 io.i2cReadOnce(address, this.REGISTER.PADC_MSB, 4, handler);
772
773 // TODO: User specified "frequency" needs to be applied here.
774 }.bind(this);
775
776 var pCoefficients = new Promise(function(resolve) {
777 io.i2cReadOnce(address, this.REGISTER.COEFFICIENTS, 8, function(data) {
778 var A0 = int16(data[0], data[1]);
779 var B1 = int16(data[2], data[3]);
780 var B2 = int16(data[4], data[5]);
781 var C12 = int16(data[6], data[7]) >> 2;
782
783 // Source:
784 // https://github.com/adafruit/Adafruit_MPL115A2
785 // a0 is the pressure offset coefficient
786 // b1 is the pressure sensitivity coefficient
787 // b2 is the temperature coefficient of offset (TCO)
788 // c12 is the temperature coefficient of sensitivity (TCS)
789 cof.a0 = A0 / 8;
790 cof.b1 = B1 / 8192;
791 cof.b2 = B2 / 16384;
792 cof.c12 = C12 / 4194304;
793
794 resolve();
795 }.bind(this));
796 }.bind(this));
797
798 pCoefficients.then(readCycle);
799 },
800 },
801 identifier: {
802 value: function(opts) {
803 var address = opts.address || Drivers.MPL115A2.ADDRESSES.value[0];
804 return "mpl115a2-" + address;
805 }
806 }
807 },
808 // Based off of the AdaFruit Arduino library for this chip
809 // https://github.com/adafruit/Adafruit_MPL3115A2_Library
810 MPL3115A2: {
811 ADDRESSES: {
812 value: [0x60]
813 },
814 REGISTER: {
815 // Page 18
816 // 13 Register descriptions
817 value: {
818 STATUS: 0x00,
819 PRESSURE: 0x01,
820 CONFIG: 0x13,
821 BAR_IN_MSB: 0x14,
822 BAR_IN_LSB: 0x15,
823 CONTROL: 0x26,
824 }
825 },
826 MASK: {
827 value: {
828 STATUS: {
829 PRESSURE_DATA_READ: 0x04
830 },
831 CONTROL: {
832 SBYB: 0x01,
833 OS128: 0x38,
834 ALTIMETER: 0x80,
835 PRESSURE: 0x00
836 },
837 CONFIG: {
838 TDEFE: 0x01,
839 PDEFE: 0x02,
840 DREM: 0x04
841 }
842 }
843 },
844 initialize: {
845 value: function(board, opts) {
846 var READLENGTH = 6;
847 var io = board.io;
848 var address = opts.address || this.ADDRESSES[0];
849 var isPressure = false;
850 var elevation = null;
851 var offset = 0;
852
853 opts.address = address;
854
855 // See http://www.henrylahr.com/?p=99 for implementation approach
856 //
857 var altNow = 0;
858 var computed = {
859 pressure: 0,
860 altitude: 0,
861 temperature: 0
862 };
863
864 if (typeof opts.elevation !== "undefined") {
865 elevation = opts.elevation;
866 }
867
868 if (elevation !== null && elevation <= 0) {
869 offset = Math.abs(elevation) + 1;
870 elevation = 1;
871 }
872
873 var waitForReady = function(next) {
874 io.i2cReadOnce(address, this.REGISTER.STATUS, 1, function(data) {
875 if (data[0] & this.MASK.STATUS.PRESSURE_DATA_READ) {
876 next();
877 } else {
878 setTimeout(function() {
879 waitForReady(next);
880 }, 100);
881 }
882 }.bind(this));
883 }.bind(this);
884
885 var readValues = function() {
886 var modeMask = isPressure ? this.MASK.CONTROL.PRESSURE : this.MASK.CONTROL.ALTIMETER;
887 var mode = this.MASK.CONTROL.SBYB | this.MASK.CONTROL.OS128 | modeMask;
888
889 io.i2cWrite(address, this.REGISTER.CONTROL, mode);
890
891 waitForReady(function() {
892 io.i2cReadOnce(address, this.REGISTER.PRESSURE, READLENGTH, function(data) {
893 var value = uint24(data[1], data[2], data[3]) >> 4;
894 var temperature = uint16(data[4], data[5]) >> 4;
895 var altVal;
896
897 computed.temperature = temperature;
898
899 if (isPressure) {
900 computed.pressure = value;
901 this.emit("data", computed);
902 } else {
903 var m = data[1];
904 var c = data[2];
905 var l = data[3];
906 var fl = (l >> 4) / 16;
907
908 altVal = (m << 8 | c) + fl;
909 altNow = (altNow * 3 + altVal) / 4;
910
911 computed.altitude = altNow - offset;
912 }
913
914 isPressure = !isPressure;
915
916 readValues();
917 }.bind(this));
918 }.bind(this));
919 }.bind(this);
920
921 var reads = [];
922 var calibrate = function() {
923 // Clear Oversampling and OST
924 io.i2cWrite(address, this.REGISTER.CONTROL, 0x3B);
925 io.i2cWrite(address, this.REGISTER.CONTROL, 0x39);
926
927 setTimeout(function() {
928 io.i2cReadOnce(address, this.REGISTER.PRESSURE, READLENGTH, function(data) {
929 var m = data[1];
930 var c = data[2];
931 var l = data[3];
932 var fl = (l >> 4) / 4;
933
934 reads.push((m << 10 | c << 2) + fl);
935
936 if (reads.length === 4) {
937 var curpress = (reads[0] + reads[1] + reads[2] + reads[3]) / 4;
938 var seapress = curpress / Math.pow(1 - elevation * 0.0000225577, 5.255);
939
940 // Update Barometric input for Altitude
941 io.i2cWrite(address, this.REGISTER.BAR_IN_MSB, (seapress / 2) >> 8);
942 io.i2cWrite(address, this.REGISTER.BAR_IN_LSB, (seapress / 2) & 0xFF);
943
944 // Get into Altitude mode
945 // One shot & OST bit
946 io.i2cWrite(address, this.REGISTER.CONTROL, 0xBB);
947 io.i2cWrite(address, this.REGISTER.CONTROL, 0xB9);
948
949 setTimeout(function() {
950 io.i2cReadOnce(address, this.REGISTER.PRESSURE, READLENGTH, function(data) {
951 var m = data[1];
952 var c = data[2];
953 var l = data[3];
954 var fl = (l >> 4) / 16;
955
956 altNow = (m << 8 | c) + fl;
957
958 readValues(false);
959 });
960 }.bind(this), 550);
961
962 } else {
963 calibrate();
964 }
965 }.bind(this));
966 }.bind(this), 500);
967 }.bind(this);
968
969 io.i2cConfig(
970 Object.assign(opts, {
971 settings: {
972 stopTX: true
973 }
974 })
975 );
976
977 // configure the chip
978 // Set Altitude Offset.
979 io.i2cWriteReg(address, 0x2D, 0x00);
980
981 io.i2cWriteReg(address, this.REGISTER.BAR_IN_MSB, 0);
982 io.i2cWriteReg(address, this.REGISTER.BAR_IN_LSB, 0);
983
984 io.i2cWriteReg(address, this.REGISTER.CONFIG,
985 this.MASK.CONFIG.TDEFE |
986 this.MASK.CONFIG.PDEFE |
987 this.MASK.CONFIG.DREM);
988
989 if (elevation !== null) {
990 calibrate();
991 } else {
992 readValues();
993 }
994 }
995 },
996 identifier: {
997 value: function(opts) {
998 var address = opts.address || Drivers.MPL3115A2.ADDRESSES.value[0];
999 return "mpl3115a2-" + address;
1000 }
1001 }
1002 },
1003 BMP180: {
1004 ADDRESSES: {
1005 value: [0x77]
1006 },
1007 REGISTER: {
1008 value: {
1009 COEFFICIENTS: 0xAA,
1010 READ: 0x00,
1011 READ_START: 0xF4,
1012 READ_RESULT: 0xF6,
1013 }
1014 },
1015 initialize: {
1016 value: function(board, opts) {
1017 var io = board.io;
1018 var address = opts.address || this.ADDRESSES[0];
1019 var elevation = null;
1020 var offset = 0;
1021
1022 if (typeof opts.elevation !== "undefined") {
1023 elevation = opts.elevation;
1024 }
1025
1026 if ((elevation != null && elevation <= 0) ||
1027 elevation == null) {
1028 offset = Math.abs(elevation) + 1;
1029 elevation = 1;
1030 }
1031
1032 opts.address = address;
1033
1034 /**
1035 * Table 1: Operating conditions, output signal and mechanical characteristics
1036 *
1037 * Pressure Conversion Delay (ms)
1038 *
1039 * [
1040 * 5, LOW
1041 * 8, STANDARD
1042 * 14, HIGH
1043 * 26, ULTRA
1044 * ]
1045 *
1046 * These numbers are derived from rounding the Max column of
1047 * Table 1, for the Conversion Time entries.
1048 */
1049
1050 var mode = opts.mode || 3;
1051 var kpDelay = [5, 8, 14, 26][mode];
1052 var oss = Fn.constrain(mode, 0, 3);
1053
1054 var cof = {
1055 a1: null,
1056 a2: null,
1057 a3: null,
1058 a4: null,
1059 a5: null,
1060 a6: null,
1061 b1: null,
1062 b2: null,
1063 b5: null,
1064 mb: null,
1065 mc: null,
1066 md: null,
1067 };
1068
1069 io.i2cConfig(opts);
1070
1071 var pCoefficients = new Promise(function(resolve) {
1072 io.i2cReadOnce(address, this.REGISTER.COEFFICIENTS, 22, function(data) {
1073 // BMP085
1074 // Page 12
1075 // 3.4 Calibration Coefficients
1076 //
1077 // BMP180
1078 // Page 13
1079 // 3.4 Calibration Coefficients
1080 //
1081 cof.a1 = int16(data[0], data[1]);
1082 cof.a2 = int16(data[2], data[3]);
1083 cof.a3 = int16(data[4], data[5]);
1084 cof.a4 = uint16(data[6], data[7]);
1085 cof.a5 = uint16(data[8], data[9]);
1086 cof.a6 = uint16(data[10], data[11]);
1087 cof.b1 = int16(data[12], data[13]);
1088 cof.b2 = int16(data[14], data[15]);
1089 cof.mb = int16(data[16], data[17]);
1090 cof.mc = int16(data[18], data[19]);
1091 cof.md = int16(data[20], data[21]);
1092
1093 resolve();
1094 });
1095 }.bind(this));
1096
1097 pCoefficients.then(function() {
1098
1099 // BMP085
1100 // Pages 10, 11
1101 // 3.3 Measurement of pressure and temperature
1102 // Pages 12, 13, 14
1103 // 3.5 Calculating pressure and temperature
1104 //
1105 // BMP180
1106 // Pages 11, 12
1107 // 3.3 Measurement of pressure and temperature
1108 // Pages 13, 14, 15, 16
1109 // 3.5 Calculating pressure and temperature
1110 //
1111 var computed = {
1112 altitude: null,
1113 pressure: null,
1114 temperature: null,
1115 };
1116
1117 var cycle = 0;
1118
1119 // BMP180
1120 // Pages 11, 15
1121 // 3.3 Measurement of pressure and temperature
1122 // 3.5 Calculating pressure and temperature
1123 var readCycle = function() {
1124
1125 // cycle 0: temperature
1126 // cycle 1: pressure
1127
1128 var isTemperatureCycle = cycle === 0;
1129 var component = isTemperatureCycle ? 0x2E : 0x34 + (oss << 6);
1130 var numBytes = isTemperatureCycle ? 2 : 3;
1131 var delay = isTemperatureCycle ? 5 : kpDelay;
1132
1133
1134 io.i2cWriteReg(address, this.REGISTER.READ_START, component);
1135
1136 // Once the READ_START register is set,
1137 // delay the READ_RESULT request based on the
1138 // mode value provided by the user, or default.
1139 setTimeout(function() {
1140 io.i2cReadOnce(address, this.REGISTER.READ_RESULT, numBytes, function(data) {
1141 var compensated, uncompensated;
1142 var x1, x2, x3, b3, b4, b6, b7, b6s, bx;
1143
1144 if (isTemperatureCycle) {
1145 // TEMPERATURE
1146 uncompensated = int16(data[0], data[1]);
1147
1148 // Compute the true temperature
1149 x1 = ((uncompensated - cof.a6) * cof.a5) >> 15;
1150 x2 = ((cof.mc << 11) / (x1 + cof.md)) >> 0;
1151
1152 // Compute b5, which is used by the pressure cycle
1153 cof.b5 = (x1 + x2) | 0;
1154
1155 // Steps of 0.1°C
1156 computed.temperature = ((cof.b5 + 8) >> 4) / 10;
1157 } else {
1158 // PRESSURE
1159 uncompensated = uint24(data[0], data[1], data[2]) >> (8 - oss);
1160
1161 b6 = cof.b5 - 4000;
1162 b6s = b6 * b6;
1163 bx = b6s >> 12;
1164
1165 // Intermediary x1 & x2 to calculate x3 for b3
1166 x1 = (cof.b2 * bx) >> 11;
1167 x2 = (cof.a2 * b6) >> 11;
1168 x3 = x1 + x2;
1169 b3 = ((((cof.a1 * 4 + x3) << oss) + 2) / 4) >> 0;
1170
1171 // Intermediary x1 & x2 to calculate x3 for b4
1172 x1 = (cof.a3 * b6) >> 13;
1173 x2 = (cof.b1 * bx) >> 16;
1174 x3 = ((x1 + x2) + 2) >> 2;
1175 b4 = (cof.a4 * (x3 + 32768)) >> 15;
1176 b7 = (uncompensated - b3) * (50000 >> oss);
1177
1178 if (b7 < Fn.POW_2_31) {
1179 compensated = (b7 * 2) / b4;
1180 } else {
1181 compensated = (b7 / b4) * 2;
1182 }
1183
1184 compensated >>= 0;
1185
1186 x1 = (compensated >> 8) * (compensated >> 8);
1187 x1 = (x1 * 3038) >> 16;
1188 x2 = (-7357 * compensated) >> 16;
1189
1190 compensated += (x1 + x2 + 3791) >> 4;
1191
1192 // Steps of 1Pa (= 0.01hPa = 0.01mbar) (=> 0.001kPa)
1193 computed.pressure = compensated;
1194
1195 // 3.7 Calculating pressure at sea level
1196 var seapress = compensated / Math.pow(1 - elevation * 0.0000225577, 5.255);
1197 var altitude = 44330 * (1 - Math.pow(compensated / seapress, 1 / 5.255));
1198
1199 // Page 3 (of BMP280 Datasheet)
1200 // ...relative accuracy is ±0.12 hPa, which is equivalent to
1201 // ±1 m difference in altitude.
1202 computed.altitude = Math.round(altitude - offset);
1203 }
1204
1205 if (++cycle === 2) {
1206 cycle = 0;
1207 this.emit("data", computed);
1208 }
1209
1210 readCycle();
1211 }.bind(this));
1212 }.bind(this), delay);
1213 }.bind(this);
1214
1215 // Kick off "read loop"
1216 //
1217 readCycle();
1218 }.bind(this));
1219 }
1220 },
1221 identifier: {
1222 value: function(opts) {
1223 var address = opts.address || Drivers.BMP180.ADDRESSES.value[0];
1224 return "bmp180-" + address;
1225 }
1226 }
1227 },
1228
1229 BMP280: {
1230 ADDRESSES: {
1231 value: [0x77]
1232 },
1233 REGISTER: {
1234 value: {
1235 COEFFICIENTS: 0x88,
1236 CONFIG: 0xF5,
1237 MEASURE: 0xF4,
1238 // 0xF7, 0xF8, 0xF9
1239 // MSB, LSB, XLSB
1240 PRESSURE: 0xF7,
1241 // 0xFA, 0xFB, 0xFC
1242 // MSB, LSB, XLSB
1243 TEMPERATURE: 0xFA,
1244 RESET: 0xE0,
1245 }
1246 },
1247 initialize: {
1248 value: function(board, opts) {
1249 var io = board.io;
1250 var address = opts.address || this.ADDRESSES[0];
1251 var elevation = null;
1252 var offset = 0;
1253
1254 if (typeof opts.elevation !== "undefined") {
1255 elevation = opts.elevation;
1256 }
1257
1258 if ((elevation != null && elevation <= 0) ||
1259 elevation == null) {
1260 offset = Math.abs(elevation) + 1;
1261 elevation = 1;
1262 }
1263
1264 opts.address = address;
1265
1266 var dig = {
1267 T1: null,
1268 T2: null,
1269 T3: null,
1270 P1: null,
1271 P2: null,
1272 P3: null,
1273 P4: null,
1274 P5: null,
1275 P6: null,
1276 P7: null,
1277 P8: null,
1278 P9: null,
1279 };
1280
1281 io.i2cConfig(opts);
1282
1283 // Page. 24
1284 // 4.3.2 Register 0xE0 "reset"
1285 io.i2cWrite(address, this.REGISTER.RESET, 0xB6);
1286
1287 var pCoefficients = new Promise(function(resolve) {
1288 io.i2cReadOnce(address, this.REGISTER.COEFFICIENTS, 24, function(data) {
1289
1290 // Page 21, Table 17
1291 // Compensation parameter storage, naming and data type
1292 // These are received LSB FIRST
1293 //
1294
1295 dig.T1 = uint16(data[1], data[0]);
1296 dig.T2 = int16(data[3], data[2]);
1297 dig.T3 = int16(data[5], data[4]);
1298
1299 dig.P1 = uint16(data[7], data[6]);
1300 dig.P2 = int16(data[9], data[8]);
1301 dig.P3 = int16(data[11], data[10]);
1302 dig.P4 = int16(data[13], data[12]);
1303 dig.P5 = int16(data[15], data[14]);
1304 dig.P6 = int16(data[17], data[16]);
1305 dig.P7 = int16(data[19], data[18]);
1306 dig.P8 = int16(data[21], data[20]);
1307 dig.P9 = int16(data[23], data[22]);
1308
1309 resolve();
1310 });
1311 }.bind(this));
1312
1313 pCoefficients.then(function() {
1314 /*
1315 CTRL_MEAS bits
1316
1317 | DATA LSB |
1318 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1319 | - | - | - | - | - | - | - | - |
1320 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
1321 */
1322
1323 io.i2cWrite(address, this.REGISTER.MEASURE, 0x3F);
1324
1325 var computed = {
1326 altitude: null,
1327 pressure: null,
1328 temperature: null,
1329 };
1330
1331 //
1332 // Page 12
1333 // 3.3.1 Pressure measurement
1334 //
1335 // Page 13
1336 // 3.3.2 Temperature measurement
1337 //
1338
1339 io.i2cRead(address, this.REGISTER.PRESSURE, 6, function(data) {
1340 var compensated = 0;
1341
1342 // Page 45
1343 // "Returns temperature in DegC, double precision. Output value of
1344 // '51.23' equals 51.23 DegC. t_fine carries fine temperature as global value"
1345 var fine;
1346
1347 // var1, var2
1348 //
1349 // Expect:
1350 //
1351 // int32
1352 //
1353 var v1, v2;
1354
1355 // Page 44
1356 // "Both pressure and temperature values are expected to be
1357 // received in 20 bit format, positive, stored in a 32 bit signed integer. "
1358 //
1359 // V = int32(uint24(m, l, xl))
1360 // V >> 4;
1361 //
1362
1363 // Page 45
1364 var P = s32(uint24(data[0], data[1], data[2]));
1365 var T = s32(uint24(data[3], data[4], data[5]));
1366
1367 P >>= 4;
1368 T >>= 4;
1369
1370 // TEMPERATURE
1371
1372 // Page 45
1373 // bmp280_compensate_T_int32
1374 // var1 = ((((adc_T>>3) – ((BMP280_S32_t)dig_T1<<1))) *
1375 // ((BMP280_S32_t)dig_T2)) >> 11;
1376 // var2 = (((((adc_T>>4) – ((BMP280_S32_t)dig_T1)) *
1377 // ((adc_T>>4) – ((BMP280_S32_t)dig_T1))) >> 12) *
1378 // ((BMP280_S32_t)dig_T3)) >> 14;
1379 //
1380 //
1381 var adc16 = T >> 4;
1382 var adc16subT1 = adc16 - dig.T1;
1383 v1 = (((T >> 3) - (dig.T1 << 1)) * dig.T2) >> 11;
1384 v2 = (((adc16subT1 * adc16subT1) >> 12) * dig.T3) >> 14;
1385
1386 // t_fine = var1 + var2;
1387 fine = v1 + v2;
1388
1389 // Page 7, 8
1390 // Table 2: Parameter specification
1391 //
1392 //
1393 // Temperature 0.01 °C
1394 //
1395 // As toFixed(2)
1396 //
1397 // C = +(((t_fine * 5 + 128) >> 8) / 100).toFixed(resolution)
1398 //
1399 computed.temperature = ((fine * 5 + 128) >> 8) / 100;
1400
1401 v1 = undefined;
1402 v2 = undefined;
1403
1404
1405 // PRESSURE
1406 // Page 46
1407 // bmp280_compensate_P_int32
1408 //
1409 // Every single seemingly arbitrary magic number comes from the datasheet.
1410 // Datasheets are evidently written by people that don't care about
1411 // anyone else actually understanding how a thing works.
1412 //
1413
1414 // var1 = (((BMP280_S32_t)t_fine)>>1) – (BMP280_S32_t)64000;
1415 v1 = s32(fine >> 1) - 64000;
1416
1417 // var2 = (((var1>>2) * (var1>>2)) >> 11 ) * ((BMP280_S32_t)dig_P6);
1418 v2 = (((v1 >> 2) * (v1 >> 2)) >> 11) * s32(dig.P6);
1419
1420 // var2 = var2 + ((var1*((BMP280_S32_t)dig_P5))<<1);
1421 v2 += (v1 * s32(dig.P5)) << 1;
1422
1423 // var2 = (var2>>2)+(((BMP280_S32_t)dig_P4)<<16);
1424 v2 = (v2 >> 2) + (s32(dig.P4) << 16);
1425
1426
1427 // var1 = (((dig_P3 * (((var1>>2) * (var1>>2)) >> 13 )) >> 3) +
1428 // ((((BMP280_S32_t)dig_P2) * var1)>>1))>>18;
1429 v1 = (((dig.P3 * (((v1 >> 2) * (v1 >> 2)) >> 13)) >> 3) + ((s32(dig.P2) * v1) >> 1)) >> 18;
1430
1431 // var1 =((((32768+var1))*((BMP280_S32_t)dig_P1))>>15);
1432 v1 = (((Fn.POW_2_15 + v1) * s32(dig.P1)) >> 15);
1433
1434 if (v1 === 0) {
1435 // Prevent division by zero
1436 return 0;
1437 }
1438
1439 // p = (((BMP280_U32_t)(((BMP280_S32_t)1048576)-adc_P)-(var2>>12)))*3125;
1440 compensated = u32((s32(Fn.POW_2_20) - P) - (v2 >> 12)) * 3125;
1441
1442 if (compensated < Fn.POW_2_31) {
1443 // p = (p << 1) / ((BMP280_U32_t)var1);
1444 compensated = ((compensated << 1) >>> 0) / u32(v1);
1445 } else {
1446 // p = (p / (BMP280_U32_t)var1) * 2;
1447 compensated = ((compensated / u32(v1)) >>> 0) * 2;
1448 }
1449
1450 compensated = u32(compensated) >>> 0;
1451
1452 // var1 = (((BMP280_S32_t)dig_P9) * ((BMP280_S32_t)(((p>>3) * (p>>3))>>13)))>>12;
1453 var compshift3r = compensated >> 3;
1454 v1 = (s32(dig.P9) * s32(((compshift3r * compshift3r) >> 13))) >> 12;
1455
1456 // var2 = (((BMP280_S32_t)(p>>2)) * ((BMP280_S32_t)dig_P8))>>13;
1457 v2 = (s32(compensated >> 2) * s32(dig.P8)) >> 13;
1458
1459 // p = (BMP280_U32_t)((BMP280_S32_t)p + ((var1 + var2 + dig_P7) >> 4));
1460 compensated = u32(s32(compensated) + ((v1 + v2 + dig.P7) >> 4));
1461
1462 // Steps of 1Pa (= 0.01hPa = 0.01mbar) (=> 0.001kPa)
1463 computed.pressure = compensated;
1464
1465 // Calculating pressure at sea level (copied from BMP180)
1466 var seapress = compensated / Math.pow(1 - elevation * 0.0000225577, 5.255);
1467 var altitude = 44330 * (1 - Math.pow(compensated / seapress, 1 / 5.255));
1468
1469 // Page 3
1470 // ...relative accuracy is ±0.12 hPa, which is equivalent to
1471 // ±1 m difference in altitude.
1472 computed.altitude = Math.round(altitude - offset);
1473
1474 this.emit("data", computed);
1475 }.bind(this));
1476 }.bind(this));
1477 }
1478 },
1479 identifier: {
1480 value: function(opts) {
1481 var address = opts.address || Drivers.BMP280.ADDRESSES.value[0];
1482 return "bmp280-" + address;
1483 }
1484 }
1485 },
1486
1487 BME280: {
1488 ADDRESSES: {
1489 value: [0x77]
1490 },
1491 REGISTER: {
1492 value: {
1493 COEFFICIENTS_TP: 0x88,
1494 COEFFICIENTS_H: 0xE1,
1495 CONFIG: 0xF5,
1496 MEASURE_H: 0xF2,
1497 MEASURE_TP: 0xF4,
1498 PRESSURE: 0xF7,
1499 // 0xF7, 0xF8, 0xF9
1500 // MSB, LSB, XLSB
1501 TEMPERATURE: 0xFA,
1502 // 0xFA, 0xFB, 0xFC
1503 // MSB, LSB, XLSB
1504 HUMIDITY: 0xFD,
1505 // 0xFD, 0xFE
1506 // MSB, LSB
1507 RESET: 0xE0,
1508 }
1509 },
1510 initialize: {
1511 value: function(board, opts) {
1512 var io = board.io;
1513 var address = opts.address || this.ADDRESSES[0];
1514 var elevation = null;
1515 var offset = 0;
1516
1517 if (typeof opts.elevation !== "undefined") {
1518 elevation = opts.elevation;
1519 }
1520
1521 if ((elevation != null && elevation <= 0) ||
1522 elevation == null) {
1523 offset = Math.abs(elevation) + 1;
1524 elevation = 1;
1525 }
1526
1527 opts.address = address;
1528
1529 var dig = {
1530 T1: null,
1531 T2: null,
1532 T3: null,
1533 P1: null,
1534 P2: null,
1535 P3: null,
1536 P4: null,
1537 P5: null,
1538 P6: null,
1539 P7: null,
1540 P8: null,
1541 P9: null,
1542 H1: null,
1543 H2: null,
1544 H3: null,
1545 H4: null,
1546 H5: null,
1547 H6: null,
1548 };
1549
1550 io.i2cConfig(opts);
1551
1552 // Page. 24
1553 // 4.3.2 Register 0xE0 "reset"
1554 io.i2cWrite(address, this.REGISTER.RESET, 0xB6);
1555
1556 var pCoefficients = new Promise(function(resolveCoeffs) {
1557
1558 // Page 22,
1559 // Table 16: Compensation parameter storage, naming and data type
1560 // These are received LSB FIRST
1561 //
1562 // The H register is not contiguous!
1563
1564
1565 Promise.all([
1566 new Promise(function(resolve) {
1567 io.i2cReadOnce(address, 0x88, 24, function(data) {
1568 dig.T1 = uint16(data[1], data[0]);
1569 dig.T2 = int16(data[3], data[2]);
1570 dig.T3 = int16(data[5], data[4]);
1571
1572 dig.P1 = uint16(data[7], data[6]);
1573 dig.P2 = int16(data[9], data[8]);
1574 dig.P3 = int16(data[11], data[10]);
1575 dig.P4 = int16(data[13], data[12]);
1576 dig.P5 = int16(data[15], data[14]);
1577 dig.P6 = int16(data[17], data[16]);
1578 dig.P7 = int16(data[19], data[18]);
1579 dig.P8 = s32(int16(data[21], data[20]));
1580 dig.P9 = s32(int16(data[23], data[22]));
1581 resolve();
1582 });
1583 }),
1584 new Promise(function(resolve) {
1585 io.i2cReadOnce(address, 0xA1, 1, function(data) {
1586 dig.H1 = Fn.u8(data[0]);
1587 resolve();
1588 });
1589 }),
1590 new Promise(function(resolve) {
1591 io.i2cReadOnce(address, 0xE1, 8, function(data) {
1592 /*
1593 0xE1 => data[0]
1594 0xE2 => data[1]
1595 0xE3 => data[2]
1596 0xE4 => data[3]
1597 0xE5 => data[4]
1598 0xE6 => data[5]
1599 0xE7 => data[6]
1600 */
1601
1602 // 0xE2 0xE1
1603 // H2 [15:8] [7:0]
1604 dig.H2 = s32(int16(data[1], data[0]));
1605
1606 // 0xE3
1607 dig.H3 = s32(data[2]);
1608
1609 // Special Bit arrangements for H4 & H5
1610 //
1611 // 0xE5 0xE4
1612 // H4 [3:0] [11:4] signed short
1613 // 0xE6 0xE5
1614 // H5 [11:4] [3:0] signed short
1615
1616 dig.H4 = s32((data[3] << 4) | (data[4] & 0xF));
1617 dig.H5 = s32((data[5] << 4) | (data[4] >> 4));
1618
1619 // 0xE7
1620 dig.H6 = Fn.s8(data[6]);
1621
1622 resolve();
1623 });
1624 })
1625 ]).then(resolveCoeffs);
1626 }.bind(this));
1627
1628 pCoefficients.then(function() {
1629 /*
1630 Table 19: Register 0xF2 "ctrl_hum"
1631
1632 Bit 2, 1, 0
1633 Controls oversampling of humidity
1634
1635
1636 osrs_h[2:0] Humidity oversampling
1637 000 Skipped (output set to 0x8000)
1638 001 oversampling ×1
1639 010 oversampling ×2
1640 011 oversampling ×4
1641 100 oversampling ×8
1642 101, others oversampling ×16
1643
1644 | | | HUM |
1645 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1646 | - | - | - | - | - | - | - | - |
1647 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
1648 */
1649 io.i2cWrite(address, this.REGISTER.MEASURE_H, 0x05);
1650
1651 /*
1652 Table 22: Register 0xF4 "ctrl_meas"
1653
1654 Bit 7, 6, 5
1655 Controls oversampling of temperature data
1656
1657 Bit 4, 3, 2
1658 Controls oversampling of pressure data
1659
1660 Bit 1, 0
1661 Controls the sensor mode of the device
1662
1663
1664 osrs_h[2:0] Humidity oversampling
1665 000 Skipped (output set to 0x8000)
1666 001 oversampling ×1
1667 010 oversampling ×2
1668 011 oversampling ×4
1669 100 oversampling ×8
1670 101, others oversampling ×16
1671
1672
1673 000 Skipped (output set to 0x80000)
1674 001 oversampling ×1
1675 010 oversampling ×2
1676 011 oversampling ×4
1677 100 oversampling ×8
1678 101, others oversampling ×16
1679
1680 00 Sleep mode
1681 01 and 10 Forced mode
1682 11 Normal mode
1683
1684 | TEMP | PRES | Mode |
1685 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1686 | - | - | - | - | - | - | - | - |
1687 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 |
1688
1689 */
1690 io.i2cWrite(address, this.REGISTER.MEASURE_TP, 0xB7);
1691
1692
1693 var computed = {
1694 altitude: null,
1695 pressure: null,
1696 humidity: null,
1697 temperature: null,
1698 };
1699
1700 //
1701 // Page 12
1702 // 3.3.1 Pressure measurement
1703 //
1704 // Page 13
1705 // 3.3.2 Temperature measurement
1706 //
1707
1708 var standby = Date.now();
1709
1710 io.i2cRead(address, this.REGISTER.PRESSURE, 8, function(data) {
1711 //
1712 // Response time to complete 63% of a step is 1 second.
1713 // Don't emit a reading until a complete step has occurred.
1714 // This will be ~1587ms
1715 // (1 / 63 * 100) * 1000 = 1587.3015873015872ms
1716 // if ((standby + 1587) > Date.now()) {
1717 if (!process.env.IS_TEST_MODE) {
1718 if ((standby + 1000) > Date.now()) {
1719 return;
1720 }
1721 }
1722
1723 var compensated = 0;
1724
1725 // Page 45
1726 // "Returns temperature in DegC, double precision. Output value of
1727 // '51.23' equals 51.23 DegC. t_fine carries fine temperature as global value"
1728 var fine;
1729
1730 // var1, var2
1731 //
1732 // Expect:
1733 //
1734 // int32
1735 //
1736 var v1, v2, vx;
1737
1738 // Page 50
1739 // "Both pressure and temperature values are expected to be
1740 // received in 20 bit format, positive, stored in a 32 bit signed integer. "
1741 //
1742 // V = int32(uint24(m, l, xl))
1743 // V >> 4;
1744 //
1745
1746 // Page 50
1747 var P = s32(uint24(data[0], data[1], data[2]));
1748 var T = s32(uint24(data[3], data[4], data[5]));
1749 var H = s32(uint16(data[6], data[7]));
1750
1751 P >>= 4;
1752 T >>= 4;
1753
1754 // TEMPERATURE
1755
1756 // Page 23
1757 // bmp280_compensate_T_int32
1758 // var1 = ((((adc_T>>3) – ((BMP280_S32_t)dig_T1<<1))) *
1759 // ((BMP280_S32_t)dig_T2)) >> 11;
1760 // var2 = (((((adc_T>>4) – ((BMP280_S32_t)dig_T1)) *
1761 // ((adc_T>>4) – ((BMP280_S32_t)dig_T1))) >> 12) *
1762 // ((BMP280_S32_t)dig_T3)) >> 14;
1763 //
1764 //
1765 var adc16 = T >> 4;
1766 var adc16subT1 = adc16 - dig.T1;
1767 v1 = (((T >> 3) - (dig.T1 << 1)) * dig.T2) >> 11;
1768 v2 = (((adc16subT1 * adc16subT1) >> 12) * dig.T3) >> 14;
1769
1770 // t_fine = var1 + var2;
1771 fine = v1 + v2;
1772
1773 // Page 7, 8
1774 // Table 2: Parameter specification
1775 //
1776 //
1777 // Temperature 0.01 °C
1778 //
1779 // As toFixed(2)
1780 //
1781 // C = +(((t_fine * 5 + 128) >> 8) / 100).toFixed(resolution)
1782 //
1783 computed.temperature = ((fine * 5 + 128) >> 8) / 100;
1784
1785 v1 = undefined;
1786 v2 = undefined;
1787
1788
1789 // PRESSURE
1790 // Page 23
1791 // bmp280_compensate_P_int32
1792 //
1793 // Every single seemingly arbitrary magic number comes from the datasheet.
1794 // Datasheets are evidently written by people that don't care about
1795 // anyone else actually understanding how a thing works.
1796 //
1797
1798 // var1 = (((BMP280_S32_t)t_fine)>>1) – (BMP280_S32_t)64000;
1799 v1 = s32(fine >> 1) - 64000;
1800
1801 // var2 = (((var1>>2) * (var1>>2)) >> 11 ) * ((BMP280_S32_t)dig_P6);
1802 v2 = (((v1 >> 2) * (v1 >> 2)) >> 11) * s32(dig.P6);
1803
1804 // var2 = var2 + ((var1*((BMP280_S32_t)dig_P5))<<1);
1805 v2 += (v1 * s32(dig.P5)) << 1;
1806
1807 // var2 = (var2>>2)+(((BMP280_S32_t)dig_P4)<<16);
1808 v2 = (v2 >> 2) + (s32(dig.P4) << 16);
1809
1810
1811 // var1 = (((dig_P3 * (((var1>>2) * (var1>>2)) >> 13 )) >> 3) +
1812 // ((((BMP280_S32_t)dig_P2) * var1)>>1))>>18;
1813 v1 = (((dig.P3 * (((v1 >> 2) * (v1 >> 2)) >> 13)) >> 3) + ((s32(dig.P2) * v1) >> 1)) >> 18;
1814
1815 // var1 =((((32768+var1))*((BMP280_S32_t)dig_P1))>>15);
1816 v1 = (((Fn.POW_2_15 + v1) * s32(dig.P1)) >> 15);
1817
1818 if (v1 === 0) {
1819 // Prevent division by zero
1820 return 0;
1821 }
1822
1823 // p = (((BMP280_U32_t)(((BMP280_S32_t)1048576)-adc_P)-(var2>>12)))*3125;
1824 compensated = u32((s32(Fn.POW_2_20) - P) - (v2 >> 12)) * 3125;
1825
1826 if (compensated < Fn.POW_2_31) {
1827 // p = (p << 1) / ((BMP280_U32_t)var1);
1828 compensated = ((compensated << 1) >>> 0) / u32(v1);
1829 } else {
1830 // p = (p / (BMP280_U32_t)var1) * 2;
1831 compensated = ((compensated / u32(v1)) >>> 0) * 2;
1832 }
1833
1834 compensated = u32(compensated) >>> 0;
1835
1836 // var1 = (((BMP280_S32_t)dig_P9) * ((BMP280_S32_t)(((p>>3) * (p>>3))>>13)))>>12;
1837 var compshift3r = compensated >> 3;
1838 v1 = (s32(dig.P9) * s32(((compshift3r * compshift3r) >> 13))) >> 12;
1839
1840 // var2 = (((BMP280_S32_t)(p>>2)) * ((BMP280_S32_t)dig_P8))>>13;
1841 v2 = (s32(compensated >> 2) * dig.P8) >> 13;
1842
1843 // p = (BMP280_U32_t)((BMP280_S32_t)p + ((var1 + var2 + dig_P7) >> 4));
1844 compensated = u32(s32(compensated) + ((v1 + v2 + dig.P7) >> 4));
1845
1846 // Steps of 1Pa (= 0.01hPa = 0.01mbar) (=> 0.001kPa)
1847 computed.pressure = compensated;
1848
1849 // Calculating pressure at sea level (copied from BMP180)
1850 var seapress = compensated / Math.pow(1 - elevation * 0.0000225577, 5.255);
1851 var altitude = 44330 * (1 - Math.pow(compensated / seapress, 1 / 5.255));
1852
1853 // Page 3
1854 // ...relative accuracy is ±0.12 hPa, which is equivalent to
1855 // ±1 m difference in altitude.
1856 computed.altitude = Math.round(altitude - offset);
1857
1858
1859 // Page 23, 24
1860 // BME280_U32_t bme280_compensate_H_int32(BME280_S32_t adc_H)
1861
1862 // BME280_S32_t v_x1_u32r;
1863 // v_x1_u32r = (t_fine – ((BME280_S32_t)76800));
1864 vx = s32(fine - 76800);
1865
1866 // v_x1_u32r = (((((adc_H << 14) – (((BME280_S32_t)dig_H4) << 20) – (((BME280_S32_t)dig_H5) * v_x1_u32r)) +
1867 // ((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)dig_H6)) >> 10) * (((v_x1_u32r * ((BME280_S32_t)dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) *
1868 // ((BME280_S32_t)dig_H2) + 8192) >> 14));
1869
1870 vx = (((((H << 14) - s32(dig.H4 << 20) - (dig.H5 * vx)) + Fn.POW_2_14) >> 15) *
1871 (((((((vx * dig.H6) >> 10) * (((vx * dig.H3) >> 11) + Fn.POW_2_15)) >> 10) + Fn.POW_2_21) * dig.H2 + Fn.POW_2_13) >> 14));
1872
1873 // v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)_bme280_calib.dig_H1)) >> 4));
1874 vx -= (((((vx >> 15) * (vx >> 15)) >> 7) * s32(dig.H1) >> 4));
1875
1876 // v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
1877 // v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
1878 vx = Fn.constrain(vx, 0, 419430400);
1879
1880 computed.humidity = u32(vx >> 12);
1881
1882 this.emit("data", computed);
1883 }.bind(this));
1884 }.bind(this));
1885 }
1886 },
1887 identifier: {
1888 value: function(opts) {
1889 var address = opts.address || Drivers.BME280.ADDRESSES.value[0];
1890 return "bme280-" + address;
1891 }
1892 }
1893 },
1894 SI7020: {
1895 ADDRESSES: {
1896 value: [0x40]
1897 },
1898 REGISTER: {
1899 value: {
1900 HUMIDITY: 0xE5,
1901 TEMPERATURE: 0xE0,
1902 }
1903 },
1904 initialize: {
1905 value: function(board, opts) {
1906 var io = board.io;
1907 var address = opts.address || this.ADDRESSES[0];
1908
1909 opts.address = address;
1910
1911 // The "no hold" measurement requires waiting
1912 // _at least_ 22ms between register write and
1913 // register read. Delay is measured in μs:
1914 // 22ms = 22000μs; recommend 50ms = 50000μs
1915 opts.delay = 50000;
1916
1917 io.i2cConfig(opts);
1918
1919 // Reference
1920 // P. 19
1921 var computed = {
1922 temperature: null,
1923 humidity: null,
1924 };
1925
1926 // Despite the registers being back to back, the SI7020
1927 // does not like when 5 bytes are requested, so we put
1928 // the two data sources on their own read channels.
1929 io.i2cRead(address, this.REGISTER.TEMPERATURE, 2, function(data) {
1930 computed.temperature = uint16(data[0], data[1]);
1931 this.emit("data", computed);
1932 }.bind(this));
1933
1934 io.i2cRead(address, this.REGISTER.HUMIDITY, 2, function(data) {
1935 computed.humidity = uint16(data[0], data[1]);
1936 this.emit("data", computed);
1937 }.bind(this));
1938 }
1939 },
1940 identifier: {
1941 value: function(opts) {
1942 var address = opts.address || Drivers.SI7020.ADDRESSES.value[0];
1943 return "si7020-" + address;
1944 }
1945 },
1946 },
1947
1948 MS5611: {
1949 ADDRESSES: {
1950 value: [0x77]
1951 },
1952 REGISTER: {
1953 value: {
1954 COEFFICIENTS: 0xA2,
1955 READ: 0x00,
1956 PRESSURE: 0x40,
1957 TEMPERATURE: 0x50,
1958 RESET: 0x1E,
1959 }
1960 },
1961 initialize: {
1962 value: function(board, opts) {
1963 var io = board.io;
1964 var address = opts.address || this.ADDRESSES[0];
1965 var elevation = null;
1966 var offset = 0;
1967
1968
1969 if (typeof opts.elevation !== "undefined") {
1970 elevation = opts.elevation;
1971 }
1972
1973 if ((elevation != null && elevation <= 0) ||
1974 elevation == null) {
1975 offset = Math.abs(elevation) + 1;
1976 elevation = 1;
1977 }
1978
1979 opts.address = address;
1980
1981 var computed = {
1982 altitude: null,
1983 pressure: null,
1984 temperature: null,
1985 };
1986
1987 /**
1988 * Page 6
1989 *
1990 * Startup in I2C Mode
1991 *
1992 * 1. Reset
1993 * 2. Read PROM (128 bits of calibration data)
1994 * 3. D1 Conversion
1995 * 4. D2 Conversion
1996 * 5. Read ADC (24 but pressure/temperature)
1997 */
1998 var mode = opts.mode || 5;
1999 /*
2000 [
2001 ULTRA_LOW_POWER
2002 LOW_POWER
2003 STANDARD
2004 HIGH_RES
2005 ULTRA_HIGH_RES *
2006 ]
2007 */
2008
2009 var kpDelay = [1, 2, 3, 4, 5, 10][mode];
2010
2011 /**
2012 * Page 7
2013 */
2014 var cof = {
2015 C1: null,
2016 C2: null,
2017 C3: null,
2018 C4: null,
2019 C5: null,
2020 C6: null,
2021 };
2022
2023 var cKeys = Object.keys(cof);
2024
2025
2026 // TODO: confirm this is actually necessary?
2027 opts.delay = kpDelay * 1000;
2028
2029 io.i2cConfig(opts);
2030 io.i2cWrite(address, this.REGISTER.RESET);
2031
2032 var pCoefficients = new Promise(function(resolve) {
2033 // First, a small delay is required following the reset...
2034 setTimeout(function() {
2035 // Next, each coefficient must be read on it's own.
2036 var cofs = cKeys.map(function(key, index) {
2037 var register = this.REGISTER.COEFFICIENTS + (index * 2);
2038 return new Promise(function(resolve) {
2039 io.i2cReadOnce(address, register, 2, function(data) {
2040 cof[key] = uint16(data[0], data[1]);
2041 resolve();
2042 });
2043 });
2044 }.bind(this));
2045
2046 Promise.all(cofs).then(resolve);
2047 }.bind(this), 50);
2048 }.bind(this));
2049
2050 pCoefficients.then(function() {
2051 // Page 7, 8
2052 //
2053 var cycle = 0;
2054 var D1, D2;
2055 var dT, TEMP, OFF, SENS, P;
2056 var TEMP2, OFF2, SENS2;
2057
2058 var readCycle = function() {
2059
2060 // cycle 0: temperature
2061 // cycle 1: pressure
2062
2063 var isTemperatureCycle = cycle === 0;
2064 var component = (isTemperatureCycle ? 0x50 : 0x40) + mode;
2065
2066 io.i2cWrite(address, component);
2067
2068 if (isTemperatureCycle) {
2069 D2 = 0;
2070 dT = 0;
2071 TEMP = 0;
2072 TEMP2 = 0;
2073 OFF2 = 0;
2074 SENS2 = 0;
2075 } else {
2076 D1 = 0;
2077 OFF = 0;
2078 SENS = 0;
2079 P = 0;
2080 }
2081
2082 // Once the READ_START register is set,
2083 // delay the READ_RESULT request based on the
2084 // mode value provided by the user, or default.
2085 setTimeout(function() {
2086 io.i2cReadOnce(address, this.REGISTER.READ, 3, function(data) {
2087
2088 if (isTemperatureCycle) {
2089 // TEMPERATURE
2090 D2 = uint24(data[0], data[1], data[2]);
2091
2092 // Calculate temperature
2093 // Page 7
2094 // Difference between actual and reference temperature [2]
2095 // dT
2096 // = D2 - TREF
2097 // = D2 - C5 * (2 ** 8)
2098 dT = D2 - (cof.C5 * Fn.POW_2_8);
2099
2100 // Actual temperature (-40…85°C with 0.01°C resolution)
2101 // TEMP
2102 // = 20°C + dT * TEMP * SENS
2103 // = 2000 + dT * C6 / (2 ** 23)
2104 TEMP = 2000 + dT * cof.C6 / Fn.POW_2_23;
2105
2106 // SECOND ORDER TEMPERATURE COMPENSATION
2107 // Page 8
2108 // These ridiculous magic numbers come from
2109 // the datasheet. No explanation is given.
2110 //
2111 if (TEMP < 2000) {
2112 TEMP2 = Math.pow(dT, 2) / Fn.POW_2_31;
2113 OFF2 = 5 * Math.pow(TEMP - 2000, 2) / 2;
2114 SENS2 = 5 * Math.pow(TEMP - 2000, 2) / Fn.POW_2_2;
2115
2116 if (TEMP < -1500) {
2117 OFF2 = OFF2 + 7 * Math.pow(TEMP + 1500, 2);
2118 SENS2 = SENS2 + 11 * Math.pow(TEMP + 1500, 2) / 2;
2119 }
2120 }
2121
2122
2123 TEMP -= TEMP2;
2124
2125 computed.temperature = TEMP / 100;
2126 } else {
2127 // PRESSURE
2128 D1 = uint24(data[0], data[1], data[2]);
2129
2130 // Offset at actual temperature [3]
2131 // OFF
2132 // = OFFT1 +TCO* dT = C2 * (2 ** 16) + (C4 * dT )/ (2 ** 7)
2133 OFF = cof.C2 * Fn.POW_2_16 + (cof.C4 * dT) / Fn.POW_2_7;
2134
2135 // Sensitivity at actual temperature [4]
2136 // SENS =SENST1 +TCS* dT= C1 * (2 ** 15) + (C3 * dT )/ (2 ** 8)
2137 SENS = cof.C1 * Fn.POW_2_15 + (cof.C3 * dT) / Fn.POW_2_8;
2138
2139 // SECOND ORDER TEMPERATURE COMPENSATION
2140 // Page 8
2141 OFF -= OFF2;
2142 SENS -= SENS2;
2143
2144 // Temperature compensated pressure (10…1200mbar with 0.01mbar resolution)
2145 // P = D1 * SENS - OFF = (D1 * SENS / 2 21 - OFF) / 2 15
2146 P = (D1 * SENS / Fn.POW_2_21 - OFF) / Fn.POW_2_15;
2147
2148 // Steps of 1Pa (= 0.01hPa = 0.01mbar) (=> 0.001kPa)
2149 computed.pressure = P;
2150
2151 // Sea level pressure...
2152 var seapress = P / Math.pow(1 - elevation * 0.0000225577, 5.255);
2153 var altitude = 44330 * (1 - Math.pow(P / seapress, 1 / 5.255));
2154
2155 computed.altitude = altitude - offset;
2156 }
2157
2158 if (++cycle === 2) {
2159 cycle = 0;
2160 this.emit("data", computed);
2161 }
2162
2163 readCycle();
2164 }.bind(this));
2165 }.bind(this), kpDelay);
2166 }.bind(this);
2167
2168 // Kick off "read loop"
2169 //
2170 readCycle();
2171 }.bind(this));
2172 }
2173 },
2174 identifier: {
2175 value: function(opts) {
2176 var address = opts.address || Drivers.MS5611.ADDRESSES.value[0];
2177 return "ms5611-" + address;
2178 }
2179 },
2180 },
2181
2182 TH02: {
2183 ADDRESSES: {
2184 value: [0x40]
2185 },
2186 COMMAND: {
2187 value: {
2188 MEASURE_HUMIDITY: 0x01,
2189 MEASURE_TEMPERATURE: 0x11,
2190 }
2191 },
2192 REGISTER: {
2193 value: {
2194 STATUS: 0x00,
2195 READ: 0x01,
2196 CONFIG: 0x03,
2197 }
2198 },
2199 initialize: {
2200 value: function(board, opts) {
2201 var io = board.io;
2202 var address = opts.address || this.ADDRESSES[0];
2203
2204 opts.address = address;
2205
2206 var computed = {
2207 temperature: null,
2208 humidity: null,
2209 };
2210
2211 var cycle = 0;
2212
2213 io.i2cConfig(
2214 Object.assign(opts, {
2215 settings: {
2216 stopTX: true
2217 }
2218 })
2219 );
2220
2221 var readCycle = function() {
2222 // 1. Determine which data we want to request
2223 var isTemperatureCycle = cycle === 0;
2224 var command = isTemperatureCycle ?
2225 this.COMMAND.MEASURE_TEMPERATURE :
2226 this.COMMAND.MEASURE_HUMIDITY;
2227
2228
2229 var conversion = new Promise(function(resolve) {
2230 // 2. Send the appropriate measurement/conversion
2231 // command for this read cycle.
2232 io.i2cWrite(address, this.REGISTER.CONFIG, command);
2233
2234 // 3. Await an affirmative status result. This signifies that
2235 // measurement and conversion are complete and values may
2236 // be read from the peripheral register.get
2237 //
2238 // Register design like this is really painful to work
2239 // with. These peripherals have ample space to store data
2240 // in different registers, but do not.
2241 var requestStatus = function() {
2242 io.i2cReadOnce(address, this.REGISTER.STATUS, 1, function(data) {
2243 var status = data[0];
2244
2245 if (!(status & 0x01)) {
2246 resolve();
2247 } else {
2248 requestStatus();
2249 }
2250 });
2251 }.bind(this);
2252
2253 requestStatus();
2254 }.bind(this));
2255
2256 // Page. 16, 18
2257 //
2258 conversion.then(function() {
2259 // Both values will be placed in the 0x01 after
2260 // the command is received and the measurement taken.
2261
2262 // The datasheet _SAYS_ read the MSB and LSB from 0x01 and 0x02,
2263 // but in reality, reading from 0x01 produces nothing. Trial and
2264 // error testing resulted in discovering the correct data located
2265 // in 0x02 & 0x03.
2266 //
2267 // One might assume that we could then read 2 bytes from 0x02,
2268 // but that also produces garbage, so in the end we need to read
2269 // 3 bytes from 0x01.
2270 Promise.all([
2271 new Promise(function(resolve) {
2272 io.i2cReadOnce(address, 0x01, 1, function(data) {
2273 resolve(data[0]);
2274 });
2275 }),
2276 new Promise(function(resolve) {
2277 io.i2cReadOnce(address, 0x02, 1, function(data) {
2278 resolve(data[0]);
2279 });
2280 })
2281 ]).then(function(data) {
2282
2283 if (isTemperatureCycle) {
2284 computed.temperature = ((uint16(data[0], data[1]) >> 2) / 32) - 50;
2285 } else {
2286 computed.humidity = ((uint16(data[0], data[1]) >> 4) / 16) - 24;
2287 }
2288
2289 if (++cycle === 2) {
2290 cycle = 0;
2291 this.emit("data", computed);
2292 }
2293
2294 readCycle();
2295 }.bind(this));
2296 }.bind(this));
2297 }.bind(this);
2298
2299 readCycle();
2300 },
2301 },
2302 identifier: {
2303 value: function(opts) {
2304 var address = opts.address || Drivers.TH02.ADDRESSES.value[0];
2305 return "th02-" + address;
2306 }
2307 }
2308 },
2309
2310 /**
2311 * LSM303C: 6Dof 3-Axis Magnetometer & Accelerometer
2312 *
2313 * https://learn.sparkfun.com/tutorials/lsm303c-6dof-hookup-guide
2314 * https://github.com/sparkfun/LSM303C_6_DOF_IMU_Breakout
2315 */
2316 LSM303C: {
2317 ADDRESSES: {
2318 value: {
2319 ACC: 0x1D,
2320 MAG: 0x1E
2321 }
2322 },
2323 COMMAND: {
2324 value: {
2325 ACC_SETUP: [0x4, 0x3F, 0x3F, 0x3F],
2326 MAG_SETUP: [0xD8, 0x60, 0x40, 0xD8, 0x8, 0x00]
2327 }
2328 },
2329 REGISTER: {
2330 value: {
2331 ACC_CTRL_SEQ: [0x23, 0x20, 0x20, 0x20],
2332 ACC_STATUS: 0x27,
2333 ACC_OUTX_L: 0x28,
2334 ACC_OUTX_H: 0x29,
2335 ACC_OUTY_L: 0x2A,
2336 ACC_OUTY_H: 0x2B,
2337 ACC_OUTZ_L: 0x2C,
2338 ACC_OUTZ_H: 0x2D,
2339 MAG_CTRL_SEQ: [0x20, 0x21, 0x24, 0x20, 0x23, 0x22],
2340 MAG_STATUS: 0x27,
2341 MAG_OUTX_L: 0x28,
2342 MAG_OUTX_H: 0x29,
2343 MAG_OUTY_L: 0x2A,
2344 MAG_OUTY_H: 0x2B,
2345 MAG_OUTZ_L: 0x2C,
2346 MAG_OUTZ_H: 0x2D,
2347 MAG_TEMP_OUT_L: 0x2E,
2348 MAG_TEMP_OUT_H: 0x2F,
2349 }
2350 },
2351 initialize: {
2352 value: function(board, opts) {
2353
2354 var ACC_SENSITIVITY = 0.06103515625; // LSB/mg
2355 var MAG_SENSITIVITY = 0.00048828125; // LSB/Ga
2356
2357 var io = board.io;
2358 var frequency = this.freq || 40;
2359 var addresses = this.ADDRESSES;
2360
2361 var accelerometer = {};
2362 var magnetometer = {};
2363 var computed = {
2364 temperature: 0,
2365 magnetometer,
2366 accelerometer
2367 };
2368
2369 // ACC Initialization sequence (4 bytes)
2370 var initializeAccelerometer = function() {
2371 io.i2cConfig(Object.assign({}, opts, { address: addresses.ACC }));
2372 this.REGISTER.ACC_CTRL_SEQ
2373 .forEach(function(ctrlReg, i) {
2374 io.i2cWrite(addresses.ACC, ctrlReg, this.COMMAND.ACC_SETUP[i]);
2375 }.bind(this));
2376 }.bind(this);
2377
2378 // MAG Initialization sequence (6 bytes)
2379 var initializeMagnetometer = function() {
2380 io.i2cConfig(Object.assign({}, opts, { address: addresses.MAG }));
2381 this.REGISTER.MAG_CTRL_SEQ
2382 .forEach(function (ctrlReg, i) {
2383 io.i2cWrite(addresses.MAG, ctrlReg, this.COMMAND.MAG_SETUP[i]);
2384 }.bind(this));
2385 }.bind(this);
2386
2387 var readAccelerometer = function(done) {
2388 io.i2cReadOnce(addresses.ACC, this.REGISTER.ACC_OUTX_L, 6, function(data) {
2389 accelerometer.x = int16(data[1], data[0]) * ACC_SENSITIVITY;
2390 accelerometer.y = int16(data[3], data[2]) * ACC_SENSITIVITY;
2391 accelerometer.z = int16(data[5], data[4]) * ACC_SENSITIVITY;
2392 done();
2393 });
2394 }.bind(this);
2395
2396 var readMagnetometer = function(done) {
2397 io.i2cReadOnce(addresses.MAG, this.REGISTER.MAG_OUTX_L, 6, function(data) {
2398 magnetometer.x = int16(data[1], data[0]) * MAG_SENSITIVITY;
2399 magnetometer.y = int16(data[3], data[2]) * MAG_SENSITIVITY;
2400 magnetometer.z = int16(data[5], data[4]) * MAG_SENSITIVITY;
2401 done();
2402 });
2403 }.bind(this);
2404
2405 var readTemperature = function(done) {
2406 io.i2cReadOnce(addresses.MAG, this.REGISTER.MAG_TEMP_OUT_L, 2, function(data) {
2407 computed.temperature = int16(data[1], data[0]);
2408 done();
2409 });
2410 }.bind(this);
2411
2412 // Rinse and repeat
2413 var readCycle = function() {
2414 Promise.all([
2415 new Promise(readAccelerometer),
2416 new Promise(readMagnetometer),
2417 new Promise(readTemperature)
2418 ])
2419 .then(function() {
2420 this.emit("data", computed);
2421 setTimeout(readCycle, frequency);
2422 }.bind(this));
2423 }.bind(this);
2424
2425 // Kick off
2426 initializeAccelerometer();
2427 initializeMagnetometer();
2428 readCycle();
2429 },
2430 },
2431 identifier: {
2432 value: function(opts) {
2433 var address = opts.address || Drivers.LSM303C.ADDRESSES.value[0];
2434 return "lsm303c-" + address;
2435 }
2436 }
2437 },
2438};
2439
2440// Otherwise known as...
2441Drivers.BMP085 = Drivers.BMP180;
2442Drivers.GY521 = Drivers.MPU6050;
2443Drivers.SI7021 = Drivers.SI7020;
2444Drivers.DHT11_I2C_NANO_BACKPACK = Drivers.DHT_I2C_NANO_BACKPACK;
2445Drivers.DHT21_I2C_NANO_BACKPACK = Drivers.DHT_I2C_NANO_BACKPACK;
2446Drivers.DHT22_I2C_NANO_BACKPACK = Drivers.DHT_I2C_NANO_BACKPACK;
2447
2448
2449Drivers.get = function(board, driverName, opts) {
2450 var drivers, driverKey, driver;
2451
2452 if (!activeDrivers.has(board)) {
2453 activeDrivers.set(board, {});
2454 }
2455
2456 opts = opts || {};
2457
2458 drivers = activeDrivers.get(board);
2459 driverKey = Drivers[driverName].identifier.value(opts);
2460
2461 if (!drivers[driverKey]) {
2462 driver = new Emitter();
2463 Object.defineProperties(driver, Drivers[driverName]);
2464 driver.initialize(board, opts);
2465 drivers[driverKey] = driver;
2466 }
2467
2468 return drivers[driverKey];
2469};
2470
2471Drivers.clear = function() {
2472 activeDrivers.clear();
2473};
2474
2475var Controllers = {
2476 /**
2477 * MPU6050 3-axis Gyro/Accelerometer and Thermometer
2478 *
2479 * http://playground.arduino.cc/Main/MPU6050
2480 */
2481
2482 MPU6050: {
2483 initialize: {
2484 value: function(opts) {
2485 Components.call(this, "MPU6050", opts);
2486 }
2487 },
2488 components: {
2489 value: [ACCELEROMETER, GYRO, THERMOMETER]
2490 },
2491 },
2492
2493 BNO055: {
2494 initialize: {
2495 value: function(opts) {
2496 var state = priv.get(this);
2497 var CONTROLLER = "BNO055";
2498
2499 state.calibrationMask = opts.calibrationMask || 0xC0;
2500
2501 // here we want to catch the events coming out of the driver and re-emit them
2502 // not sure what is cleaner here, picking these up from a data event
2503 // in the sub controllers, or this
2504 var driver = Drivers.get(this.board, CONTROLLER, opts);
2505 driver.on("calibrated", function() {
2506 this.emit("calibrated");
2507 }.bind(this));
2508
2509 driver.on("calibration", function(state) {
2510 this.emit("calibration", state);
2511 }.bind(this));
2512
2513 Components.call(this, CONTROLLER, opts);
2514 }
2515 },
2516 components: {
2517 value: [ACCELEROMETER, GYRO, MAGNETOMETER, ORIENTATION, THERMOMETER]
2518 },
2519 calibration: {
2520 get: function() {
2521 return this.orientation.calibration;
2522 }
2523 },
2524 isCalibrated: {
2525 get: function() {
2526 //returns if the system and all sensors are fully calibrated
2527 var calibrationMask = priv.get(this).calibrationMask;
2528 return (this.orientation.calibration & calibrationMask) === calibrationMask;
2529 }
2530 }
2531 },
2532 MPL115A2: {
2533 initialize: {
2534 value: function(opts) {
2535 Components.call(this, "MPL115A2", opts);
2536 }
2537 },
2538 components: {
2539 value: [BAROMETER, THERMOMETER]
2540 },
2541 },
2542 SHT31D: {
2543 initialize: {
2544 value: function(opts) {
2545 Components.call(this, "SHT31D", opts);
2546 }
2547 },
2548 components: {
2549 value: [HYGROMETER, THERMOMETER]
2550 },
2551 },
2552 HTU21D: {
2553 initialize: {
2554 value: function(opts) {
2555 Components.call(this, "HTU21D", opts);
2556 }
2557 },
2558 components: {
2559 value: [HYGROMETER, THERMOMETER]
2560 },
2561 },
2562 HIH6130: {
2563 initialize: {
2564 value: function(opts) {
2565 Components.call(this, "HIH6130", opts);
2566 }
2567 },
2568 components: {
2569 value: [HYGROMETER, THERMOMETER]
2570 },
2571 },
2572 DHT_I2C_NANO_BACKPACK: {
2573 initialize: {
2574 value: function(opts) {
2575 Components.call(this, "DHT_I2C_NANO_BACKPACK", opts);
2576 }
2577 },
2578 components: {
2579 value: [HYGROMETER, THERMOMETER]
2580 },
2581 },
2582 MPL3115A2: {
2583 initialize: {
2584 value: function(opts) {
2585 Components.call(this, "MPL3115A2", opts);
2586 }
2587 },
2588 components: {
2589 value: [ALTIMETER, BAROMETER, THERMOMETER]
2590 },
2591 },
2592 // This controller and driver pair are used for both
2593 // BMP180 and BMP085
2594 BMP180: {
2595 initialize: {
2596 value: function(opts) {
2597 Components.call(this, "BMP180", opts);
2598 }
2599 },
2600 components: {
2601 value: [ALTIMETER, BAROMETER, THERMOMETER]
2602 },
2603 },
2604 BMP280: {
2605 initialize: {
2606 value: function(opts) {
2607 Components.call(this, "BMP280", opts);
2608 }
2609 },
2610 components: {
2611 value: [ALTIMETER, BAROMETER, THERMOMETER]
2612 },
2613 },
2614 BME280: {
2615 initialize: {
2616 value: function(opts) {
2617 Components.call(this, "BME280", opts);
2618 }
2619 },
2620 components: {
2621 value: [ALTIMETER, BAROMETER, HYGROMETER, THERMOMETER]
2622 },
2623 },
2624 SI7020: {
2625 initialize: {
2626 value: function(opts) {
2627 Components.call(this, "SI7020", opts);
2628 }
2629 },
2630 components: {
2631 value: [HYGROMETER, THERMOMETER]
2632 },
2633 },
2634 MS5611: {
2635 initialize: {
2636 value: function(opts) {
2637 Components.call(this, "MS5611", opts);
2638 }
2639 },
2640 components: {
2641 value: [ALTIMETER, BAROMETER, THERMOMETER]
2642 },
2643 },
2644
2645 TH02: {
2646 initialize: {
2647 value: function(opts) {
2648 Components.call(this, "TH02", opts);
2649 }
2650 },
2651 components: {
2652 value: [HYGROMETER, THERMOMETER]
2653 },
2654 },
2655
2656 LSM303C: {
2657 initialize: {
2658 value: function(opts) {
2659 Components.call(this, "LSM303C", opts);
2660 }
2661 },
2662 components: {
2663 value: [MAGNETOMETER, THERMOMETER, ACCELEROMETER]
2664 },
2665 },
2666};
2667
2668// Otherwise known as...
2669Controllers.BMP085 = Controllers.BMP180;
2670Controllers.GY521 = Controllers.MPU6050;
2671Controllers.SI7021 = Controllers.SI7020;
2672Controllers.DHT11_I2C_NANO_BACKPACK = Controllers.DHT_I2C_NANO_BACKPACK;
2673Controllers.DHT21_I2C_NANO_BACKPACK = Controllers.DHT_I2C_NANO_BACKPACK;
2674Controllers.DHT22_I2C_NANO_BACKPACK = Controllers.DHT_I2C_NANO_BACKPACK;
2675
2676
2677function IMU(opts) {
2678
2679 if (!(this instanceof IMU)) {
2680 return new IMU(opts);
2681 }
2682
2683 var controller, state;
2684
2685 Board.Component.call(
2686 this, opts = Board.Options(opts)
2687 );
2688
2689 if (opts.controller && typeof opts.controller === "string") {
2690 controller = Controllers[opts.controller.toUpperCase()];
2691 } else {
2692 controller = opts.controller;
2693 }
2694
2695 if (controller == null) {
2696 throw new Error("Missing IMU/Multi controller");
2697 }
2698
2699 this.freq = opts.freq || 20;
2700
2701 state = {};
2702 priv.set(this, state);
2703
2704 Board.Controller.call(this, controller, opts);
2705
2706 if (typeof this.initialize === "function") {
2707 this.initialize(opts);
2708 }
2709
2710 // The IMU/Multi isn't considered "ready"
2711 // until one of the components has notified via
2712 // a change event.
2713 this.isReady = false;
2714
2715 setInterval(function() {
2716 if (this.isReady) {
2717 this.emit("data", this);
2718 }
2719 }.bind(this), this.freq);
2720
2721 var awaiting = this.components.slice();
2722
2723 if (this.components && this.components.length > 0) {
2724 this.components.forEach(function(component) {
2725 if (!(this[component] instanceof Emitter)) {
2726 return;
2727 }
2728
2729 this[component].on("change", function() {
2730 if (awaiting.length) {
2731 var index = awaiting.indexOf(component);
2732
2733 if (index !== -1) {
2734 awaiting.splice(index, 1);
2735 }
2736 }
2737
2738 if (!awaiting.length && !this.isReady) {
2739 this.isReady = true;
2740 }
2741
2742 if (this.isReady) {
2743 this.emit("change", this, component);
2744 }
2745 }.bind(this));
2746 }, this);
2747 }
2748}
2749
2750util.inherits(IMU, Emitter);
2751
2752IMU.Drivers = Drivers;
2753
2754/* istanbul ignore else */
2755if (!!process.env.IS_TEST_MODE) {
2756 IMU.Controllers = Controllers;
2757 IMU.purge = function() {
2758 priv.clear();
2759 };
2760}
2761
2762module.exports = IMU;