UNPKG

johnny-five

Version:

The JavaScript Arduino Programming Framework.

652 lines (565 loc) 13.3 kB
var Board = require("../lib/board.js"), events = require("events"), util = require("util"), __ = require("../lib/fn.js"); var priv = new WeakMap(), Devices; /** * Compass * @constructor * * five.Compass(); * * five.Compass({ * device: "HMC5883L", * freq: 50, * }); * * * Device Shorthands: * * "HMC5883L": new five.Magnetometer() * * * @param {Object} opts [description] * */ function Compass(opts) { if (!(this instanceof Compass)) { return new Compass(opts); } var address, bytes, data, descriptor, device, delay, last, properties, read, setup; // Initialize a Device instance on a Board Board.Device.call( this, opts = Board.Options(opts) ); device = Devices[opts.device]; address = device.address; bytes = device.bytes; data = device.data; delay = device.delay; setup = device.setup; properties = device.properties; // Read event throttling this.freq = opts.freq || 500; // Make private data entry priv.set(this, { x: 0, y: 0, z: 0 }); // Correct value set in scale() this.scale = 1; this.register = 0x00; Compass.scale.call(this, opts.gauss); // Set up I2C data connection this.io.sendI2CConfig(); // Enumerate and write each set of setup instructions setup.forEach(function(byteArray) { this.io.sendI2CWriteRequest(address, byteArray); }, this); this.setMaxListeners(100); // Read Request Loop setInterval(function() { // Set pointer to X most signficant byte this.io.sendI2CWriteRequest(address, [0x03]); // Read from register this.io.sendI2CReadRequest(address, bytes, data.bind(this)); // Emit "headingchange" events whenever the calculated // heading accessor changes // TODO: handling for 360/0 if (__.range(last - 1, last + 1).indexOf(Math.floor(this.heading)) === -1) { this.emit("headingchange", { now: this.heading, previous: last }); } last = Math.floor(this.heading); }.bind(this), delay); // "read" throttle loop setInterval(function() { // @DEPRECATE this.emit("read"); // The "read" event has been deprecated in // favor of a "data" event. this.emit("data"); }.bind(this), this.freq); descriptor = __.extend({}, { /** * raw x, y, z data * @name raw * @property * @type Object */ raw: { get: function() { var raw = priv.get(this); return { x: raw.x, y: raw.y, z: raw.z || null }; } }, /** * scaled x, y, z data * @name scaled * @property * @type Object */ scaled: { get: function() { var raw = priv.get(this); return { x: raw.x * this.scale, y: raw.y * this.scale, z: raw.z ? raw.z * this.scale : null }; } } }); // If this compass device has it's own properties // then merge with the defaults; if (properties) { descriptor = __.extend(descriptor, properties); } // Define instance accessors with merged descriptor properties Object.defineProperties(this, descriptor); } /** * Compass.scale Set the scale gauss for compass readings * @param {Number} gauss [description] * @return {register} [description] * * Ported from: * http://bildr.org/2012/02/hmc5883l_arduino/ */ Compass.scale = function(gauss) { if (!(this instanceof Compass)) { return; } if (gauss === 0.88) { this.register = 0x00; this.scale = 0.73; } else if (gauss === 1.3) { this.register = 0x01; this.scale = 0.92; } else if (gauss === 1.9) { this.register = 0x02; this.scale = 1.22; } else if (gauss === 2.5) { this.register = 0x03; this.scale = 1.52; } else if (gauss === 4.0) { this.register = 0x04; this.scale = 2.27; } else if (gauss === 4.7) { this.register = 0x05; this.scale = 2.56; } else if (gauss === 5.6) { this.register = 0x06; this.scale = 3.03; } else if (gauss === 8.1) { this.register = 0x07; this.scale = 4.35; } else { this.register = 0x00; this.scale = 1; } // Setting is in the top 3 bits of the register. this.register = this.register << 5; }; /** * Compass.Points * * 32 Point Compass * +1 for North * */ Compass.Points = [{ "point": "North", "abbr": "N", "low": 354.38, "mid": 360, "high": 360 }, { "point": "North", "abbr": "N", "low": 0, "mid": 0, "high": 5.62 }, { "point": "North by East", "abbr": "NbE", "low": 5.63, "mid": 11.25, "high": 16.87 }, { "point": "North-NorthEast", "abbr": "NNE", "low": 16.88, "mid": 22.5, "high": 28.12 }, { "point": "NorthEast by North", "abbr": "NEbN", "low": 28.13, "mid": 33.75, "high": 39.37 }, { "point": "NorthEast", "abbr": "NE", "low": 39.38, "mid": 45, "high": 50.62 }, { "point": "NorthEast by East", "abbr": "NEbE", "low": 50.63, "mid": 56.25, "high": 61.87 }, { "point": "East-NorthEast", "abbr": "ENE", "low": 61.88, "mid": 67.5, "high": 73.12 }, { "point": "East by North", "abbr": "EbN", "low": 73.13, "mid": 78.75, "high": 84.37 }, { "point": "East", "abbr": "E", "low": 84.38, "mid": 90, "high": 95.62 }, { "point": "East by South", "abbr": "EbS", "low": 95.63, "mid": 101.25, "high": 106.87 }, { "point": "East-SouthEast", "abbr": "ESE", "low": 106.88, "mid": 112.5, "high": 118.12 }, { "point": "SouthEast by East", "abbr": "SEbE", "low": 118.13, "mid": 123.75, "high": 129.37 }, { "point": "SouthEast", "abbr": "SE", "low": 129.38, "mid": 135, "high": 140.62 }, { "point": "SouthEast by South", "abbr": "SEbS", "low": 140.63, "mid": 146.25, "high": 151.87 }, { "point": "South-SouthEast", "abbr": "SSE", "low": 151.88, "mid": 157.5, "high": 163.12 }, { "point": "South by East", "abbr": "SbE", "low": 163.13, "mid": 168.75, "high": 174.37 }, { "point": "South", "abbr": "S", "low": 174.38, "mid": 180, "high": 185.62 }, { "point": "South by West", "abbr": "SbW", "low": 185.63, "mid": 191.25, "high": 196.87 }, { "point": "South-SouthWest", "abbr": "SSW", "low": 196.88, "mid": 202.5, "high": 208.12 }, { "point": "SouthWest by South", "abbr": "SWbS", "low": 208.13, "mid": 213.75, "high": 219.37 }, { "point": "SouthWest", "abbr": "SW", "low": 219.38, "mid": 225, "high": 230.62 }, { "point": "SouthWest by West", "abbr": "SWbW", "low": 230.63, "mid": 236.25, "high": 241.87 }, { "point": "West-SouthWest", "abbr": "WSW", "low": 241.88, "mid": 247.5, "high": 253.12 }, { "point": "West by South", "abbr": "WbS", "low": 253.13, "mid": 258.75, "high": 264.37 }, { "point": "West", "abbr": "W", "low": 264.38, "mid": 270, "high": 275.62 }, { "point": "West by North", "abbr": "WbN", "low": 275.63, "mid": 281.25, "high": 286.87 }, { "point": "West-NorthWest", "abbr": "WNW", "low": 286.88, "mid": 292.5, "high": 298.12 }, { "point": "NorthWest by West", "abbr": "NWbW", "low": 298.13, "mid": 303.75, "high": 309.37 }, { "point": "NorthWest", "abbr": "NW", "low": 309.38, "mid": 315.00, "high": 320.62 }, { "point": "NorthWest by North", "abbr": "NWbN", "low": 320.63, "mid": 326.25, "high": 331.87 }, { "point": "North-NorthWest", "abbr": "NNW", "low": 331.88, "mid": 337.5, "high": 343.12 }, { "point": "North by West", "abbr": "NbW", "low": 343.13, "mid": 348.75, "high": 354.37 }]; // Add ranges to each compass point record Compass.Points.forEach(function(point, k) { this[k].range = __.range(Math.floor(point.low), Math.floor(point.high)); }, Compass.Points); util.inherits(Compass, events.EventEmitter); Devices = { /** * HMC5883L: 3-Axis Compass Module * 0x1E * * https://sites.google.com/site/parallaxinretailstores/home/compass-module-3-axis-hmc5883l * * http://www51.honeywell.com/aero/common/documents/myaerospacecatalog-documents/Defense_Brochures-documents/HMC5883L_3-Axis_Digital_Compass_IC.pdf * P. 10,11,12,13 * * http://www.memsense.com/docs/MTD-0801_1_0_Calculating_Heading_Elevation_Bank_Angle.pdf * * https://www.loveelectronics.co.uk/Tutorials/13/tilt-compensated-compass-arduino-tutorial * */ "HMC5883L": { address: 0x1E, bytes: 6, delay: 100, // read request data handler data: function(data) { var raw = priv.get(this); // console.log( data ); raw.x = (data[0] << 8) | data[1]; raw.y = (data[4] << 8) | data[5]; raw.z = (data[2] << 8) | data[3]; // Negative number, nT spike correction // Derived and adapted from: // Jeff Hoefs' Breakout.js > MagnetoMeterHMC5883.js Object.keys(raw).forEach(function(key) { var val = raw[key]; raw[key] = val >> 15 ? ((val ^ 0xFFFF) + 1) * -1 : val; }); // console.log( raw ); priv.set(this, raw); }, // These are added to the property descriptors defined // within the constructor // Reference: // http://casanovasadventures.com/catalog/compass/p1409.htm // http://en.wikipedia.org/wiki/Boxing_the_compass // http://en.wikipedia.org/wiki/File:Compass_Card.png // http://en.wikipedia.org/wiki/Boxing_the_compass#Compass_point_names properties: { /** * [read-only] Bearing information * @name bearing * @property * @type Object * * name abbr low mid high heading * */ bearing: { get: function() { var k, len, heading, point; k = 0; len = Compass.Points.length; heading = Math.floor(this.heading); for (; k < len; k++) { point = Compass.Points[k]; if (point.range.indexOf(heading) !== -1) { // Specify fields to return to avoid returning the // range array (too much noisy data) return { name: point.point, abbr: point.abbr, low: point.low, mid: point.mid, high: point.high, heading: heading }; } } } }, /** * [read-only] Heading (azimuth) * @name heading * @property * @type number */ heading: { get: function() { var heading, raw, x, y, z; // Aquire raw x, y, z data from private data map raw = priv.get(this); x = raw.x; y = raw.y; z = raw.z; /** * * Applications of Magnetoresistive Sensors in Navigation Systems * by Michael J. Caruso of Honeywell Inc. * http://www.ssec.honeywell.com/position-sensors/datasheets/sae.pdf * * * Azimuth (x=0, y<0) = 90.0 (3) * Azimuth (x=0, y>0) = 270.0 * Azimuth (x<0) = 180 - [arcTan(y/x)]*180/PI * Azimuth (x>0, y<0) = - [arcTan(y/x)]*180/PI * Azimuth (x>0, y>0) = 360 - [arcTan(y/x)]*180/PI * * * * * */ /** * * * http://bildr.org/2012/02/hmc5883l_arduino/ * @type {[type]} * Copyright (C) 2011 Love Electronics (loveelectronics.co.uk) This program is free software: you can redistribute it and/or modify it under the terms of the version 3 GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ heading = Math.atan2(y, x); if (heading < 0) { heading += 2 * Math.PI; } if (heading > 2 * Math.PI) { heading -= 2 * Math.PI; } return heading * (180 / Math.PI); } } }, // http://torrentula.to.funpic.de/tag/i2c/ setup: [ // CRA [0x00, 0x70], // CRB [0x01, 0x40], // Continuous measurement mode [0x02, 0x00] ], preread: [ [0x03] ] }, /** * HMC6352: 2-Axis Compass Module * 0x42 * * http://www.sparkfun.com/datasheets/Components/HMC6352.pdf */ "HMC6352": { address: 0x42, bytes: 2, read: function(data) { var v; v = (data[1] << 8) | data[2]; // TODO: Find out if this device is still in production }, setup: [] } }; /** * Fires once every N ms, equal to value of `freq`. Defaults to 66ms * * @event * @name read * @memberOf Compass */ /** * Fires when the calculated heading has changed * * @event * @name headingchange * @memberOf Compass */ module.exports = Compass; // http://en.wikipedia.org/wiki/Relative_direction