johnny-five-electron
Version:
Temporary fork to support Electron (to be deprecated)
613 lines (540 loc) • 11.8 kB
JavaScript
var Board = require("../lib/board.js"),
Emitter = require("events").EventEmitter,
util = require("util"),
__ = require("../lib/fn.js"),
int16 = __.int16;
var priv = new Map();
var Controllers = {
/**
* 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: {
REGISTER: {
value: {
// Configuration Register A
CRA: 0x00,
// Configuration Register B
// This may change, depending on gauss
CRB: 0x01,
MEASUREMENT: 0x02,
READ: 0x03
}
},
initialize: {
value: function(opts, dataHandler) {
var state = priv.get(this);
var address = 0x1E;
var READLENGTH = 6;
state.scale = 1;
state.register = 0x40;
Object.assign(state, new Compass.Scale(opts.gauss || 0.88));
this.io.i2cConfig(opts);
// Set CRA
this.io.i2cWrite(address, this.REGISTER.CRA, 0x70);
// Set CRB
this.io.i2cWrite(address, this.REGISTER.CRB, state.register);
// Measurement: Continuous
this.io.i2cWrite(address, this.REGISTER.MEASUREMENT, 0x00);
// Initialize continuous read
this.io.i2cRead(address, this.REGISTER.READ, READLENGTH, function(bytes) {
dataHandler.call(this, {
x: int16(bytes[0], bytes[1]),
y: int16(bytes[4], bytes[5]),
z: int16(bytes[2], bytes[3]),
});
}.bind(this));
}
},
toScaledHeading: {
value: function(data) {
var x = data.x * data.scale;
var y = data.y * data.scale;
return ToHeading(x, y);
}
}
},
/**
* HMC6352: 2-Axis Compass Module
* 0x42
*
* http://www.sparkfun.com/datasheets/Components/HMC6352.pdf
* http://bildr.org/2011/01/hmc6352/
*/
HMC6352: {
REGISTER: {
value: {
READ: 0x41
}
},
initialize: {
value: function(opts, dataHandler) {
var state = priv.get(this);
var address = 0x42 >> 1; // 0x42 >> 1
var READLENGTH = 2;
state.scale = 1;
opts.delay = 10;
this.io.i2cConfig(opts);
this.io.i2cWrite(address, this.REGISTER.READ);
// Initialize continuous read
this.io.i2cRead(address, this.REGISTER.READ, READLENGTH, function(bytes) {
dataHandler.call(this, {
x: (((bytes[0] << 8) + bytes[1]) / 10) | 0,
y: null,
z: null,
});
}.bind(this));
}
},
toScaledHeading: {
value: function(data) {
return data.x * data.scale;
}
}
},
};
/**
* Compass
* @constructor
*
* five.Compass();
*
* five.Compass({
* controller: "HMC5883L",
* freq: 50,
* });
*
*
* Device Shorthands:
*
* "HMC5883L": new five.Magnetometer()
*
*
* @param {Object} opts [description]
*
*/
function Compass(opts) {
if (!(this instanceof Compass)) {
return new Compass(opts);
}
Board.Component.call(
this, opts = Board.Options(opts)
);
var freq = opts.freq || 25;
var controller = null;
var raw = null;
var state = {
x: 0,
y: 0,
z: 0,
scale: 0,
register: 0,
heading: 0
};
if (opts.controller && typeof opts.controller === "string") {
controller = Controllers[opts.controller.toUpperCase()];
} else {
controller = opts.controller;
}
if (controller === null || typeof controller !== "object") {
throw new Error("Missing valid Compass controller");
}
Object.defineProperties(this, controller);
if (!this.toScaledHeading) {
this.toScaledHeading = opts.toScaledHeading || function(raw) { return raw; };
}
priv.set(this, state);
if (typeof this.initialize === "function") {
this.initialize(opts, function(data) {
raw = data;
});
}
setInterval(function() {
if (raw === null) {
return;
}
var isChange = false;
state.x = raw.x;
state.y = raw.y;
state.z = raw.z;
var heading = this.heading;
if (heading !== state.heading) {
state.heading = heading;
isChange = true;
}
this.emit("data", {
heading: state.heading
});
if (isChange) {
this.emit("change", {
heading: state.heading
});
}
}.bind(this), freq);
}
util.inherits(Compass, Emitter);
Object.defineProperties(Compass.prototype, {
/**
* [read-only] Bearing information
* @name bearing
* @property
* @type Object
*
*
name
abbr
low
mid
high
heading
*
*/
bearing: {
get: function() {
var length = Compass.Points.length;
var heading = Math.floor(this.heading);
var point;
for (var i = 0; i < length; i++) {
point = Compass.Points[i];
if (point.range.includes(heading)) {
// 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 state = priv.get(this);
return this.toScaledHeading(state);
}
}
});
function ToHeading(x, y) {
/**
*
* 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/>.
*/
var 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);
}
/**
* 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 (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);
/**
* 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