UNPKG

johnny-five

Version:

The JavaScript Robotics and Hardware Programming Framework. Use with: Arduino (all models), Electric Imp, Beagle Bone, Intel Galileo & Edison, Linino One, Pinoccio, pcDuino3, Raspberry Pi, Particle/Spark Core & Photon, Tessel 2, TI Launchpad and more!

1,009 lines (879 loc) 27.2 kB
var Emitter = require("events").EventEmitter; var util = require("util"); var Board = require("./board"); var Fn = require("./fn"); var toFixed = Fn.toFixed; var CELSIUS_TO_KELVIN = 273.15; function analogHandler(opts, dataHandler) { var pin = opts.pin; this.io.pinMode(pin, this.io.MODES.ANALOG); this.io.analogRead(pin, function(data) { dataHandler.call(this, data); }.bind(this)); } var activeDrivers = new Map(); var Drivers = { MAX31850K: { initialize: { value: function(board, opts) { var CONSTANTS = { TEMPERATURE_FAMILY: 0x3B, CONVERT_TEMPERATURE_COMMAND: 0x44, READ_SCRATCHPAD_COMMAND: 0xBE, READ_COUNT: 9 }, pin = opts.pin, freq = opts.freq || 100, getAddress, readTemperature, isConversionAvailable, getAddresses, readOne; getAddress = function(device) { // 64-bit device code // device[0] => Family Code // device[1..6] => Serial Number (device[1] is LSB) // device[7] => CRC var i, result = 0; for (i = 6; i > 0; i--) { result = result * 256 + device[i]; } return result; }; board.io.sendOneWireConfig(pin, true); board.io.sendOneWireSearch(pin, function(err, devices) { if (err) { this.emit("error", err); return; } this.devices = devices.filter(function(device) { return device[0] === CONSTANTS.TEMPERATURE_FAMILY; }, this); if (devices.length === 0) { this.emit("error", new Error("FAILED TO FIND TEMPERATURE DEVICE")); return; } this.devices.forEach(function(device) { this.emit("initialized", getAddress(device)); }.bind(this)); getAddresses = function() { if (this.addresses) { return this.devices.filter(function(device) { var address = getAddress(device); return this.addresses.includes(address); }, this); } else { return [this.devices[0]]; } }.bind(this); readTemperature = function() { var devicesToWait, devicesToRead, result; // request tempeature conversion devicesToWait = getAddresses(); devicesToRead = getAddresses(); devicesToRead.forEach(function(device) { board.io.sendOneWireReset(pin); board.io.sendOneWireWrite(pin, device, CONSTANTS.CONVERT_TEMPERATURE_COMMAND); }); isConversionAvailable = function(done) { var nextDevice; if (devicesToWait.length === 0) { return done(); } nextDevice = devicesToWait.pop(); board.io.sendOneWireReset(pin); board.io.sendOneWireWriteAndRead(pin, nextDevice, CONSTANTS.READ_SCRATCHPAD_COMMAND, CONSTANTS.READ_COUNT, function(err, data) { if (!data[0]) { devicesToWait.push(nextDevice); if (data[1] !== 0) { //*****checks if second data bit is 0, if not its an error and gets thrown out return done(); } } isConversionAvailable(done); }); }.bind(this); readOne = function() { var device; if (devicesToRead.length === 0) { setTimeout(readTemperature, freq); return; } device = devicesToRead.pop(); // read from the scratchpad board.io.sendOneWireReset(pin); board.io.sendOneWireWriteAndRead(pin, device, CONSTANTS.READ_SCRATCHPAD_COMMAND, CONSTANTS.READ_COUNT, function(err, data) { if (err) { this.emit("error", err); return; } result = (data[1] << 8) | data[0]; this.emit("data", getAddress(device), result); readOne(); }.bind(this)); }.bind(this); isConversionAvailable(readOne); }.bind(this); readTemperature(); }.bind(this)); } }, register: { value: function(address) { if (!this.addresses) { this.addresses = []; } this.addresses.push(address); } } }, DS18B20: { initialize: { value: function(board, opts) { var CONSTANTS = { TEMPERATURE_FAMILY: 0x28, CONVERT_TEMPERATURE_COMMAND: 0x44, READ_SCRATCHPAD_COMMAND: 0xBE, READ_COUNT: 2 }, pin = opts.pin, freq = opts.freq || 100, getAddress, readThermometer, readOne; getAddress = function(device) { // 64-bit device code // device[0] => Family Code // device[1..6] => Serial Number (device[1] is LSB) // device[7] => CRC var i, result = 0; for (i = 6; i > 0; i--) { result = result * 256 + device[i]; } return result; }; board.io.sendOneWireConfig(pin, true); board.io.sendOneWireSearch(pin, function(err, devices) { if (err) { this.emit("error", err); return; } this.devices = devices.filter(function(device) { return device[0] === CONSTANTS.TEMPERATURE_FAMILY; }, this); if (devices.length === 0) { this.emit("error", new Error("FAILED TO FIND TEMPERATURE DEVICE")); return; } this.devices.forEach(function(device) { this.emit("initialized", getAddress(device)); }.bind(this)); readThermometer = function() { var devicesToRead, result; // request tempeature conversion if (this.addresses) { devicesToRead = this.devices.filter(function(device) { var address = getAddress(device); return this.addresses.includes(address); }, this); } else { devicesToRead = [this.devices[0]]; } devicesToRead.forEach(function(device) { board.io.sendOneWireReset(pin); board.io.sendOneWireWrite(pin, device, CONSTANTS.CONVERT_TEMPERATURE_COMMAND); }); // the delay gives the sensor time to do the calculation board.io.sendOneWireDelay(pin, 1); readOne = function() { var device; if (devicesToRead.length === 0) { setTimeout(readThermometer, freq); return; } device = devicesToRead.pop(); // read from the scratchpad board.io.sendOneWireReset(pin); board.io.sendOneWireWriteAndRead(pin, device, CONSTANTS.READ_SCRATCHPAD_COMMAND, CONSTANTS.READ_COUNT, function(err, data) { if (err) { this.emit("error", err); return; } result = (data[1] << 8) | data[0]; this.emit("data", getAddress(device), result); readOne(); }.bind(this)); }.bind(this); readOne(); }.bind(this); readThermometer(); }.bind(this)); } }, register: { value: function(address) { if (!this.addresses) { this.addresses = []; } this.addresses.push(address); } } } }; Drivers.get = function(board, driverName, opts) { var drivers, driver; if (!activeDrivers.has(board)) { activeDrivers.set(board, {}); } drivers = activeDrivers.get(board); if (!drivers[driverName]) { driver = new Emitter(); Object.defineProperties(driver, Drivers[driverName]); driver.initialize(board, opts); drivers[driverName] = driver; } return drivers[driverName]; }; Drivers.clear = function() { activeDrivers.clear(); }; // References // var Controllers = { // Generic thermistors. See datasheet for each device. ANALOG: { initialize: { value: analogHandler } }, // http://www.ti.com/lit/ds/symlink/lm35.pdf LM35: { initialize: { value: analogHandler }, toCelsius: { value: function(raw) { // VOUT = 1500 mV at 150°C // VOUT = 250 mV at 25°C // VOUT = –550 mV at –55°C var mV = this.aref * 1000 * raw / 1024; // 10mV = 1°C // // Page 1 return Math.round(mV / 10); } } }, // http://www.ti.com/lit/ds/symlink/lm335.pdf LM335: { initialize: { value: analogHandler }, toCelsius: { value: function(raw) { // OUTPUT 10mV/°K var mV = this.aref * 1000 * raw / 1024; // Page 1 return Math.round((mV / 10) - CELSIUS_TO_KELVIN); } } }, // http://www.analog.com/media/en/technical-documentation/data-sheets/TMP35_36_37.pdf TMP36: { initialize: { value: analogHandler }, toCelsius: { value: function(raw) { // Analog Reference Voltage var mV = this.aref * 1000 * raw / 1024; // tempC = (mV / 10) - 50 // http://ctms.engin.umich.edu/CTMS/Content/Activities/TMP35_36_37.pdf // // Page 3 // Table 1 // Accuracy 1°C return Math.round((mV / 10) - 50); } } }, // http://www.ti.com.cn/cn/lit/ds/symlink/tmp102.pdf TMP102: { ADDRESSES: { value: [0x48] }, initialize: { value: function(opts, dataHandler) { var address = opts.address || this.ADDRESSES[0]; opts.address = address; this.io.i2cConfig(opts); // http://www.ti.com/lit/ds/sbos397b/sbos397b.pdf // Addressing is unclear. this.io.i2cRead(address, 0x00, 2, function(data) { // Based on the example code from https://www.sparkfun.com/products/11931 var raw = ((data[0] << 8) | data[1]) >> 4; // The tmp102 does twos compliment but has the negative bit in the wrong spot, so test for it and correct if needed if (raw & (1 << 11)) { raw |= 0xF800; // Set bits 11 to 15 to 1s to get this reading into real twos compliment } // twos compliment raw = raw >> 15 ? ((raw ^ 0xFFFF) + 1) * -1 : raw; dataHandler(raw); }); } }, toCelsius: { value: function(raw) { // 6.5 Electrical Characteristics // –25°C to 85°C ±0.5 return toFixed(raw / 16, 1); } }, }, // https://cdn-shop.adafruit.com/datasheets/MAX31850-MAX31851.pdf MAX31850K: { initialize: { value: function(opts, dataHandler) { var state = priv.get(this), address = opts.address, driver = Drivers.get(this.board, "MAX31850K", opts); if (address) { state.address = address; driver.register(address); } else { if (driver.addressless) { this.emit("error", "You cannot have more than one MAX31850K without an address"); } driver.addressless = true; } driver.once("initialized", function(dataAddress) { if (!state.address) { state.address = dataAddress; } }); driver.on("data", function(dataAddress, data) { if (!address || dataAddress === address) { dataHandler(data); } }.bind(this)); } }, toCelsius: { // Page 4 // Thermocouple Temperature Data Resolution value: function(raw) { return toFixed(raw / 16, 2); } }, address: { get: function() { return priv.get(this).address || 0x00; } } }, // Based on code from Westin Pigott: // https://github.com/westinpigott/one-wire-temps // And the datasheet: // http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf // OneWire protocol. The device needs to be issued a "Convert Temperature" // command which can take up to 10 microseconds to compute, so we need // tell the board to delay 1 millisecond before issuing the "Read Scratchpad" command // // This device requires the OneWire support enabled via ConfigurableFirmata DS18B20: { initialize: { value: function(opts, dataHandler) { var state = priv.get(this), address = opts.address, driver = Drivers.get(this.board, "DS18B20", opts); if (address) { state.address = address; driver.register(address); } else { if (driver.addressless) { this.emit("error", "You cannot have more than one DS18B20 without an address"); } driver.addressless = true; } driver.once("initialized", function(dataAddress) { if (!state.address) { state.address = dataAddress; } }); driver.on("data", function(dataAddress, data) { if (!address || dataAddress === address) { dataHandler(data); } }); } }, toCelsius: { value: function(raw) { // DS18B20.pdf, Default // ±0.5°C accuracy from -10°C to +85°C // // Temp resolution is as follows: // 9b, 10b 11b, 12b // 0.5°C, 0.25°C, 0.125°C, 0.0625°C // // I'm not sure which we're reading, so default to 4 // fractional digits until we can verify return toFixed(raw / 16, 4); } }, address: { get: function() { return priv.get(this).address || 0x00; } } }, // https://cdn-shop.adafruit.com/product-files/2857/Sensirion_Humidity_SHT3x_Datasheet_digital-767294.pdf SHT31D: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "SHT31D", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 4 // Table 1.2 Temperature Sensor Performance // Resolution: 0.015 // // Page 14 // 4.13 Conversion of Signal Output // T[C] = -45 + 175 * (St / ((2 ** 26) - 1)) // St = Sensor raw temperature return toFixed((175 * raw / 65535) - 45, 3); } } }, // https://www.adafruit.com/datasheets/1899_HTU21D.pdf HTU21D: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "HTU21D", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 5 // Digital Relative Humidity sensor with Temperature output // Resolution shows 0.01-0.04 // // Page 15 // CONVERSION OF SIGNAL OUTPUTS // T = -46.85 + 175.72 * (Stemp / (2 ** 16)) // Stemp = Sensor raw temperature return toFixed((175.72 * raw / 65536) - 46.85, 2); } } }, // http://www.phanderson.com/arduino/I2CCommunications.pdf // http://cdn.sparkfun.com/datasheets/Prototyping/1443945.pdf HIH6130: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "HIH6130", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 3 // 5.0 Calculation of Optional Temperature // from the Digital Output // // -40 C = 0 // 125 C = 2 ** 14 - 1 return Math.round(raw / 1000); } } }, // http://akizukidenshi.com/download/ds/aosong/DHT11.pdf DHT_I2C_NANO_BACKPACK: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "DHT_I2C_NANO_BACKPACK", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 2 // 5. Product parameters // Range: ... ±2°C return Math.round(raw / 100); } } }, // http://www.seeedstudio.com/wiki/images/3/30/TH02_SENSOR.pdf TH02: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "TH02", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 8 // Table 5. Temperature Sensor // Accuracy Typical at 25 °C — ±0.5 ±1.0 °C return toFixed(raw, 1); } } }, // https://cdn.sparkfun.com/datasheets/Components/General%20IC/PS-MPU-6000A.pdf MPU6050: { initialize: { value: function(opts, dataHandler) { var IMU = require("./imu"); var driver = IMU.Drivers.get(this.board, "MPU6050", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // No sub-degree/fractional parts illustrated in datasheet return Math.round((raw / 340.00) + 36.53); } } }, // https://cdn-shop.adafruit.com/datasheets/BST_BNO055_DS000_12.pdf BNO055: { initialize: { value: function(opts, dataHandler) { var IMU = require("./imu"); var driver = IMU.Drivers.get(this.board, "BNO055", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 37 // Table 3-37: Temperature data representation // 1°C = 1 LSB // raw is already C return Math.trunc(raw); } } }, // http://cache.freescale.com/files/sensors/doc/data_sheet/MPL115A2.pdf MPL115A2: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "MPL115A2", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // No description, so removing fractional parts return Math.trunc((raw - 498) / -5.35 + 25); } } }, // http://www.nxp.com/files/sensors/doc/data_sheet/MPL3115A2.pdf MPL3115A2: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "MPL3115A2", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 5 // Table 2 Mechanical Characteristics // Accuracy @ 25 °C ±1°C return Math.round(raw / 16); } } }, // http://www.hpinfotech.ro/MS5611-01BA03.pdf MS5611: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "MS5611", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 1 // TECHNICAL DATA // Resolution <0.01 °C return toFixed(raw, 2); } } }, GROVE: { initialize: { value: analogHandler }, toCelsius: { value: function(raw) { // http://www.seeedstudio.com/wiki/Grove_-_Temperature_Sensor var adcres = 1023; // Beta parameter var beta = 3975; // 10 kOhm (sensor resistance) var rb = 10000; // Ginf = 1/Rinf // var ginf = 120.6685; // Reference Temperature 25°C var tempr = 298.15; var rthermistor = (adcres - raw) * rb / raw; var tempc = 1 / (Math.log(rthermistor / rb) / beta + 1 / tempr) - CELSIUS_TO_KELVIN; return Math.round(tempc); } } }, // http://www.cantherm.com/media/productPDF/cantherm_mf52_1.pdf // MF52A103J3470 TINKERKIT: { initialize: { value: analogHandler }, toCelsius: { value: function(raw) { var adcres = 1023; var beta = 3950; var rb = 10000; // 10 kOhm var ginf = 120.6685; // Ginf = 1/Rinf var rthermistor = rb * (adcres / raw - 1); var tempc = beta / (Math.log(rthermistor * ginf)); return Math.round(tempc - CELSIUS_TO_KELVIN); } } }, // https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf BMP180: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "BMP180", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 6 // Table 1: Operating conditions, output signal and mechanical characteristics // // Resolution of output data // pressure 0.01 hPa // temperature 0.1 °C return toFixed(raw, 1); } } }, // https://cdn-shop.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf BMP280: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "BMP280", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 8 // // Resolution of output data in ultra high resolution mode* // Pressure 0.0016 hPa // Temperature 0.01 °C // // * resolution mode is currently not configurable. // return toFixed(raw, 2); } } }, // https://cdn.sparkfun.com/assets/learn_tutorials/4/1/9/BST-BME280_DS001-10.pdf BME280: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "BME280", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 23 // Resolution is 0.01 DegC. return toFixed(raw, 2); } } }, // https://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020-A20.pdf SI7020: { initialize: { value: function(opts, dataHandler) { var Multi = require("./imu"); var driver = Multi.Drivers.get(this.board, "SI7020", opts); driver.on("data", function(data) { dataHandler(data.temperature); }); } }, toCelsius: { value: function(raw) { // Page 9 // Table 5. Temperature Sensor // Accuracy1 –10 °C< tA < 85 °C — ±0.3 ±0.4 °C // // Page 23 // (See temperature conversion expression) return toFixed((175.72 * raw / 65536) - 46.85, 1); } } }, // http://ww1.microchip.com/downloads/en/DeviceDoc/25095A.pdf MCP9808: { ADDRESSES: { value: [0x18] }, initialize: { value: function(opts, dataHandler) { var address = opts.address || this.ADDRESSES[0]; opts.address = address; this.io.i2cConfig(opts); // Page 17 // Register 0x05 = Ta (Temp, Ambient) this.io.i2cRead(address, 0x05, 2, function(data) { // Page 24 // 5.1.3 AMBIENT TEMPERATURE REGISTER (TA) var raw = (data[0] << 8) | data[1]; // Page 25 raw = (raw & 0x0FFF) / 16; if (raw & 0x1000) { raw -= 256; } dataHandler(raw); }); } }, toCelsius: { value: function(raw) { // Page 1 // Microchip Technology Inc.s MCP9808 digital // temperature sensor converts temperatures between // -20°C and +100°C to a digital word with // ±0.25°C/±0.5°C (typical/maximum) accuracy. return toFixed(raw, 2); } }, }, }; Controllers.BMP085 = Controllers.BMP180; Controllers.GY521 = Controllers.MPU6050; Controllers.DHT11_I2C_NANO_BACKPACK = Controllers.DHT_I2C_NANO_BACKPACK; Controllers.DHT21_I2C_NANO_BACKPACK = Controllers.DHT_I2C_NANO_BACKPACK; Controllers.DHT22_I2C_NANO_BACKPACK = Controllers.DHT_I2C_NANO_BACKPACK; var priv = new Map(); function Thermometer(opts) { if (!(this instanceof Thermometer)) { return new Thermometer(opts); } var controller = null; var last = null; var raw = null; Board.Component.call( this, opts = Board.Options(opts) ); var freq = opts.freq || 25; // Analog Reference Voltage (default to board.io.aref || 5) this.aref = opts.aref || this.io.aref || 5; if (opts.controller && typeof opts.controller === "string") { controller = Controllers[opts.controller.toUpperCase()]; } else { controller = opts.controller; } if (controller == null) { controller = Controllers.ANALOG; } priv.set(this, {}); Board.Controller.call(this, controller, opts); if (!this.toCelsius) { this.toCelsius = opts.toCelsius || function(x) { return x; }; } var descriptors = { celsius: { get: function() { return this.toCelsius(raw); } }, fahrenheit: { get: function() { return toFixed((this.celsius * 9 / 5) + 32, 2); } }, kelvin: { get: function() { return toFixed(this.celsius + CELSIUS_TO_KELVIN, 2); } } }; // Convenience aliases descriptors.C = descriptors.celsius; descriptors.F = descriptors.fahrenheit; descriptors.K = descriptors.kelvin; Object.defineProperties(this, descriptors); if (typeof this.initialize === "function") { this.initialize(opts, function(data) { raw = data; }); } setInterval(function() { if (raw == null) { return; } var data = {}; data.C = data.celsius = this.celsius; data.F = data.fahrenheit = this.fahrenheit; data.K = data.kelvin = this.kelvin; this.emit("data", data); if (this.celsius !== last) { last = this.celsius; this.emit("change", data); } }.bind(this), freq); } util.inherits(Thermometer, Emitter); Thermometer.Drivers = Drivers; module.exports = Thermometer;