openi2c
Version:
This library is a set of cross platform drivers for common I2C devices.
560 lines (472 loc) • 18.2 kB
text/typescript
import * as i2c from 'i2c-bus';
import { sleep } from '../../utils';
import { AK8963, ak8963 } from '../AK8963';
import { BaseDevice } from '../BaseDevice';
// Interface for MPU9250 configuration
interface MPU9250Config {
// device: string;
bus: number;
address: number;
UpMagneto: boolean;
DEBUG: boolean;
scaleValues: boolean;
ak_address: number;
GYRO_FS: number;
ACCEL_FS: number;
gyroBiasOffset: Offset;
accelCalibration: AccelCalibration;
DLPF_CFG?: number;
A_DLPF_CFG?: number;
SAMPLE_RATE?: number;
}
// Interface for Offset
interface Offset {
x: number;
y: number;
z: number;
}
// Interface for AccelCalibration
interface AccelCalibration {
offset: Offset;
scale: Scale;
}
// Interface for Scale
interface Scale {
x: number[];
y: number[];
z: number[];
}
// MPU9250 Map
export const MPU9250 = {
ID_MPU_9250: 0x71,
ID_MPU_9255: 0x73,
I2C_ADDRESS_AD0_LOW: 0x68,
I2C_ADDRESS_AD0_HIGH: 0x69,
WHO_AM_I: 0x75,
SMPLRT_DIV: 0x19,
RA_CONFIG: 0x1A,
RA_GYRO_CONFIG: 0x1B,
RA_ACCEL_CONFIG_1: 0x1C,
RA_ACCEL_CONFIG_2: 0x1D,
RA_INT_PIN_CFG: 0x37,
INTCFG_ACTL_BIT: 7,
INTCFG_OPEN_BIT: 6,
INTCFG_LATCH_INT_EN_BIT: 5,
INTCFG_INT_ANYRD_2CLEAR_BIT: 4,
INTCFG_ACTL_FSYNC_BIT: 3,
INTCFG_FSYNC_INT_MODE_EN_BIT: 2,
INTCFG_BYPASS_EN_BIT: 1,
INTCFG_NONE_BIT: 0,
// BY_PASS_MODE: 0x02,
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,
RA_USER_CTRL: 0x6A,
RA_PWR_MGMT_1: 0x6B,
RA_PWR_MGMT_2: 0x6C,
PWR1_DEVICE_RESET_BIT: 7,
PWR1_SLEEP_BIT: 6,
PWR1_CYCLE_BIT: 5,
PWR1_TEMP_DIS_BIT: 3, // (PD_PTAT)
PWR1_CLKSEL_BIT: 0,
PWR1_CLKSEL_LENGTH: 3,
GCONFIG_FS_SEL_BIT: 3,
GCONFIG_FS_SEL_LENGTH: 2,
GYRO_FS_250: 0x00,
GYRO_FS_500: 0x01,
GYRO_FS_1000: 0x02,
GYRO_FS_2000: 0x03,
GYRO_SCALE_FACTOR: [131, 65.5, 32.8, 16.4],
ACONFIG_FS_SEL_BIT: 3,
ACONFIG_FS_SEL_LENGTH: 2,
ACCEL_FS_2: 0x00,
ACCEL_FS_4: 0x01,
ACCEL_FS_8: 0x02,
ACCEL_FS_16: 0x03,
ACCEL_SCALE_FACTOR: [16384, 8192, 4096, 2048],
CLOCK_INTERNAL: 0x00,
CLOCK_PLL_XGYRO: 0x01,
CLOCK_PLL_YGYRO: 0x02,
CLOCK_PLL_ZGYRO: 0x03,
CLOCK_KEEP_RESET: 0x07,
CLOCK_PLL_EXT32K: 0x04,
CLOCK_PLL_EXT19M: 0x05,
I2C_SLV0_DO: 0x63,
I2C_SLV1_DO: 0x64,
I2C_SLV2_DO: 0x65,
USERCTRL_DMP_EN_BIT: 7,
USERCTRL_FIFO_EN_BIT: 6,
USERCTRL_I2C_MST_EN_BIT: 5,
USERCTRL_I2C_IF_DIS_BIT: 4,
USERCTRL_DMP_RESET_BIT: 3,
USERCTRL_FIFO_RESET_BIT: 2,
USERCTRL_I2C_MST_RESET_BIT: 1,
USERCTRL_SIG_COND_RESET_BIT: 0,
DEFAULT_GYRO_OFFSET: { x: 0, y: 0, z: 0 },
DEFAULT_ACCEL_CALIBRATION: {
offset: { x: 0, y: 0, z: 0 },
scale: {
x: [-1, 1],
y: [-1, 1],
z: [-1, 1]
}
},
/** For Gyro */
DLPF_CFG_250HZ: 0x00,
DLPF_CFG_184HZ: 0x01,
DLPF_CFG_92HZ: 0x02,
DLPF_CFG_41HZ: 0x03,
DLPF_CFG_20HZ: 0x04,
DLPF_CFG_10HZ: 0x05,
DLPF_CFG_5HZ: 0x06,
DLPF_CFG_3600HZ: 0x07,
/** Sample rate min/max value */
SAMPLERATE_MIN: 5,
SAMPLERATE_MAX: 32000,
/** For accel. */
A_DLPF_CFG_460HZ: 0x00,
A_DLPF_CFG_184HZ: 0x01,
A_DLPF_CFG_92HZ: 0x02,
A_DLPF_CFG_41HZ: 0x03,
A_DLPF_CFG_20HZ: 0x04,
A_DLPF_CFG_10HZ: 0x05,
A_DLPF_CFG_5HZ: 0x06,
A_DLPF_CFG_460HZ_2: 0x07,
A_DLPF_CFG_MASK: 0x07
};
export class mpu9250 extends BaseDevice {
private _config: MPU9250Config;
// private i2c: any; // Specify the correct type for i2c
private gyroScalarInv!: number;
private accelScalarInv!: number;
private ak8963?: ak8963;
// other properties as needed
constructor(config: Partial<MPU9250Config> = {}) {
super();
this._config = {
// device: '/dev/i2c-0',
bus: 0,
address: MPU9250.I2C_ADDRESS_AD0_LOW,
UpMagneto: false,
DEBUG: false,
scaleValues: false,
ak_address: AK8963.ADDRESS,
GYRO_FS: 0,
ACCEL_FS: 2,
gyroBiasOffset: MPU9250.DEFAULT_GYRO_OFFSET,
accelCalibration: MPU9250.DEFAULT_ACCEL_CALIBRATION,
...config,
}
this.address = this._config.address;
}
public async initialize() {
this.bus = i2c.openSync(this._config.bus).promisifiedBus(); //new MOD_I2C(this._config.address, { device: this._config.device });
// this.debug = new debugConsole(this._config.DEBUG);
this.debug.log('INFO', 'Initialization MPU9250 ....');
// Clear configuration
await this.writeBit(MPU9250.RA_PWR_MGMT_1, MPU9250.PWR1_DEVICE_RESET_BIT, 1);
this.debug.log('INFO', 'Reset configuration MPU9250.');
await sleep(100);
// defined sample rate
if (
'SAMPLE_RATE' in this._config
&& this._config.SAMPLE_RATE
&& (
this._config.SAMPLE_RATE > MPU9250.SAMPLERATE_MIN
&& this._config.SAMPLE_RATE < MPU9250.SAMPLERATE_MAX
)
) {
await this.setSampleRate(this._config.SAMPLE_RATE);
await sleep(100);
}
// Define DLPF_CFG
if ('DLPF_CFG' in this._config && this._config.DLPF_CFG) {
await this.setDLPFConfig(this._config.DLPF_CFG);
await sleep(100);
}
// define A_DLPF_CFG
if ('A_DLPF_CFG' in this._config && this._config.A_DLPF_CFG) {
await this.setAccelDLPFConfig(this._config.A_DLPF_CFG);
await sleep(100);
}
// define clock source
this.setClockSource(MPU9250.CLOCK_PLL_XGYRO);
await sleep(100);
// define gyro range
var gyro_fs = [MPU9250.GYRO_FS_250, MPU9250.GYRO_FS_500, MPU9250.GYRO_FS_1000, MPU9250.GYRO_FS_2000];
var gyro_value = MPU9250.GYRO_FS_250;
if (this._config.GYRO_FS > -1 && this._config.GYRO_FS < 4) gyro_value = gyro_fs[this._config.GYRO_FS];
await this.setFullScaleGyroRange(gyro_value);
await sleep(100);
// define accel range
var accel_fs = [MPU9250.ACCEL_FS_2, MPU9250.ACCEL_FS_4, MPU9250.ACCEL_FS_8, MPU9250.ACCEL_FS_16];
var accel_value = MPU9250.ACCEL_FS_4;
if (this._config.ACCEL_FS > -1 && this._config.ACCEL_FS < 4) accel_value = accel_fs[this._config.ACCEL_FS];
await this.setFullScaleAccelRange(accel_value);
await sleep(100);
// disable sleepEnabled
await this.setSleepEnabled(false);
await sleep(100);
if (this._config.UpMagneto) {
this.debug.log('INFO', 'Enabled magnetometer. Starting initialization ....');
await this.enableMagnetometer();
this.debug.log('INFO', 'END of magnetometer initialization.');
}
this.debug.log('INFO', 'END of MPU9150 initialization.');
// Print out the configuration
if (this._config.DEBUG) {
await this.printSettings();
await this.printAccelSettings();
await this.printGyroSettings();
if (this.ak8963) {
await this.ak8963.printSettings();
}
}
return await this.testDevice();
}
async getMotion9() {
const mpudata = await this.getMotion6();
let magdata;
if (this.ak8963) {
magdata = await this.ak8963.getMagAttitude();
} else {
magdata = [0, 0, 0];
}
return mpudata.concat(magdata);
}
async getMotion6() {
var buffer = await this.readBytes(MPU9250.ACCEL_XOUT_H, 14);
var gCal = this._config.gyroBiasOffset;
var aCal = this._config.accelCalibration;
var xAccel = buffer.readInt16BE(0) * this.accelScalarInv;
var yAccel = buffer.readInt16BE(2) * this.accelScalarInv;
var zAccel = buffer.readInt16BE(4) * this.accelScalarInv;
return [
// Accelerometer
this.scaleAccel(xAccel, aCal.offset.x, aCal.scale.x),
this.scaleAccel(yAccel, aCal.offset.y, aCal.scale.y),
this.scaleAccel(zAccel, aCal.offset.z, aCal.scale.z),
// Skip Temperature - bytes 6:7
// Gyroscope
buffer.readInt16BE(8) * this.gyroScalarInv + gCal.x,
buffer.readInt16BE(10) * this.gyroScalarInv + gCal.y,
buffer.readInt16BE(12) * this.gyroScalarInv + gCal.z
];
}
scaleAccel(val: number, offset: number, scalerArr: number[]) {
if (val < 0) {
return -(val - offset) / (scalerArr[0] - offset);
} else {
return (val - offset) / (scalerArr[1] - offset);
}
}
async testDevice() {
var currentDeviceID = await this.getIDDevice();
return (currentDeviceID === MPU9250.ID_MPU_9250 || currentDeviceID === MPU9250.ID_MPU_9255);
}
async printGyroSettings() {
var FS_RANGE = ['+250dps (0)', '+500 dps (1)', '+1000 dps (2)', '+2000 dps (3)'];
this.debug.log('INFO', 'Gyroscope:');
this.debug.log('INFO', '--> Full Scale Range (0x1B): ' + FS_RANGE[await this.getFullScaleGyroRange()]);
this.debug.log('INFO', '--> Scalar: 1/' + (1 / this.gyroScalarInv));
this.debug.log('INFO', '--> Bias Offset:');
this.debug.log('INFO', ' --> x: ' + this._config.gyroBiasOffset.x);
this.debug.log('INFO', ' --> y: ' + this._config.gyroBiasOffset.y);
this.debug.log('INFO', ' --> z: ' + this._config.gyroBiasOffset.z);
}
async getFullScaleGyroRange() {
var byte = await this.readByte(MPU9250.RA_GYRO_CONFIG);
byte = byte & 0x18;
byte = byte >> 3;
return byte;
}
async printAccelSettings() {
var FS_RANGE = ['±2g (0)', '±4g (1)', '±8g (2)', '±16g (3)'];
this.debug.log('INFO', 'Accelerometer:');
this.debug.log('INFO', '--> Full Scale Range (0x1C): ' + FS_RANGE[await this.getFullScaleAccelRange()]);
this.debug.log('INFO', '--> Scalar: 1/' + (1 / this.accelScalarInv));
this.debug.log('INFO', '--> Calibration:');
this.debug.log('INFO', ' --> Offset: ');
this.debug.log('INFO', ' --> x: ' + this._config.accelCalibration.offset.x);
this.debug.log('INFO', ' --> y: ' + this._config.accelCalibration.offset.y);
this.debug.log('INFO', ' --> z: ' + this._config.accelCalibration.offset.z);
this.debug.log('INFO', ' --> Scale: ');
this.debug.log('INFO', ' --> x: ' + this._config.accelCalibration.scale.x);
this.debug.log('INFO', ' --> y: ' + this._config.accelCalibration.scale.y);
this.debug.log('INFO', ' --> z: ' + this._config.accelCalibration.scale.z);
}
async getFullScaleAccelRange() {
var byte = await this.readByte(MPU9250.RA_ACCEL_CONFIG_1);
byte = byte & 0x18;
byte = byte >> 3;
return byte;
}
async printSettings() {
var CLK_RNG = [
'0 (Internal 20MHz oscillator)',
'1 (Auto selects the best available clock source)',
'2 (Auto selects the best available clock source)',
'3 (Auto selects the best available clock source)',
'4 (Auto selects the best available clock source)',
'5 (Auto selects the best available clock source)',
'6 (Internal 20MHz oscillator)',
'7 (Stops the clock and keeps timing generator in reset)'
];
this.debug.log('INFO', 'MPU9250:');
this.debug.log('INFO', '--> Device address: 0x' + this._config.address.toString(16));
this.debug.log('INFO', '--> i2c bus: 0x' + (await this.getIDDevice()).toString(16));
this.debug.log('INFO', '--> Device ID: 0x' + (await this.getIDDevice()).toString(16));
this.debug.log('INFO', '--> BYPASS enabled: ' + ((await this.getByPASSEnabled()) ? 'Yes' : 'No'));
this.debug.log('INFO', '--> SleepEnabled Mode: ' + ((await this.getSleepEnabled()) === 1 ? 'On' : 'Off'));
this.debug.log('INFO', '--> i2c Master Mode: ' + ((await this.getI2CMasterMode()) === 1 ? 'Enabled' : 'Disabled'));
this.debug.log('INFO', '--> Power Management (0x6B, 0x6C):');
this.debug.log('INFO', ' --> Clock Source: ' + CLK_RNG[await this.getClockSource()]);
this.debug.log('INFO', ' --> Accel enabled (x, y, z): ' + this.vectorToYesNo(await this.getAccelPowerSettings()));
this.debug.log('INFO', ' --> Gyro enabled (x, y, z): ' + this.vectorToYesNo(await this.getGyroPowerSettings()));
}
async getGyroPowerSettings() {
var byte = await this.readByte(MPU9250.RA_PWR_MGMT_2);
byte = byte & 0x07;
return [
(byte >> 2) & 1, // X
(byte >> 1) & 1, // Y
(byte >> 0) & 1 // Z
];
}
getPitch(value: number[]){
return ((Math.atan2(value[0], value[2]) + Math.PI) * (180 / Math.PI)) - 180;
}
getRoll(value: number[]){
return ((Math.atan2(value[1], value[2]) + Math.PI) * (180 / Math.PI)) - 180;
}
getYaw(value: number[]){
return 0;
}
async getGyro(){
var buffer = await this.readBytes(MPU9250.GYRO_XOUT_H, 6);
var gCal = this._config.gyroBiasOffset;
return [
buffer.readInt16BE(0) * this.gyroScalarInv + gCal.x,
buffer.readInt16BE(2) * this.gyroScalarInv + gCal.y,
buffer.readInt16BE(4) * this.gyroScalarInv + gCal.z
];
}
async getAccel() {
var buffer = await this.readBytes(MPU9250.ACCEL_XOUT_H, 6);
var aCal = this._config.accelCalibration;
var xAccel = buffer.readInt16BE(0) * this.accelScalarInv;
var yAccel = buffer.readInt16BE(2) * this.accelScalarInv;
var zAccel = buffer.readInt16BE(4) * this.accelScalarInv;
return [
this.scaleAccel(xAccel, aCal.offset.x, aCal.scale.x),
this.scaleAccel(yAccel, aCal.offset.y, aCal.scale.y),
this.scaleAccel(zAccel, aCal.offset.z, aCal.scale.z)
];
}
async getAccelPowerSettings() {
var byte = await this.readByte(MPU9250.RA_PWR_MGMT_2);
byte = byte & 0x38;
return [
(byte >> 5) & 1, // X
(byte >> 4) & 1, // Y
(byte >> 3) & 1 // Z
];
}
async getClockSource() {
return await this.readByte(MPU9250.RA_PWR_MGMT_1) & 0x07;
}
async getI2CMasterMode() {
return await this.readBit(MPU9250.RA_USER_CTRL, MPU9250.USERCTRL_I2C_MST_EN_BIT);
}
async getSleepEnabled() {
return await this.readBit(MPU9250.RA_PWR_MGMT_1, MPU9250.PWR1_SLEEP_BIT)
}
async getIDDevice() {
return await this.readByte(MPU9250.WHO_AM_I);
}
async setSampleRate(sample_rate: number) {
if (sample_rate < MPU9250.SAMPLERATE_MAX && sample_rate >= 8000) {
sample_rate = 8000;
}
if (sample_rate < 8000 && sample_rate > 1000) {
sample_rate = 1000;
}
if (sample_rate < 1000) {
sample_rate = 1000 / (1 + sample_rate);
}
await this.writeBits(MPU9250.SMPLRT_DIV, 0, 8, sample_rate).catch((r) => {
this.debug.log('ERROR', 'setSampleRate ' + r.message);
});
}
async enableMagnetometer() {
await this.setI2CMasterModeEnabled(false);
await sleep(100);
await this.setByPASSEnabled(true);
await sleep(100);
if (await this.getByPASSEnabled()) {
// this.ak8963 = new ak8963(this._config);
// await this.ak8963.initialize();
} else {
this.debug.log('ERROR', 'Can\'t turn on RA_INT_PIN_CFG.');
}
}
async getByPASSEnabled() {
return await this.readBit(MPU9250.RA_INT_PIN_CFG, MPU9250.INTCFG_BYPASS_EN_BIT);
}
async setByPASSEnabled(bool: boolean) {
var val = bool ? 1 : 0;
// await this.writeByte(MPU9250.RA_INT_PIN_CFG, 0x02)
await this.writeBit(MPU9250.RA_INT_PIN_CFG, MPU9250.INTCFG_BYPASS_EN_BIT, val);
}
async setI2CMasterModeEnabled(bool: boolean) {
var val = bool ? 1 : 0;
await this.writeBit(MPU9250.RA_USER_CTRL, MPU9250.USERCTRL_I2C_MST_EN_BIT, val);
}
async setSleepEnabled(bool: boolean) {
var val = bool ? 1 : 0;
await this.writeBit(MPU9250.RA_PWR_MGMT_1, MPU9250.PWR1_SLEEP_BIT, val);
}
async setFullScaleAccelRange(adrs: number) {
if (this._config.scaleValues) {
this.accelScalarInv = 1 / MPU9250.ACCEL_SCALE_FACTOR[adrs];
} else {
this.accelScalarInv = 1;
}
await this.writeBits(MPU9250.RA_ACCEL_CONFIG_1, MPU9250.ACONFIG_FS_SEL_BIT, MPU9250.ACONFIG_FS_SEL_LENGTH, adrs);
}
async setFullScaleGyroRange(adrs: number) {
if (this._config.scaleValues) {
this.gyroScalarInv = 1 / MPU9250.GYRO_SCALE_FACTOR[adrs];
} else {
this.gyroScalarInv = 1;
}
await this.writeBits(MPU9250.RA_GYRO_CONFIG, MPU9250.GCONFIG_FS_SEL_BIT, MPU9250.GCONFIG_FS_SEL_LENGTH, adrs);
}
async setClockSource(adrs: number) {
await this.writeBits(MPU9250.RA_PWR_MGMT_1, MPU9250.PWR1_CLKSEL_BIT, MPU9250.PWR1_CLKSEL_LENGTH, adrs);
}
async setDLPFConfig(dlpf_cfg: number) {
await this.writeBits(MPU9250.RA_CONFIG, 0, 3, dlpf_cfg).catch((r) => {
this.debug.log('ERROR', 'setDLPFConfig ' + r.message);
});
}
async setAccelDLPFConfig(a_dlpf_cfg: number) {
await this.writeBits(MPU9250.RA_ACCEL_CONFIG_2, 0, 4, a_dlpf_cfg).catch((r) => {
this.debug.log('ERROR', 'setAccelDLPFConfig ' + r.message);
});
}
}
// Other classes (ak8963, debugConsole, etc.) and their methods with type annotations
// Export the module
export default mpu9250;