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!

520 lines (445 loc) 13.5 kB
const Board = require("./board"); const EVS = require("./evshield"); const Withinable = require("./mixins/withinable"); const { uint16, toFixed, scale } = require("./fn"); const priv = new Map(); const Controllers = { DEFAULT: { initialize: { value(options, callback) { this.io.pinMode(this.pin, this.io.MODES.ANALOG); this.io.analogRead(this.pin, callback); }, }, toIntensityLevel: { value(value) { return toFixed(scale(value, 0, 1023, 0, 100) / 100, 2); } } }, EVS_EV3: { initialize: { value(options, callback) { const state = priv.get(this); if (options.mode) { options.mode = options.mode.toUpperCase(); } state.mode = options.mode === "REFLECTED" ? EVS.Type_EV3_LIGHT_REFLECTED : EVS.Type_EV3_LIGHT; state.shield = EVS.shieldPort(options.pin); state.ev3 = new EVS(Object.assign(options, { io: this.io })); state.ev3.setup(state.shield, EVS.Type_EV3); state.ev3.write(state.shield, 0x81 + state.shield.offset, state.mode); state.ev3.read(state.shield, EVS.Light, EVS.Light_Bytes, data => { const value = data[0] | (data[1] << 8); callback(value); }); } }, toIntensityLevel: { value(value) { return toFixed(value / 100, 2); } } }, EVS_NXT: { initialize: { value(options, callback) { const state = priv.get(this); if (options.mode) { options.mode = options.mode.toUpperCase(); } state.mode = options.mode === "REFLECTED" ? EVS.Type_NXT_LIGHT_REFLECTED : EVS.Type_NXT_LIGHT; state.shield = EVS.shieldPort(options.pin); state.ev3 = new EVS(Object.assign(options, { io: this.io })); state.ev3.setup(state.shield, state.mode); state.ev3.read(state.shield, state.shield.analog, EVS.Analog_Bytes, data => { callback(data[0] | (data[1] << 8)); }); } }, toIntensityLevel: { value(value) { return toFixed(scale(value, 0, 1023, 100, 0) / 100, 2); } } }, TSL2561: { ADDRESSES: { value: [0x29, 0x39, 0x49] }, REGISTER: { value: { CONTROL: 0x00, TIMING: 0x01, READ: 0x2C, }, }, initialize: { value(options, callback) { const command = byte => byte | 0b10000000; const { Drivers } = require("./sip"); const address = Drivers.addressResolver(this, options); this.io.i2cConfig(options); // Page 15 // Control Register (0h) // RESV 7:2 // POWER 1:0 // // Power up/power down. // By writing a 03h to this register, the device is powered up. // By writing a 00h to this register, the device is powered down. // // 0b00000011 = 0x03 // 0b00000000 = 0x00 this.io.i2cWriteReg(address, command(this.REGISTER.CONTROL), 0x03); // Gain & Integration // var isAutoGain = false; // Page 24 // Integration time scaling factors const LUX_SCALE = 14; // scale by (2 ** 14) const RATIO_SCALE = 9; // scale ratio by (2 ** 9) // Page 24 // T, FN, and CL Package coefficients const K1T = 0x0040; // 0.125 * (2 ** RATIO_SCALE) const B1T = 0x01F2; // 0.0304 * (2 ** LUX_SCALE) const M1T = 0x01BE; // 0.0272 * (2 ** LUX_SCALE) const K2T = 0x0080; // 0.250 * (2 ** RATIO_SCALE) const B2T = 0x0214; // 0.0325 * (2 ** LUX_SCALE) const M2T = 0x02D1; // 0.0440 * (2 ** LUX_SCALE) const K3T = 0x00C0; // 0.375 * (2 ** RATIO_SCALE) const B3T = 0x023F; // 0.0351 * (2 ** LUX_SCALE) const M3T = 0x037B; // 0.0544 * (2 ** LUX_SCALE) const K4T = 0x0100; // 0.50 * (2 ** RATIO_SCALE) const B4T = 0x0270; // 0.0381 * (2 ** LUX_SCALE) const M4T = 0x03FE; // 0.0624 * (2 ** LUX_SCALE) const K5T = 0x0138; // 0.61 * (2 ** RATIO_SCALE) const B5T = 0x016F; // 0.0224 * (2 ** LUX_SCALE) const M5T = 0x01FC; // 0.0310 * (2 ** LUX_SCALE) const K6T = 0x019A; // 0.80 * (2 ** RATIO_SCALE) const B6T = 0x00D2; // 0.0128 * (2 ** LUX_SCALE) const M6T = 0x00FB; // 0.0153 * (2 ** LUX_SCALE) const K7T = 0x029A; // 1.3 * (2 ** RATIO_SCALE) const B7T = 0x0018; // 0.00146 * (2 ** LUX_SCALE) const M7T = 0x0012; // 0.00112 * (2 ** LUX_SCALE) const K8T = 0x029A; // 1.3 * (2 ** RATIO_SCALE) const B8T = 0x0000; // 0.000 * (2 ** LUX_SCALE) const M8T = 0x0000; // 0.000 * (2 ** LUX_SCALE) // Auto-gain thresholds // Max value at Ti 13ms = 5047 // var AGT_LO_13MS = 100; // var AGT_HI_13MS = 4850; // // Max value at Ti 101ms = 37177 // var AGT_LO_101MS = 200; // var AGT_HI_101MS = 36000; // // Max value at Ti 402ms = 65535 // var AGT_LO_402MS = 500; // var AGT_HI_402MS = 63000; // var agtRanges = [ // // 0, TI_13MS // [100, 4850], // // 1, TI_101MS // [200, 36000], // // 2, TI_402MS // [500, 63000], // ]; // var CLIPPING_13MS = 4900; // var CLIPPING_101MS = 37000; // var CLIPPING_402MS = 65000; // var clipping = [ // // 0, TI_13MS // 4900, // // 1, TI_101MS // 37000, // // 2, TI_402MS // 65000, // ]; const GAIN_1X = 0x00; const GAIN_16X = 0x10; // var TI_13MS = 0x00; // var TI_101MS = 0x01; // var TI_402MS = 0x02; const TintMs = [ // 0, TI_13MS 13, // 1, TI_101MS 101, // 2, TI_402MS 402, ]; const TintDelayMs = [ // 0, TI_13MS 15, // 1, TI_101MS 120, // 2, TI_402MS 450, ]; // Page 23 - 28 // Simplified Lux Calculation // var CH_SCALE_D = 0x0010; // var CH_SCALE_0 = 0x7517; // var CH_SCALE_1 = 0x0FE7; const chScales = [ // 0, TI_13MS 0x07517, // 1, TI_101MS 0x00FE7, // 2, TI_402MS 0x10000, ]; // Gain and Tint defaults; let gain = GAIN_16X; let TintIndex = 0; let Tint = TintMs[TintIndex]; let lux = 0; // if (typeof options.gain !== "undefined") { // isAutoGain = false; // gain = options.gain; // } // if (typeof options.integration !== "undefined") { // isAutoGain = false; // Tint = options.integration; // } // TODO: reduce duplication here Object.defineProperties(this, { gain: { get() { return gain; }, set(value) { if (value !== GAIN_1X && value !== GAIN_16X) { throw new RangeError("Invalid gain. Expected one of: 0, 16"); } gain = value; this.io.i2cWriteReg(address, command(this.REGISTER.TIMING), TintIndex | gain); } }, integration: { get() { return Tint; }, set(value) { TintIndex = TintMs.indexOf(value); if (TintIndex === -1) { throw new RangeError("Invalid integration. Expected one of: 13, 101, 402"); } Tint = value; this.io.i2cWriteReg(address, command(this.REGISTER.TIMING), TintIndex | gain); } }, lux: { get() { return lux; } } }); // Assign the default gain and integration values // These are validated and written to the device. // These will invoke the accessors above. this.gain = gain; this.integration = Tint; // Page 1 // Description // Page 2 // Functional Block Diagram // var data = { // broadband: null, // infrared: null, // }; const read = () => { setTimeout(() => { // Page 19 // Read ADC Channels Using Read Word Protocol − RECOMMENDED this.io.i2cReadOnce(address, command(this.REGISTER.READ), 4, data => { // Page 23 - 28 // Simplified Lux Calculation let ch0 = uint16(data[1], data[0]); let ch1 = uint16(data[3], data[2]); let b = 0; let m = 0; // Page 26 // CalculateLux(...) let chScale = chScales[TintIndex]; if (!gain) { chScale = chScale << 4; } // Page 27 // CalculateLux(...) ch0 = (ch0 * chScale) >> 10; ch1 = (ch1 * chScale) >> 10; let ratio1 = 0; if (ch0) { ratio1 = (ch1 << (RATIO_SCALE + 1)) / ch0; } ratio1 = Math.round(ratio1); const ratio = (ratio1 + 1) >> 1; if (ratio >= 0 && ratio <= K1T) { b = B1T; m = M1T; } else if (ratio <= K2T) { b = B2T; m = M2T; } else if (ratio <= K3T) { b = B3T; m = M3T; } else if (ratio <= K4T) { b = B4T; m = M4T; } else if (ratio <= K5T) { b = B5T; m = M5T; } else if (ratio <= K6T) { b = B6T; m = M6T; } else if (ratio <= K7T) { b = B7T; m = M7T; } else if (ratio > K8T) { b = B8T; m = M8T; } // I followed the datasheet and it had no else clause here. let temp = (ch0 * b) - (ch1 * m); if (temp < 0) { temp = 0; } temp += 1 << (LUX_SCALE - 1); // Updating the `lux` binding // in the outer scope. lux = temp >>> LUX_SCALE; callback(lux); read(); }); }, TintDelayMs[TintIndex]); }; read(); } }, toLux: { value(value) { return value; }, }, toIntensityLevel: { value(value) { return toFixed(scale(value, 0, 17000, 0, 100) / 100, 2); }, }, }, BH1750: { // code based on Arduino library https://github.com/claws/BH1750 // currently only "continuous H-resolution" mode supported ADDRESSES: { value: [0x23, 0x5C] }, initialize: { value(options, callback) { const { Drivers } = require("./sip"); const address = Drivers.addressResolver(this, options); const mode = options.mode || 0x10; this.io.i2cConfig(options); this.io.i2cWrite(address, mode); const read = () => { setTimeout(() => { this.io.i2cReadOnce(address, 2, data => { callback(uint16(data[0], data[1])); read(); }); }, 120); }; read(); }, }, toLux: { value(value) { // Page 2 // H-Resolution Mode Resolution rHR - 1 - lx return Math.round(value / 1.2); }, }, toIntensityLevel: { value(value) { return toFixed(scale(value / 1.2, 0, 65535, 0, 100) / 100, 2); }, }, }, }; Controllers.ALSPT19 = Controllers["ALS-PT19"] = Controllers.DEFAULT; /** * Light * @constructor * */ class Light extends Withinable { constructor(options) { super(); Board.Component.call( this, options = Board.Options(options) ); Board.Controller.call(this, Controllers, options); let value = 0; let last = 0; let { freq = 25 } = options; priv.set(this, {}); if (!this.toIntensityLevel) { this.toIntensityLevel = options.toIntensityLevel || (x => x); } if (!this.toLux) { this.toLux = options.toLux || (x => x); } Object.defineProperties(this, { value: { get() { return value; }, }, level: { get() { return this.toIntensityLevel(value); }, }, }); /* istanbul ignore else */ if (typeof this.initialize === "function") { this.initialize(options, data => value = data); } if (typeof this.lux === "undefined") { Object.defineProperty(this, "lux", { get() { return this.toLux(value); }, }); } const data = { level: 0, lux: 0, }; setInterval(() => { data.level = this.level; data.lux = this.lux; this.emit("data", data); if (value !== last) { last = value; this.emit("change", data); } }, freq); } } /* istanbul ignore else */ if (process.env.IS_TEST_MODE) { Light.Controllers = Controllers; Light.purge = function() { priv.clear(); }; } module.exports = Light;