/**
* Copyright (c) 2012 Jeff Hoefs <soundanalogous@gmail.com>
* Released under the MIT license. See LICENSE file for details.
*/
JSUTILS.namespace('BO.io.MagnetometerHMC5883');
BO.io.MagnetometerHMC5883 = (function() {
var MagnetometerHMC5883;
// private static constants
var RAD_TO_DEG = 180 / Math.PI,
DEG_TO_RAD = Math.PI / 180,
ADDRESS = 0x1E,
CRA = 0x00,
CRB = 0x01,
MODE = 0x02,
DATAX0 = 0x03,
NUM_BYTES = 6;
// dependencies
var I2CBase = BO.I2CBase,
MagnetometerEvent = BO.io.MagnetometerEvent;
/**
* Creates an interface to an HMC5883 3-axis magnetometer. Use the
* magnetometer to obtain a compass heading or rotation in relation to
* a fixed point. See [Breakout/examples/sensors/hmc5883.html](https://github.com/soundanalogous/Breakout/blob/master/examples/sensors/hmc5883.html) for an example
* application.
*
* @class MagnetometerHMC5883
* @constructor
* @extends BO.I2CBase
* @param {IOBoard} board The IOBoard instance
* @param {Number} address The i2c address of the compass module
* @param {Number} numSamples The number of samples averaged per
* measurement output. Options are: `MagnetometerHMC5883.SAMPLES_1`,
* `MagnetometerHMC5883.SAMPLES_2`, `MagnetometerHMC5883.SAMPLES_4`
* `MagnetometerHMC5883.SAMPLES_8` (default = `MagnetometerHMC5883.SAMPLES_1`)
* @param {Number} outputRate The data output rate in Hz
* (default = `MagnetometerHMC5883.HZ_30`)
*/
MagnetometerHMC5883 = function(board, address, numSamples, outputRate) {
address = address || MagnetometerHMC5883.DEVICE_ID;
numSamples = numSamples || MagnetometerHMC5883.SAMPLES_1;
outputRate = outputRate || MagnetometerHMC5883.HZ_30;
I2CBase.call(this, board, address);
this._x = 0;
this._y = 0;
this._z = 0;
// To do: scale is currently fixed. allow user to change this value?
this._scale = 0.92; // mG/LSb
this._isReading = false;
this._debugMode = BO.enableDebugging;
this.name = "MagnetometerHMC5883";
var measurement = 0x00;
var CRAVal = (numSamples << 5) | (outputRate << 2) | measurement;
// 1 sample, continuous measurement rate 30 Hz, normal measurement config
this.sendI2CRequest([I2CBase.WRITE, this.address, CRA, CRAVal]);
// startup in continuous measurement mode
this.sendI2CRequest([I2CBase.WRITE, this.address, MODE, 0x00]);
this.startReading();
};
MagnetometerHMC5883.prototype = JSUTILS.inherit(I2CBase.prototype);
MagnetometerHMC5883.prototype.constructor = MagnetometerHMC5883;
Object.defineProperties(MagnetometerHMC5883.prototype, {
/**
* [read-only] The heading in degrees.
* @property heading
* @type Number
*/
heading: {
get: function() {
return this.getHeading(this._x, this._y);
}
},
/**
* [read-only] The x-axis measurement
* @property x
* @type Number
*/
x: {
get: function() {
return this._x;
}
},
/**
* [read-only] The y-axis measurement
* @property y
* @type Number
*/
y: {
get: function() {
return this._y;
}
},
/**
* [read-only] The z-axis measurement
* @property z
* @type Number
*/
z: {
get: function() {
return this._z;
}
}
});
/**
* @private
* @method handleI2C
*/
MagnetometerHMC5883.prototype.handleI2C = function(data) {
var xVal,
yVal,
zVal;
// data[0] = register
if (data[0] === DATAX0) {
xVal = (data[1] << 8) | data[2];
zVal = (data[3] << 8) | data[4];
yVal = (data[5] << 8) | data[6];
// correct for negative number
if (xVal >> 15) {
this._x = ((xVal ^ 0xFFFF) + 1) * -1;
} else {
this._x = xVal;
}
if (yVal >> 15) {
this._y = ((yVal ^ 0xFFFF) + 1) * -1;
} else {
this._y = yVal;
}
if (zVal >> 15) {
this._z = ((zVal ^ 0xFFFF) + 1) * -1;
} else {
this._z = zVal;
}
// a value of -4096 indicates an ADC overflow or underflow
this.dispatchEvent(new MagnetometerEvent(MagnetometerEvent.UPDATE));
} else {
console.log("Warning: MagnetometerHMC5883 received data from unknown register");
}
};
/**
* @private
* @method getHeading
*/
MagnetometerHMC5883.prototype.getHeading = function(x, y) {
var heading = 0.0;
// algorithm from Applications of Magnetoresistive Sensors in Navigation Systems
// by Michael J. Caruso of Honeywell Inc.
if (y > 0) {
heading = 90.0 - Math.atan(x / y) * 180 / Math.PI;
} else if (y < 0) {
heading = 270.0 - Math.atan(x / y) * 180 / Math.PI;
} else if (y === 0 && x < 0) {
heading = 180.0;
} else if (y === 0 && x > 0) {
heading = 0.0;
}
// alternate algorithm
// heading = Math.atan2(y, x);
// if (heading < 0) heading += 2*Math.PI;
// if (heading > 2*Math.PI) heading -= 2*Math.PI;
// return heading * RAD_TO_DEG;
return heading;
};
/**
* Get a tilt-compensated heading. Pitch and roll values from an accelerometer
* must be passed to this method.
*
* Note: this method is not working properly. Marking it private until resolved
* @private
* @method getTiltCompensatedHeading
* @param {Number} pitch The pitch value (supplied by an accelerometer)
* @param {Number} roll The roll value (supplied by an accelerometer)
* @return {Number} tilt-compensated heading direction
*/
MagnetometerHMC5883.prototype.getTiltCompensatedHeading = function(pitch, roll) {
pitch = pitch * DEG_TO_RAD;
roll = roll * DEG_TO_RAD;
//var xH = this._x * Math.cos(pitch) + this._z * Math.sin(pitch);
//var yH = this._x * Math.sin(roll) * Math.sin(pitch) + this._y * Math.cos(roll) - this._z * Math.sin(roll) * Math.cos(pitch);
//var zH = -this._x * Math.cos(roll) * Math.sin(pitch) + this._y * Math.sin(roll) + this._z * Math.cos(roll) * Math.cos(pitch);
// algorithm from: Applications of Magnetoresistive Sensors in Navigation Systems
// by Michael J. Caruso, Honeywell Inc.
var xH = this._x * Math.cos(pitch) + this._y * Math.sin(roll) * Math.sin(pitch) - this._z * Math.cos(roll) * Math.sin(pitch);
var yH = this._y * Math.cos(roll) + this._z * Math.sin(roll);
return this.getHeading(xH, yH);
};
/**
* Start continuous reading of the sensor.
* @method startReading
*/
MagnetometerHMC5883.prototype.startReading = function() {
if (!this._isReading) {
this._isReading = true;
this.sendI2CRequest([I2CBase.READ_CONTINUOUS, this.address, DATAX0, 6]);
}
};
/**
* Stop continuous reading of the sensor.
* @method stopReading
*/
MagnetometerHMC5883.prototype.stopReading = function() {
this._isReading = false;
this.sendI2CRequest([I2CBase.STOP_READING, this.address]);
// set idle mode?
//this.sendI2CRequest([I2CBase.WRITE, this.address, MODE, 0x03]);
};
/**
* Sends read request to magnetometer and updates magnetometer values.
* @method update
*/
MagnetometerHMC5883.prototype.update = function() {
if (this._isReading) {
this.stopReading();
}
// read data: contents of X, Y, and Z registers
this.sendI2CRequest([I2CBase.READ, this.address, DATAX0, NUM_BYTES]);
};
/**
* for debugging
* @private
*/
MagnetometerHMC5883.prototype.debug = function(str) {
if (this._debugMode) {
console.log(str);
}
};
// public static constants
/**
* @property MagnetometerHMC5883.DEVICE_ID
* @static
*/
MagnetometerHMC5883.DEVICE_ID = 0x1E;
/**
* @property MagnetometerHMC5883.SAMPLES_1
* @static
*/
MagnetometerHMC5883.SAMPLES_1 = 0;
/**
* @property MagnetometerHMC5883.SAMPLES_2
* @static
*/
MagnetometerHMC5883.SAMPLES_2 = 1;
/**
* @property MagnetometerHMC5883.SAMPLES_4
* @static
*/
MagnetometerHMC5883.SAMPLES_4 = 2;
/**
* @property MagnetometerHMC5883.SAMPLES_8
* @static
*/
MagnetometerHMC5883.SAMPLES_8 = 3;
/** 0.75 Hz
* @property MagnetometerHMC5883.HZ_0_75
* @static
*/
MagnetometerHMC5883.HZ_0_75 = 0x00;
/** 1.5 Hz
* @property MagnetometerHMC5883.HZ_1_5
* @static
*/
MagnetometerHMC5883.HZ_1_5 = 0x01;
/** 3 Hz
* @property MagnetometerHMC5883.HZ_3
* @static
*/
MagnetometerHMC5883.HZ_3 = 0x02;
/** 7.5 Hz
* @property MagnetometerHMC5883.HZ_7_5
* @static
*/
MagnetometerHMC5883.HZ_7_5 = 0x03;
/** 15 Hz
* @property MagnetometerHMC5883.HZ_15
* @static
*/
MagnetometerHMC5883.HZ_15 = 0x04;
/** 30 Hz
* @property MagnetometerHMC5883.HZ_30
* @static
*/
MagnetometerHMC5883.HZ_30 = 0x05;
/** 75 Hz
* @property MagnetometerHMC5883.HZ_75
* @static
*/
MagnetometerHMC5883.HZ_75 = 0x06;
// document events
/**
* The update event is dispatched when the compass heading is updated.
* @type BO.io.MagnetometerEvent.UPDATE
* @event update
* @param {BO.io.MagnetometerHMC5883} target A reference to the MagnetometerHMC5883 object.
*/
return MagnetometerHMC5883;
}());