MPU-9520 9-Axis Sensor on Kris Winers Library - Wrong Adress?

Hi Guys,

I have received my MPU-9520 9-Axis sensor today. It outputs data fine on the asukiaaa library, but this has almost no functionality which i can use, and delivers very very low precision on the magnetic orientation.
I have loaded the Kris Winer library as i have heard many good things and seen good functions on youtube tutorials.

If i connect and run it, i get following output:

MPU9250 I AM 0xFF I should be 0x71
Could not connect to MPU9250: 0xFF
Communication failed, abort!

it is configured to AD1. Changing to AD0 changes the WHO AM I to 0x73
Apparently the Winer library expects a sensor on 0x71.....can anyone tell me how i can change this?

(I installed the I2C scanner, it detects the MPU on 0x68)

Thanks for any hints

delivers very very low precision on the magnetic orientation.

Magnetometers don't work "out of the box" and must be calibrated to be useful. Comprehensive tutorial here: Tutorial: How to calibrate a compass (and accelerometer) with Arduino | Underwater Arduino Data Loggers

Simpler but less accurate approach here: Simple and Effective Magnetometer Calibration · kriswiner/MPU6050 Wiki · GitHub

This message suggests a general communications failure (wiring error):

MPU9250 I AM 0xFF I should be 0x71
Could not connect to MPU9250: 0xFF

Yes calibration and all done according to the library. Still getting large jumps, largely because the code contains many delay() functions.

Problem is not wiring, i just found the problem. The Program insists of 0x71 as adress code, if its not it enforces a stop. Removing the adress check made the program run ahead.

Ok all is working swell now.

But the yaw channel on krises library is showing severe movement if i am just pitching or rolling. Has anyone observed or fixed that?

Magnetometer calibration, again.

Nope. Apparently the magic word is "tilt compensated compass"

Miniflyer:
Nope. Apparently the magic word is "tilt compensated compass"

Could you explain how a tilt compensated compass causes a compass to be automatically calibrated? I to have a tile compensated compass on an X/Y platform which I cause to go through an alignment upon each boot.

Also, why do I have to the make a figure 8 with my phone to align my phone compass when it has a tilt compensated compass?

Again: calibration is not the issue...the issue was the code....the winer code does the calibration, figure 8s and all.

Can anyone tell me how i can slow down the MPU-9250? It is currently sampeling too fast and causing all other program parts to stutter.....

Sample the device slower.

Look in the register data sheet, You will find info about how the MPU9250 handles data which allows you to control the thing. One thing you can do is let the MPU9250 read and hold the data till the data is read out of the registers. Another way is to reduce the number of times per second you read the data.

I just studied this intensely:

https://www.invensense.com/download-pdf/mpu-9250-datasheet/

I found out that i can adjust ranges, but not how....
Also information on different supported sampling rates, but again no info on how to adress them.

Sorry, new to all this and proud to get small steps :slight_smile:

"tilt compensated compass"

Already built in to the body orientation calculation.

Simply set the device registers appropriately for the desired sampling rate and sensitivities.

For informed help, please read and follow the directions in the "How to use this forum" post.

Adjusting ranges do not change sampling rate.

May I suggest to you that you use the Boulder Flight Systems MPU9250 to read the MPU9250 data and plug those numbers into your other calculations?

Not sure if this will help but here is my code for communications with a MPU9250 by a ESP32 using the SPI buss, you should be able to see how I make the changes to the various register settings; though it would be a lot eaiser to use the Bouler Flight Systems library. Part 1:

#include <driver/spi_master.h>
#include "esp_system.h" //This inclusion configures the peripherals in the ESP system.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include "sdkconfig.h"
////////////////////////////////////////////////////
#define evtGetIMU                  ( 1 << 1 ) // 10
///////////////////////////////////////////////////
EventGroupHandle_t eg;
///////////////////////////////////////////////////
#define TaskCore1 1
#define TaskCore0 0
#define SerialDataBits 115200
#define spiCLK 14 // CLK module pin SCL
#define spiMOSI 13 // MOSI module pin SDA
#define spiMISO 12 // MISO module pin SDOAG tied to SDOM
#define csPinAG 15
#define TaskStack30K 30000
#define Priority4 4
#define MPU_int_Pin 34
////////////////////////////////////////
spi_device_handle_t hAG;
////////////////////////////////////////
const uint8_t SPI_READ = 0x80;
const uint8_t WHO_I_AMa = 0x73;
const uint8_t WHO_I_AMb = 0x71;
const uint8_t AK8963_ISa = 0x48;
const uint8_t AK8963_ISb = 0x5F;
const uint8_t ACCELX_OUT = 0x3B;
const uint8_t ACCELY_OUT = 0x3D;
const uint8_t ACCELZ_OUT = 0x3F;
const uint8_t GYRO_OUTX = 0x43;
const uint8_t GYRO_OUTY = 0x45;
const uint8_t GYRO_OUTZ = 0x47;
// constants
const uint8_t EXT_SENS_DATA_00 = 0x49;
const float G = 9.807f;
const float _d2r = 3.14159265359f / 180.0f;
const int16_t tX[3] = {0,  1,  0};
const int16_t tY[3] = {1,  0,  0};
const int16_t tZ[3] = {0,  0, -1};
const uint8_t ACCEL_CONFIG = 0x1C;
const uint8_t ACCEL_FS_SEL_2G = 0x00;
const uint8_t ACCEL_FS_SEL_4G = 0x08;
const uint8_t ACCEL_FS_SEL_8G = 0x10;
const uint8_t ACCEL_FS_SEL_16G = 0x18;
const uint8_t GYRO_CONFIG = 0x1B;
const uint8_t GYRO_FS_SEL_250DPS = 0x00;
const uint8_t GYRO_FS_SEL_500DPS = 0x08;
const uint8_t GYRO_FS_SEL_1000DPS = 0x10;
const uint8_t GYRO_FS_SEL_2000DPS = 0x18;
const uint8_t ACCEL_CONFIG2 = 0x1D;
const uint8_t ACCEL_DLPF_184 = 0x01;
const uint8_t ACCEL_DLPF_92 = 0x02;
const uint8_t ACCEL_DLPF_41 = 0x03;
const uint8_t ACCEL_DLPF_20 = 0x04;
const uint8_t ACCEL_DLPF_10 = 0x05;
const uint8_t ACCEL_DLPF_5 = 0x06;
const uint8_t CONFIG = 0x1A;
const uint8_t GYRO_DLPF_184 = 0x01;
const uint8_t GYRO_DLPF_92 = 0x02;
const uint8_t GYRO_DLPF_41 = 0x03;
const uint8_t GYRO_DLPF_20 = 0x04;
const uint8_t GYRO_DLPF_10 = 0x05;
const uint8_t GYRO_DLPF_5 = 0x06;
const uint8_t SMPDIV = 0x19;
const uint8_t INT_PIN_CFG = 0x37;
const uint8_t INT_ENABLE = 0x38;
const uint8_t INT_DISABLE = 0x00;
const uint8_t INT_PULSE_50US = 0x00;
const uint8_t INT_WOM_EN = 0x40;
const uint8_t INT_RAW_RDY_EN = 0x01;
const uint8_t PWR_MGMNT_1 = 0x6B;
const uint8_t PWR_CYCLE = 0x20;
const uint8_t PWR_RESET = 0x80;
const uint8_t CLOCK_SEL_PLL = 0x01;
const uint8_t PWR_MGMNT_2 = 0x6C;
const uint8_t SEN_ENABLE = 0x00;
const uint8_t DIS_GYRO = 0x07;
const uint8_t USER_CTRL = 0x6A;
const uint8_t I2C_MST_EN = 0x20;
const uint8_t I2C_MST_CLK = 0x0D;
const uint8_t I2C_MST_CTRL = 0x24;
const uint8_t I2C_SLV0_ADDR = 0x25;
const uint8_t I2C_SLV0_REG = 0x26;
const uint8_t I2C_SLV0_DO = 0x63;
const uint8_t I2C_SLV0_CTRL = 0x27;
const uint8_t I2C_SLV0_EN = 0x80;
const uint8_t I2C_READ_FLAG = 0x80;
const uint8_t MOT_DETECT_CTRL = 0x69;
const uint8_t ACCEL_INTEL_EN = 0x80;
const uint8_t ACCEL_INTEL_MODE = 0x40;
const uint8_t LP_ACCEL_ODR = 0x1E;
const uint8_t WOM_THR = 0x1F;
const uint8_t WHO_AM_I = 0x75;
const uint8_t FIFO_EN = 0x23;
const uint8_t FIFO_TEMP = 0x80;
const uint8_t FIFO_GYRO = 0x70;
const uint8_t FIFO_ACCEL = 0x08;
const uint8_t FIFO_MAG = 0x01;
const uint8_t FIFO_COUNT = 0x72;
const uint8_t FIFO_READ = 0x74;
// AK8963 registers
const uint8_t AK8963_I2C_ADDR = 0x0C;
const uint8_t AK8963_HXL = 0x03;
const uint8_t AK8963_CNTL1 = 0x0A;
const uint8_t AK8963_PWR_DOWN = 0x00;
const uint8_t AK8963_CNT_MEAS1 = 0x12;
const uint8_t AK8963_CNT_MEAS2 = 0x16;
const uint8_t AK8963_FUSE_ROM = 0x0F;
const uint8_t AK8963_CNTL2 = 0x0B;
const uint8_t AK8963_RESET = 0x01;
const uint8_t AK8963_ASA = 0x10;
const uint8_t AK8963_WHO_AM_I = 0x00;
////////////////////////////////////////////////
float q[4] = {1.0f, 0.0f, 0.0f, 0.0f};           // vector to hold quaternion
float eInt[3] = {0.0f, 0.0f, 0.0f};              // vector to hold integral error for Mahony method
float deltat = 0.0f;                             // integration interval for both filter schemes
#define Kp 7.50f // found these values, for the mpu9250, from some deep digging 
#define Ki 1.7f
uint8_t txData[2] = { };
uint8_t rxData[21] = { };
////////////////////////////////////////////////
void triggerGet_IMU()
{
  BaseType_t xHigherPriorityTaskWoken;
  xEventGroupSetBitsFromISR(eg, evtGetIMU, &xHigherPriorityTaskWoken);
}
////
void setup()
{
  eg = xEventGroupCreate();
  xTaskCreatePinnedToCore ( fGetIMU, "v_getIMU", TaskStack30K, NULL, Priority4, NULL, TaskCore0 );
}
////
void loop() {}

part2a:

void fGetIMU( void *pvParameters )
{
  bool MPU9250_OK = false;
  bool AK8963_OK = false;
  esp_err_t intError;
  // data counts
  int16_t  _axcounts, _aycounts, _azcounts;
  int16_t _gxcounts, _gycounts, _gzcounts;
  int16_t _hxcounts, _hycounts, _hzcounts;
  int16_t _tcounts;
  // data buffer
  float _ax, _ay, _az;
  float _gx, _gy, _gz;
  float _hx, _hy, _hz;
  float _t;
  float _accelScale;
  float _gyroScale;
  float _magScaleX, _magScaleY, _magScaleZ;
  float _axb, _ayb, _azb;
  float _gxb, _gyb, _gzb;
  float _axs = 1.0f;
  float _ays = 1.0f;
  float _azs = 1.0f;
  float _hxb, _hyb, _hzb;
  float _hxs = 1.0f;
  float _hys = 1.0f;
  float _hzs = 1.0f;
  ////
  spi_bus_config_t bus_config = { };
  bus_config.sclk_io_num = spiCLK; // CLK
  bus_config.mosi_io_num = spiMOSI; // MOSI
  bus_config.miso_io_num = spiMISO; // MISO
  bus_config.quadwp_io_num = -1; // Not used
  bus_config.quadhd_io_num = -1; // Not used
  intError = spi_bus_initialize(HSPI_HOST, &bus_config, 1) ;
  log_i (" Initializing device bus error = %d", intError );
  //
  spi_device_interface_config_t dev_config = { };  // initializes all field to 0
  dev_config.address_bits     = 0;
  dev_config.command_bits     = 0;
  dev_config.dummy_bits       = 0;
  dev_config.mode             = SPI_MODE3 ;
  dev_config.duty_cycle_pos   = 0;
  dev_config.cs_ena_posttrans = 0;
  dev_config.cs_ena_pretrans  = 0;
  dev_config.clock_speed_hz   = 1000000; // mpu 9250 spi read registers safe up to 1Mhz.
  dev_config.spics_io_num     = csPinAG;
  dev_config.flags            = 0;
  dev_config.queue_size       = 1;
  dev_config.pre_cb           = NULL;
  dev_config.post_cb          = NULL;
  intError = spi_bus_add_device(HSPI_HOST, &dev_config, &hAG);
  log_i (" Adding device bus error = %d", intError );
  ////
  ////
  spi_transaction_t trans_desc = { };
  fWrite_AK8963( AK8963_CNTL1, AK8963_PWR_DOWN );
  vTaskDelay ( 100 );
  // fWriteSPIdata8bits( PWR_MGMNT_1, PWR_RESET );
  // wait for MPU-9250 to come back up
  // vTaskDelay(100);
  // select clock source to gyro
  // PWR_MGMNT_1,CLOCK_SEL_PLL
  fWriteSPIdata8bits( PWR_MGMNT_1, CLOCK_SEL_PLL );
  vTaskDelay(1);
  // Do who am I MPU9250
  fReadMPU9250 ( 2 , WHO_AM_I );
  if ( (WHO_I_AMa == rxData[1]) || (WHO_I_AMb == rxData[1]) )
  {
    MPU9250_OK = true;
  }
  else
  {
    log_i ( " I am not MPU9250! I am: %d", rxData[1] );
  }
  if ( MPU9250_OK )
  {
    // enable I2C master mode
    // USER_CTRL,I2C_MST_EN
    fWriteSPIdata8bits( USER_CTRL, I2C_MST_EN );
    vTaskDelay(1);
    // set the I2C bus speed to 400 kHz
    // I2C_MST_CTRL,I2C_MST_CLK
    fWriteSPIdata8bits( I2C_MST_CTRL, I2C_MST_CLK );
    vTaskDelay(1);
    fWriteSPIdata8bits( SMPDIV, 0x04 );
    vTaskDelay(1);
    // DLPF ACCEL_CONFIG2,ACCEL_DLPF_184
    fWriteSPIdata8bits( ACCEL_CONFIG2, ACCEL_DLPF_184 );
    // CONFIG,GYRO_DLPF_184
    fWriteSPIdata8bits( CONFIG, GYRO_DLPF_184 );
    vTaskDelay(1);
    // check AK8963_WHO_AM_I
    fReadAK8963( AK8963_WHO_AM_I, 1 );
    vTaskDelay(500); // giving the AK8963 lots of time to recover from reset
    fReadMPU9250( 2 , EXT_SENS_DATA_00 );
    //if ( AK8963_ISa == rxData[1] || AK8963_ISb == rxData[1] )
    if ( rxData[1] != 0 )
    {
      AK8963_OK = true;
    }
    else
    {
      log_i ( " AK8963_OK data return = %d%d", rxData[0], rxData[1] );
    }
    if ( AK8963_OK )
    {
      // set AK8963 to FUSE ROM access
      // AK8963_CNTL1,AK8963_FUSE_ROM
      fWrite_AK8963 ( AK8963_CNTL1, AK8963_FUSE_ROM );
      vTaskDelay ( 100 ); // delay for mode change
      //  // setting the accel range to 16G
      fWriteSPIdata8bits( ACCEL_CONFIG, ACCEL_FS_SEL_16G );
      _accelScale = 16.0f / 32768.0f; // setting the accel scale to 16G
      //   ACCEL_CONFIG,ACCEL_FS_SEL_8G
      // fWriteSPIdata8bits( ACCEL_CONFIG, ACCEL_FS_SEL_8G );
      // _accelScale = 8.0f/32768.0f; // setting the accel scale to 8G
      //    ACCEL_CONFIG,ACCEL_FS_SEL_4G
      // fWriteSPIdata8bits( ACCEL_CONFIG, ACCEL_FS_SEL_4G );
      //  _accelScale = 4.0f/32768.0f; // setting the accel scale to 4G
      // ACCEL_CONFIG,ACCEL_FS_SEL_2G
      // fWriteSPIdata8bits( ACCEL_CONFIG, ACCEL_FS_SEL_2G );
      // _accelScale = 2.0f/32768.0f;
      // // _accelScale = G * 2.0f / 32767.5f; // setting the accel scale to 2G
      // set gyro scale
      // GYRO_CONFIG,GYRO_FS_SEL_1000DPS
      fWriteSPIdata8bits( GYRO_CONFIG, GYRO_FS_SEL_1000DPS );
      // 250.0f/32768.0f;
      // 500.0f/32768.0f;
      // 2000.0f/32768.0f;
      _gyroScale = 1000.0f / 32768.0f;
      // read the AK8963 ASA registers and compute magnetometer scale factors
      //   set accel scale
      fReadAK8963(AK8963_ASA, 3 );

part2b

   fReadMPU9250 ( 3 , EXT_SENS_DATA_00 );
      // convert to mG multiply by 10
      _magScaleX = ((((float)rxData[0]) - 128.0f) / (256.0f) + 1.0f) * 4912.0f / 32760.0f; // micro Tesla
      _magScaleY = ((((float)rxData[1]) - 128.0f) / (256.0f) + 1.0f) * 4912.0f / 32760.0f; // micro Tesla
      _magScaleZ = ((((float)rxData[2]) - 128.0f) / (256.0f) + 1.0f) * 4912.0f / 32760.0f; // micro Tesla
      // set AK8963 to 16 bit resolution, 100 Hz update rate
      // AK8963_CNTL1,AK8963_CNT_MEAS2
      fWrite_AK8963( AK8963_CNTL1, AK8963_CNT_MEAS2 );
      // delay for mode change
      vTaskDelay ( 100 );
      // AK8963_HXL,7 ;
      fReadAK8963(AK8963_HXL, 7 );
      ////
      /* setting the interrupt */
      // INT_PIN_CFG,INT_PULSE_50US setup interrupt, 50 us pulse
      fWriteSPIdata8bits( INT_PIN_CFG, INT_PULSE_50US );
      // INT_ENABLE,INT_RAW_RDY_EN set to data ready
      fWriteSPIdata8bits( INT_ENABLE, INT_RAW_RDY_EN );
      pinMode( MPU_int_Pin, INPUT );
      attachInterrupt( MPU_int_Pin, triggerGet_IMU, RISING );
    }
  }
  ////////////////////////////////////////////////////////////////
  TickType_t TimePast = xTaskGetTickCount();
  TickType_t TimeNow = xTaskGetTickCount();

  TickType_t xLastWakeTime;
  const TickType_t xFrequency = pdMS_TO_TICKS( 100 );
  // Initialise the xLastWakeTime variable with the current time.
  xLastWakeTime = xTaskGetTickCount();
  ////
  while (1)
  {
    // xEventGroupWaitBits (eg, evtGetIMU, pdTRUE, pdTRUE, portMAX_DELAY); // use interrupt un comment
    vTaskDelayUntil( &xLastWakeTime, xFrequency ); // use timer uncomment
    // ACCEL_OUT
    // ACCELX_OUT = 0x3B;
    if ( MPU9250_OK && AK8963_OK )
    {
      TimeNow = xTaskGetTickCount();
      deltat = ( (float)TimeNow - (float)TimePast) / 1000.0f;
      ////
      fReadMPU9250 ( 8 , 0X3A );
      _axcounts = (int16_t)(((int16_t)rxData[2] << 8) | rxData[3]) ;
      _aycounts = (int16_t)(((int16_t)rxData[4] << 8) | rxData[5]) ;
      _azcounts = (int16_t)(((int16_t)rxData[6] << 8) | rxData[7]) ;
      fReadMPU9250 ( 6 , GYRO_OUTX );
      _gxcounts = (int16_t)(((int16_t)rxData[0]) << 8) | rxData[1];
      _gycounts = (int16_t)(((int16_t)rxData[2]) << 8) | rxData[3];
      _gzcounts = (int16_t)(((int16_t)rxData[4]) << 8) | rxData[5];
      // EXT_SENS_DATA_00
      fReadMPU9250 ( 6 , EXT_SENS_DATA_00 );
      _hxcounts = ((int16_t)rxData[1] << 8) | rxData[0] ;
      _hycounts = ((int16_t)rxData[3] << 8) | rxData[2] ;;
      _hzcounts = ((int16_t)rxData[5] << 8) | rxData[4] ;;
      //
      /// no biases applied just scale factor
      _ax = (float)_axcounts * _accelScale;
      _ay = (float)_aycounts * _accelScale;
      _az = (float)_azcounts * _accelScale;
      //
      _gx = (float)_gxcounts * _gyroScale;
      _gy = (float)_gycounts * _gyroScale;
      _gz = (float)_gzcounts * _gyroScale;
      //
      _hx = (float)_hxcounts * _magScaleX;
      _hy = (float)_hycounts * _magScaleY;
      _hz = (float)_hzcounts * _magScaleZ;
      ////
      log_i ( "ax = %f", ( _ax * 1000.0f) );
      log_i ( " ay = %f", ( _ay * 1000.0f) );
      log_i ( " az = %f", ( _az * 1000.0f ) );
      log_i ( " gx = %f", ( _gx * 1000.0f ) );
      log_i ( " gy = %f", ( _gy * 1000.0f ) );
      log_i ( " gz = %f", ( _gz * 1000.0f ) );
      log_i ( " hx = %f", _hx );
      log_i ( " hy = %f", _hy );
      log_i ( " hz = %f", _hz );
      // h value multiplied by 10 converts to mG from uT
      MahonyQuaternionUpdate ( _ax, _ay, _az, _gx, _gy, _gz, _hx * 10.0f, _hy * 10.0f, _hz * 10.0f );
      // Define output variables from updated quaternion---these are Tait-Bryan angles, commonly used in aircraft orientation.
      // In this coordinate system, the positive z-axis is down toward Earth.
      // Yaw is the angle between Sensor x-axis and Earth magnetic North (or true North if corrected for local declination, looking down on the sensor positive yaw is counterclockwise.
      // Pitch is angle between sensor x-axis and Earth ground plane, toward the Earth is positive, up toward the sky is negative.
      // Roll is angle between sensor y-axis and Earth ground plane, y-axis up is positive roll.
      // These arise from the definition of the homogeneous rotation matrix constructed from quaternions.
      // Tait-Bryan angles as well as Euler angles are non-commutative; that is, the get the correct orientation the rotations must be
      // applied in the correct order which for this configuration is yaw, pitch, and then roll.
      // For more see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles which has additional links.
      float yaw   = atan2f(2.0f * (q[1] * q[2] + q[0] * q[3]), q[0] * q[0] + q[1] * q[1] - q[2] * q[2] - q[3] * q[3]);
      float pitch = -asinf(2.0f * (q[1] * q[3] - q[0] * q[2]));
      float roll  = atan2f(2.0f * (q[0] * q[1] + q[2] * q[3]), q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3]);
      pitch *= 180.0f / PI;
      yaw   *= 180.0f / PI;
      yaw   += 13.13f; // Declination
      roll  *= 180.0f / PI;
      TimePast = TimeNow;
      //      log_i ( " Roll: %f", roll );
      //      log_i ( " Pitch: %f", pitch );
      //      log_i ( " Yaw: %f", yaw );
    }
    else
    {
      log_i ( "WHO AM I FAILED!! ");
      log_i ( "MPU9250 OK = %d", MPU9250_OK );
      log_i ( " AK8963 OK = %d", AK8963_OK );
    }
    xLastWakeTime = xTaskGetTickCount();
  }
  vTaskDelete(NULL);
} // void fGetIMU( void *pvParameters )

part3

///////////////////////////////////////////
void fReadMPU9250 ( uint8_t byteReadSize, uint8_t addressToRead )
{
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = (8 * 2) + (8 * byteReadSize) ; // total data bits
  trans_desc.tx_buffer = txData;
  trans_desc.rxlength = byteReadSize * 8 ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = rxData;
  txData[0] = addressToRead | SPI_READ;
  txData[1] = 0x0;
  intError = spi_device_transmit( hAG, &trans_desc);
  if ( intError != 0 )
  {
    log_i ( " WHO I am MPU9250. Transmitting error = %d", intError );
  }
} // void fReadMPU9250 ( uint8_t byteReadSize, uint8_t addressToRead )
void fWriteSPIdata8bits( uint8_t address, uint8_t DataToSend)
{
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr = 0;
  trans_desc.cmd = 0;
  trans_desc.flags  = 0;
  trans_desc.length = 8 * 2; // total data bits
  trans_desc.tx_buffer = txData;
  txData[0] = address;
  txData[1] = DataToSend;
  intError = spi_device_transmit( hAG, &trans_desc);
  if ( intError != 0 )
  {
    log_i( " Transmitting error = %d", intError );
  }
} // void fSendSPI( uint8_t count, uint8_t address, uint8_t DataToSend)
///////////////////////////////////////////////////////////////////////
void fReadAK8963( uint8_t subAddress, uint8_t count )
{
  // set slave 0 to the AK8963 and set for read
  fWriteSPIdata8bits ( I2C_SLV0_ADDR, AK8963_I2C_ADDR | I2C_READ_FLAG );
  // set the register to the desired AK8963 sub address
  // I2C_SLV0_REG, subAddress
  fWriteSPIdata8bits ( I2C_SLV0_REG, subAddress );
  // enable I2C and request the bytes
  // I2C_SLV0_CTRL, I2C_SLV0_EN | count
  fWriteSPIdata8bits ( I2C_SLV0_CTRL, I2C_SLV0_EN | count );
  vTaskDelay ( 1 );
  // read result in rxData EXT_SENS_DATA_00,count,dest
  // EXT_SENS_DATA_00,count,dest

} // fReadAK8963(uint8_t subAddress, uint8_t count, uint8_t* dest)
////////////////////////////////////////////////////////////////////////////////////////////
void fWrite_AK8963 ( uint8_t subAddress, uint8_t dataAK8963 )
{
  fWriteSPIdata8bits ( I2C_SLV0_ADDR, AK8963_I2C_ADDR );
  fWriteSPIdata8bits ( I2C_SLV0_REG, subAddress );
  fWriteSPIdata8bits ( I2C_SLV0_DO, dataAK8963 );
  fWriteSPIdata8bits ( I2C_SLV0_CTRL, I2C_SLV0_EN | (uint8_t)1) ;
  //
  vTaskDelay ( 1 );
} // void fWrite_AK8963 ( uint8_t subAddress, uint8_t dataAK8963 )
/////////////////////////////////////////////////////////////////////////////////////////////
void fwriteMUP9250register ( uint8_t addr, uint8_t sendData )
{
  esp_err_t intError;
  spi_transaction_t trans_desc = {};
  trans_desc.addr =  addr;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = 8 * 1 ; // total data bits
  trans_desc.tx_buffer = txData;
  txData[0] = sendData;
  intError = spi_device_transmit( hAG, &trans_desc);
  if ( intError != 0 )
  {
    log_i( " Transmitting error = %d", intError );
  }
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void MahonyQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz)
{
  float q1 = q[0], q2 = q[1], q3 = q[2], q4 = q[3];   // short name local variable for readability
  float norm;
  float hx, hy, bx, bz;
  float vx, vy, vz, wx, wy, wz;
  float ex, ey, ez;
  float pa, pb, pc;
  // Auxiliary variables to avoid repeated arithmetic
  float q1q1 = q1 * q1;
  float q1q2 = q1 * q2;
  float q1q3 = q1 * q3;
  float q1q4 = q1 * q4;
  float q2q2 = q2 * q2;
  float q2q3 = q2 * q3;
  float q2q4 = q2 * q4;
  float q3q3 = q3 * q3;
  float q3q4 = q3 * q4;
  float q4q4 = q4 * q4;
  // Normalise accelerometer measurement
  norm = sqrt(ax * ax + ay * ay + az * az);
  if (norm == 0.0f) return; // handle NaN
  norm = 1.0f / norm;        // use reciprocal for division
  ax *= norm;
  ay *= norm;
  az *= norm;
  // Normalise magnetometer measurement
  norm = sqrt(mx * mx + my * my + mz * mz);
  if (norm == 0.0f) return; // handle NaN
  norm = 1.0f / norm;        // use reciprocal for division
  mx *= norm;
  my *= norm;
  mz *= norm;
  // Reference direction of Earth's magnetic field
  hx = 2.0f * mx * (0.5f - q3q3 - q4q4) + 2.0f * my * (q2q3 - q1q4) + 2.0f * mz * (q2q4 + q1q3);
  hy = 2.0f * mx * (q2q3 + q1q4) + 2.0f * my * (0.5f - q2q2 - q4q4) + 2.0f * mz * (q3q4 - q1q2);
  bx = sqrt((hx * hx) + (hy * hy));
  bz = 2.0f * mx * (q2q4 - q1q3) + 2.0f * my * (q3q4 + q1q2) + 2.0f * mz * (0.5f - q2q2 - q3q3);
  // Estimated direction of gravity and magnetic field
  vx = 2.0f * (q2q4 - q1q3);
  vy = 2.0f * (q1q2 + q3q4);
  vz = q1q1 - q2q2 - q3q3 + q4q4;
  wx = 2.0f * bx * (0.5f - q3q3 - q4q4) + 2.0f * bz * (q2q4 - q1q3);
  wy = 2.0f * bx * (q2q3 - q1q4) + 2.0f * bz * (q1q2 + q3q4);
  wz = 2.0f * bx * (q1q3 + q2q4) + 2.0f * bz * (0.5f - q2q2 - q3q3);
  // Error is cross product between estimated direction and measured direction of gravity
  ex = (ay * vz - az * vy) + (my * wz - mz * wy);
  ey = (az * vx - ax * vz) + (mz * wx - mx * wz);
  ez = (ax * vy - ay * vx) + (mx * wy - my * wx);
  if (Ki > 0.0f)
  {
    eInt[0] += ex;      // accumulate integral error
    eInt[1] += ey;
    eInt[2] += ez;
  }
  else
  {
    eInt[0] = 0.0f;     // prevent integral wind up
    eInt[1] = 0.0f;
    eInt[2] = 0.0f;
  }
  // Apply feedback terms
  gx = gx + Kp * ex + Ki * eInt[0];
  gy = gy + Kp * ey + Ki * eInt[1];
  gz = gz + Kp * ez + Ki * eInt[2];
  // Integrate rate of change of quaternion
  pa = q2;
  pb = q3;
  pc = q4;
  q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltat);
  q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * deltat);
  q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * deltat);
  q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * deltat);
  // Normalise quaternion
  norm = sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
  norm = 1.0f / norm;
  q[0] = q1 * norm;
  q[1] = q2 * norm;
  q[2] = q3 * norm;
  q[3] = q4 * norm;
} // void MahonyQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz)
//////////////////////////////////////////////

Oh yea, MPU9250's last about 3 months under 24/7 use. So, put in a socket instead of soldering to a board.

I buy them 5 at a time, test each one and, typically get 3 good ones and the rest are a bit drifty.

The problems I encountered with the MPU9250 is that one of the gyros may stick or one of the gyro's/accelerometers will develop sustained oscillations.

MPU9250's last about 3 months under 24/7 use

It could be that you are abusing them by failing to use 5V to 3.3V level shifters, or they are counterfeit or rejects. If the latter, send them back to the seller.

I quit using the MPU9250's, in favor of a MMU with both the MMU and the magnetometer on the SPI buss where I trigger the IMU read once every 40ms

Excellent, thank you. I will try to work myself into the code

Idahowalker:
Could you explain how a tilt compensated compass causes a compass to be automatically calibrated? I to have a tile compensated compass on an X/Y platform which I cause to go through an alignment upon each boot.

Also, why do I have to the make a figure 8 with my phone to align my phone compass when it has a tilt compensated compass?

???????????