Skip to content

Instantly share code, notes, and snippets.

@satakagi
Created November 26, 2018 12:09
Show Gist options
  • Save satakagi/493d4e6277c92080c60dffb7c1f17926 to your computer and use it in GitHub Desktop.
Save satakagi/493d4e6277c92080c60dffb7c1f17926 to your computer and use it in GitHub Desktop.
MPU9250/MPU9255 driver
// Ported from https://github.com/tuupola/micropython-mpu9250/blob/master/ak8963.py
// by Satoru Takagi
var AK8963 = function(i2cPort,slaveAddress){
this.devConst={
_WIA : (0x00),
_HXL : (0x03),
_HXH : (0x04),
_HYL : (0x05),
_HYH : (0x06),
_HZL : (0x07),
_HZH : (0x08),
_ST2 : (0x09),
_CNTL1 : (0x0a),
_ASAX : (0x10),
_ASAY : (0x11),
_ASAZ : (0x12),
_MODE_POWER_DOWN : 0b00000000,
MODE_SINGLE_MEASURE : 0b00000001,
MODE_CONTINOUS_MEASURE_1 : 0b00000010, // 8Hz
MODE_CONTINOUS_MEASURE_2 : 0b00000110, // 100Hz
MODE_EXTERNAL_TRIGGER_MEASURE : 0b00000100,
_MODE_SELF_TEST : 0b00001000,
_MODE_FUSE_ROM_ACCESS : 0b00001111,
OUTPUT_14_BIT : 0b00000000,
OUTPUT_16_BIT : 0b00010000,
_SO_14BIT : 0.6, // μT per digit when 14bit mode
_SO_16BIT : 0.15 // μT per digit when 16bit mode
}
this.i2cPort = i2cPort;
this.i2cSlave = null;
if ( slaveAddress ){
this.slaveAddress = slaveAddress;
} else {
this.slaveAddress = 0x0c;
}
};
AK8963.prototype = {
init: async function(mode,output,offset,scale){
var cs = this.devConst;
if ( !mode){
mode=cs.MODE_CONTINOUS_MEASURE_1;
}
if ( !output){
output=cs.OUTPUT_16_BIT;
}
if ( !offset ){
offset=[0, 0, 0];
}
if ( !scale){
scale=[1, 1, 1];
}
this._offset = offset;
this._scale = scale;
this.i2cSlave = await this.i2cPort.open(this.slaveAddress);
var whoamiVal = await this.i2cSlave.read8(cs._WIA);
if ( whoamiVal ==0x48){
console.log("This device is AK8963 magnetometer");
} else {
console.log("This device is NOT Supported...");
return(null);
}
// Sensitivity adjustement values
await this.i2cSlave.write8(cs._CNTL1, cs._MODE_FUSE_ROM_ACCESS);
var asax = await this.i2cSlave.read8(cs._ASAX);
var asay = await this.i2cSlave.read8(cs._ASAY);
var asaz = await this.i2cSlave.read8(cs._ASAZ);
await this.i2cSlave.write8(cs._CNTL1, cs._MODE_POWER_DOWN);
// Should wait atleast 100us before next mode
this._adjustement = [
(0.5 * (asax - 128)) / 128 + 1,
(0.5 * (asay - 128)) / 128 + 1,
(0.5 * (asaz - 128)) / 128 + 1
];
// Power on
await this.i2cSlave.write8(cs._CNTL1, (mode | output));
if (output == cs.OUTPUT_16_BIT){
this._so = cs._SO_16BIT;
} else {
this._so = cs._SO_14BIT;
}
console.log("init AK8963 ok: this._adjustement :",this._adjustement ," this._so:",this._so ," this.cs:",cs);
},
readData: async function(){
var cs = this.devConst;
// X, Y, Z axis micro-Tesla (uT) as floats.
var mx = this.getVal(await this.i2cSlave.read16(cs._HXL)); // read16 is little endian
var my = this.getVal(await this.i2cSlave.read16(cs._HYL));
var mz = this.getVal(await this.i2cSlave.read16(cs._HZL));
console.log(mx,my,mz);
await this.i2cSlave.read8(cs._ST2) // Enable updating readings again
//Apply factory axial sensitivy adjustements
mx *= this._adjustement[0];
my *= this._adjustement[1];
mz *= this._adjustement[2];
// Apply output scale determined in constructor
var so = this._so;
mx *= so;
my *= so;
mz *= so;
// Apply hard iron ie. offset bias from calibration
mx -= this._offset[0];
my -= this._offset[1];
mz -= this._offset[2];
// Apply soft iron ie. scale bias from calibration
mx *= this._scale[0];
my *= this._scale[1];
mz *= this._scale[2];
return {
x: mx,
y: my,
z: mz
}
},
getVal: function( w ){
// Convert to signed integer while maintaining endianness
// console.log("getVal:",w.toString(16)," :" , w);
// console.log("getVal:",w);
// var l = w >>> 8;
// var b = w & 0xff;
// var v = l + ( b << 8 );
var v = w;
// console.log("Val:",w.toString(16),b.toString(16),l.toString(16),v.toString(16),b,l,v);
if ( v >= 0x8000 ){
return ( - ((65535 - v ) + 1 ) );
} else {
return(v);
}
},
calibrate: async function (count, delay){
console.log("start calibrate: ");
if ( !count){
count=128;
}
if ( !delay){
delay=100;
}
this._offset = [0, 0, 0];
this._scale = [1, 1, 1];
var reading = await this.readData();
var minx = reading.x;
var miny = reading.y;
var minz = reading.z;
var maxx = minx;
var maxy = miny;
var maxz = minz;
while (count>0){
await sleep(delay);
reading = await this.readData();
minx = Math.min(minx, reading.x);
maxx = Math.max(maxx, reading.x);
miny = Math.min(miny, reading.y);
maxy = Math.max(maxy, reading.y);
minz = Math.min(minz, reading.z);
maxz = Math.max(maxz, reading.z);
count -= 1;
console.log(count," : " , reading);
}
// Hard iron correction
var offset_x = (maxx + minx) / 2;
var offset_y = (maxy + miny) / 2;
var offset_z = (maxz + minz) / 2;
this._offset = [offset_x, offset_y, offset_z];
// Soft iron correction
var avg_delta_x = (maxx - minx) / 2;
var avg_delta_y = (maxy - miny) / 2;
var avg_delta_z = (maxz - minz) / 2;
var avg_delta = (avg_delta_x + avg_delta_y + avg_delta_z) / 3;
var scale_x = avg_delta / avg_delta_x;
var scale_y = avg_delta / avg_delta_y;
var scale_z = avg_delta / avg_delta_z;
this._scale = [scale_x, scale_y, scale_z];
console.log("end calibrate: offset:",this._offset," scale:",this._scale );
// return self._offset, self._scale
}
};
// MPU 6500 driver
// Ported from
// https://raw.githubusercontent.com/tuupola/micropython-mpu9250/master/mpu6500.py
// https://github.com/tuupola/micropython-mpu9250/blob/master/mpu6500.py
// https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf
// https://github.com/MomsFriendlyRobotCompany/mpu9250/blob/master/mpu9250/mpu9250.py
// By Satoru Takagi
// Note: MPU9250 ( or MPU9255(almost equal to 9250)) is a chip in which MPU 6500 and AK8963 are integrated.
// When initializing the MPU6500 first, AK8963 with address 0x0C appears on the I2C bus.
var MPU6500 = function(i2cPort,slaveAddress){
this.devConst={
_GYRO_CONFIG : 0x1b,
_ACCEL_CONFIG : 0x1c,
_ACCEL_CONFIG2 : 0x1d,
_INT_PIN_CFG : 0x37,
_ACCEL_XOUT_H : 0x3b,
_ACCEL_XOUT_L : 0x3c,
_ACCEL_YOUT_H : 0x3d,
_ACCEL_YOUT_L : 0x3e,
_ACCEL_ZOUT_H : 0x3f,
_ACCEL_ZOUT_L: 0x40,
_TEMP_OUT_H : 0x41,
_TEMP_OUT_L : 0x42,
_GYRO_XOUT_H : 0x43,
_GYRO_XOUT_L : 0x44,
_GYRO_YOUT_H : 0x45,
_GYRO_YOUT_L : 0x46,
_GYRO_ZOUT_H : 0x47,
_GYRO_ZOUT_L : 0x48,
_WHO_AM_I : 0x75,
// #_ACCEL_FS_MASK : 0b00011000,
ACCEL_FS_SEL_2G : 0b00000000,
ACCEL_FS_SEL_4G : 0b00001000,
ACCEL_FS_SEL_8G : 0b00010000,
ACCEL_FS_SEL_16G : 0b00011000,
_ACCEL_SO_2G : 16384, // 1 / 16384 ie. 0.061 mg / digit
_ACCEL_SO_4G : 8192, // 1 / 8192 ie. 0.122 mg / digit
_ACCEL_SO_8G : 4096, // 1 / 4096 ie. 0.244 mg / digit
_ACCEL_SO_16G : 2048, // 1 / 2048 ie. 0.488 mg / digit
// #_GYRO_FS_MASK : 0b00011000,
GYRO_FS_SEL_250DPS : 0b00000000,
GYRO_FS_SEL_500DPS : 0b00001000,
GYRO_FS_SEL_1000DPS : 0b00010000,
GYRO_FS_SEL_2000DPS : 0b00011000,
_GYRO_SO_250DPS : 131,
_GYRO_SO_500DPS : 62.5,
_GYRO_SO_1000DPS : 32.8,
_GYRO_SO_2000DPS : 16.4,
// # Used for enablind and disabling the i2c bypass access
_I2C_BYPASS_MASK : 0b00000010,
_I2C_BYPASS_EN : 0b00000010,
_I2C_BYPASS_DIS : 0b00000000,
SF_G : 1,
SF_M_S2 : 9.80665,// # 1 g : 9.80665 m/s2 ie. standard gravity
SF_DEG_S : 1,
SF_RAD_S : 57.295779578552// # 1 rad/s is 57.295779578552 deg/s
}
this.i2cPort = i2cPort;
this.i2cSlave = null;
if ( slaveAddress ){
this.slaveAddress = slaveAddress;
} else {
this.slaveAddress = 0x68;
}
};
MPU6500.prototype = {
init: async function(accel_fs,gyro_fs,accel_sf,gyro_sf){
var cs = this.devConst;
if ( !accel_fs){
accel_fs=cs.ACCEL_FS_SEL_2G;
}
if ( !gyro_fs){
gyro_fs=cs.GYRO_FS_SEL_250DPS;
}
if ( !accel_sf ){
accel_sf=cs.SF_M_S2;
}
if ( !gyro_sf){
gyro_sf=cs.SF_RAD_S;
}
this.i2cSlave = await this.i2cPort.open(this.slaveAddress);
var whoamiVal = await this.i2cSlave.read8(cs._WHO_AM_I);
if ( whoamiVal ==0x70){
console.log("This device is MPU6500");
} else if ( whoamiVal ==0x71){
console.log("This device is MPU9250");
} else if ( whoamiVal ==0x73){
// https://github.com/kriswiner/MPU9250/issues/47
console.log("This device is MPU9255");
} else {
console.log("This device is NOT Supported...");
return(null);
}
cs._accel_so = await this._accel_fs(accel_fs);
cs._gyro_so = await this._gyro_fs(gyro_fs);
cs._accel_sf = accel_sf;
cs._gyro_sf = gyro_sf;
console.log("init ok:"+this.i2cSlave+ " constant:",cs);
// await this.i2cSlave.write8(0x6B, 0x00); // clear power management
// # Enable I2C bypass to access for MPU9250 magnetometer access.
var chr = await this.i2cSlave.read8(cs._INT_PIN_CFG);
console.log("bypass:",chr);
chr &= ~cs._I2C_BYPASS_MASK; //# clear I2C bits
chr |= cs._I2C_BYPASS_EN;
console.log("bypass_:",chr);
await this.i2cSlave.write8(cs._INT_PIN_CFG, chr);
console.log("init2 ok:");
},
_accel_fs: async function(value){
var cs = this.devConst;
await this.i2cSlave.write8(cs._ACCEL_CONFIG, value);
console.log("_accel_fs:",value);
//# Return the sensitivity divider
if (cs.ACCEL_FS_SEL_2G == value){
console.log("ACCEL_FS_SEL_2G::sel");
return cs._ACCEL_SO_2G;
} else if (cs.ACCEL_FS_SEL_4G == value){
return cs._ACCEL_SO_4G;
} else if (cs.ACCEL_FS_SEL_8G == value){
return cs._ACCEL_SO_8G;
} else if (cs.ACCEL_FS_SEL_16G == value){
return cs._ACCEL_SO_16G;
}
},
_gyro_fs: async function( value){
var cs = this.devConst;
await this.i2cSlave.write8(cs._GYRO_CONFIG, value);
//# Return the sensitivity divider
if (cs.GYRO_FS_SEL_250DPS == value){
return cs._GYRO_SO_250DPS;
} else if (cs.GYRO_FS_SEL_500DPS == value){
return cs._GYRO_SO_500DPS;
} else if (cs.GYRO_FS_SEL_1000DPS == value){
return cs._GYRO_SO_1000DPS;
} else if (cs.GYRO_FS_SEL_2000DPS == value){
return cs._GYRO_SO_2000DPS;
}
},
getAcceleration: async function(self){
/**
Acceleration measured by the sensor. By default will return a
3-tuple of X, Y, Z axis acceleration values in m/s^2 as floats. Will
return values in g if constructor was provided `accel_sf=SF_M_S2`
parameter.
**/
var cs = this.devConst;
var so = cs._accel_so;
var sf = cs._accel_sf;
var x = this.getVal(await this.i2cSlave.read16(cs._ACCEL_XOUT_H));
var y = this.getVal(await this.i2cSlave.read16(cs._ACCEL_YOUT_H));
var z = this.getVal(await this.i2cSlave.read16(cs._ACCEL_ZOUT_H));
console.log("acc:",x,y,z," addr:",cs._ACCEL_XOUT_H," sosf:",so,sf);
return {
x: x / so * sf,
y: y / so * sf,
z: z / so * sf
}
},
getGyro: async function(self){
/**
X, Y, Z radians per second as floats.
**/
var cs = this.devConst;
var so = cs._gyro_so;
var sf = cs._gyro_sf;
var x = this.getVal(await this.i2cSlave.read16(cs._GYRO_XOUT_H));
var y = this.getVal(await this.i2cSlave.read16(cs._GYRO_YOUT_H));
var z = this.getVal(await this.i2cSlave.read16(cs._GYRO_ZOUT_H));
console.log("gyr:",x,y,z," addr:",cs._GYRO_XOUT_H," sosf:",so,sf);
return {
x: x / so * sf,
y: y / so * sf,
z: z / so * sf
}
},
getVal: function( w ){
var l = w >>> 8;
var b = w & 0xff;
var v = l + ( b << 8 );
// console.log("Val:",w.toString(16),b.toString(16),l.toString(16),v.toString(16),b,l,v);
if ( v >= 0x8000 ){
return ( - ((65535 - v ) + 1 ) );
} else {
return(v);
}
}
};
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<title>i2c-MPU9250</title>
<script src="../../../polyfill/polyfill.js"></script>
<script src="../../drivers/i2c-MPU6500.js"></script>
<script src="../../drivers/i2c-AK8963.js"></script>
<script src="./mainMpu92.js"></script>
<style>
table,p {
color: blue;
text-align: center;
font-size: 24px;
}
img {
position: absolute;
top: 50px;
right: 0px;
display: block;
margin-left: auto;
margin-right: auto;
}
</style>
</head>
<body>
<img src="schematic.png" width="400">
<p id="head">MPU-9250</p>
<table>
<tr><td>temp</td><td id="temp"></td></tr>
<tr><td>Gx</td><td id="gx"></td></tr>
<tr><td>Gy</td><td id="gy"></td></tr>
<tr><td>Gz</td><td id="gz"></td></tr>
<tr><td>Rx</td><td id="rx"></td></tr>
<tr><td>Ry</td><td id="ry"></td></tr>
<tr><td>Rz</td><td id="rz"></td></tr>
<tr><td>Hx</td><td id="hx"></td></tr>
<tr><td>Hy</td><td id="hy"></td></tr>
<tr><td>Hz</td><td id="hz"></td></tr>
</table>
</body>
</html>
"use strict";
var head;
window.addEventListener(
"load",
function() {
head = document.querySelector("#head");
mainFunction();
},
false
);
async function mainFunction() {
try {
var i2cAccess = await navigator.requestI2CAccess();
var port = i2cAccess.ports.get(1);
var mpu6500 = new MPU6500(port, 0x68);
var ak8963 = new AK8963(port, 0x0c);
await mpu6500.init();
await ak8963.init();
while (1) {
var val0 = await mpu6500.getAcceleration();
var val1 = await mpu6500.getGyro();
var val2 = await ak8963.readData();
// console.log('value:', value);
gx.innerHTML=val0.x;
gy.innerHTML=val0.y;
gz.innerHTML=val0.z;
rx.innerHTML=val1.x;
ry.innerHTML=val1.y;
rz.innerHTML=val1.z;
hx.innerHTML=val2.x;
hy.innerHTML=val2.y;
hz.innerHTML=val2.z;
await sleep(600);
}
} catch (error) {
console.error("error", error);
}
}
function sleep(ms) {
return new Promise(function(resolve) {
setTimeout(resolve, ms);
});
}
@satakagi
Copy link
Author

satakagi commented Nov 29, 2018

実体配線図
mpu9250

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment