Not getting data from AK8963 magnetometer in mpu9250

First post. Sorry if this isn't the right place, but looked for something like Development/Programming Questions, for example, and couldn't find.

Connected an ESP32S3 Dev Board (no pin 22, btw) to an mpu9250 gyro/accel and [supposedly containing] a magnetometer AK8963. The relevant chip in the mpu9250 board is marked MP92 917LD1 2248 or 224B?; tried to find a datasheet for that, but couldn't locate one. This is a cheap chinese board (around 4€).

The code below for configuring the magnetometer hangs when we try to get data from it. It runs until that point:

09:56:52.304 -> ESP-ROM:esp32s3-20210327
09:56:52.304 -> Build:Mar 27 2021
09:56:52.304 -> rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
09:56:52.304 -> SPIWP:0xee
09:56:52.304 -> mode:DIO, clock div:1
09:56:52.304 -> load:0x3fce3808,len:0x44c
09:56:52.304 -> load:0x403c9700,len:0xbe4
09:56:52.304 -> load:0x403cc700,len:0x2a68
09:56:52.304 -> entry 0x403c98d4
09:56:52.355 -> Configure MagnetometerDisable MPU9250 I2C master interface...done.
09:56:52.355 -> Enable MPU9250 interface bypass mux...done.
09:56:52.355 -> Access AK8963 fuse ROM...done.
09:56:52.460 -> Open and point session to AK8963 fuse ROM...done.
09:56:52.460 -> Request 3 bytes of data from fuse ROM (ASAX, ASAY, ASAZ)...

The code is:

/* 
    Test Config Magnetometer in MPU9250

    contains parts from;
    https://www.instructables.com/Tilt-Compensated-Compass/

    https://www.instructables.com/member/lingib/instructables/

*/

// ----- Libraries
#include <Wire.h>

// ----- Gyro
#define MPU9250_I2C_address 0x68                                        // I2C address for MPU9250 
#define MPU9250_I2C_master_enable 0x6A                                  // USER_CTRL[5] = I2C_MST_EN
#define MPU9250_Interface_bypass_mux_enable 0x37                        // INT_PIN_CFG[1]= BYPASS_EN

// ----- Magnetometer
#define AK8963_I2C_address 0x0C                                             // I2C address for AK8963
#define AK8963_cntrl_reg_1 0x0A                                             // CNTL[4]=#bits, CNTL[3:0]=mode
#define AK8963_status_reg_1 0x02                                            // ST1[0]=data ready
#define AK8963_data_ready_mask 0b00000001                                   // Data ready mask
#define AK8963_overflow_mask 0b00001000                                     // Magnetic sensor overflow mask
#define AK8963_data 0x03                                                    // Start address of XYZ data                                                                
#define AK8963_fuse_ROM 0x10                                                // X,Y,Z fuse ROM

bool    Record_data = false;
int     Mag_x_offset = 46,      Mag_y_offset = 190,     Mag_z_offset = -254;   // Hard-iron offsets
float   Mag_x_scale = 1.01,     Mag_y_scale = 0.99,     Mag_z_scale = 1.00;    // Soft-iron scale factors
float   ASAX = 1.17,            ASAY = 1.18,            ASAZ = 1.14;           // (A)sahi (S)ensitivity (A)djustment fuse ROM values.

void setup()
{
  Serial.begin(115200);
  //Wire.begin(21, 20, 400000); // SDA, SCL pins an Frequency
  Wire.begin(21, 20); // SDA, SCL pins
  Wire.setClock(400000);
  Serial.print("Configure Magnetometer");                           // Print text to screen

  // ----- Configure the magnetometer
  configure_magnetometer();
}

void loop()
{


}

// ----------------------------
//  Configure magnetometer
// ----------------------------
void configure_magnetometer()
{
  /*
     The MPU-9250 contains an AK8963 magnetometer and an
     MPU-6050 gyro/accelerometer within the same package.

     To access the AK8963 magnetometer chip The MPU-9250 I2C bus
     must be changed to pass-though mode. To do this we must:
      - disable the MPU-9250 slave I2C and
      - enable the MPU-9250 interface bypass mux
  */
  // ----- Disable MPU9250 I2C master interface
  Serial.print("Disable MPU9250 I2C master interface...");
  Wire.beginTransmission(MPU9250_I2C_address);                      // Open session with MPU9250
  Wire.write(MPU9250_I2C_master_enable);                            // Point USER_CTRL[5] = I2C_MST_EN
  Wire.write(0x00);                                                 // Disable the I2C master interface
  Wire.endTransmission();
  Serial.println("done.");

  // ----- Enable MPU9250 interface bypass mux
  Serial.print("Enable MPU9250 interface bypass mux...");
  Wire.beginTransmission(MPU9250_I2C_address);                      // Open session with MPU9250
  Wire.write(MPU9250_Interface_bypass_mux_enable);                  // Point to INT_PIN_CFG[1] = BYPASS_EN
  Wire.write(0x02);                                                 // Enable the bypass mux
  Wire.endTransmission();
  Serial.println("done.");

  // ----- Access AK8963 fuse ROM
  /* The factory sensitivity readings for the XYZ axes are stored in a fuse ROM.
     To access this data we must change the AK9863 operating mode.
  */
  Serial.print("Access AK8963 fuse ROM...");
  Wire.beginTransmission(AK8963_I2C_address);                       // Open session with AK8963
  Wire.write(AK8963_cntrl_reg_1);                                   // CNTL[3:0] mode bits
  Wire.write(0b00011111);                                           // Output data=16-bits; Access fuse ROM
  Wire.endTransmission();
  Serial.println("done.");
  delay(100);                                                       // Wait for mode change

  // ----- Get factory XYZ sensitivity adjustment values from fuse ROM
  /* There is a formula on page 53 of "MPU-9250, Register Map and Decriptions, Revision 1.4":
      Hadj = H*(((ASA-128)*0.5)/128)+1 where
      H    = measurement data output from data register
      ASA  = sensitivity adjustment value (from fuse ROM)
      Hadj = adjusted measurement data (after applying
  */
  Serial.print("Open and point session to AK8963 fuse ROM...");
  Wire.beginTransmission(AK8963_I2C_address);                       // Open session with AK8963
  Wire.write(AK8963_fuse_ROM);                                      // Point to AK8963 fuse ROM
  Wire.endTransmission();
  Serial.println("done.");

  Serial.print("Request 3 bytes of data from fuse ROM (ASAX, ASAY, ASAZ)...");
  Wire.requestFrom(AK8963_I2C_address, 3);                          // Request 3 bytes of data
  while (Wire.available() < 3);                                     // Wait for the data
  ASAX = (Wire.read() - 128) * 0.5 / 128 + 1;                       // Adjust data
  ASAY = (Wire.read() - 128) * 0.5 / 128 + 1;
  ASAZ = (Wire.read() - 128) * 0.5 / 128 + 1;
  Serial.println("done.");

  // ----- Power down AK8963 while the mode is changed
  /*
     This wasn't necessary for the first mode change as the chip was already powered down
  */
  Serial.print("Power down AK8963 while the mode is changed...");
  Wire.beginTransmission(AK8963_I2C_address);                       // Open session with AK8963
  Wire.write(AK8963_cntrl_reg_1);                                   // Point to mode control register
  Wire.write(0b00000000);                                           // Set mode to power down
  Wire.endTransmission();
  Serial.println("done.");
  delay(100);                                                       // Wait for mode change

  // ----- Set output to mode 2 (16-bit, 100Hz continuous)
  Serial.print("Set output to mode 2 (16-bit, 100Hz continuous)...");
  Wire.beginTransmission(AK8963_I2C_address);                       // Open session with AK8963
  Wire.write(AK8963_cntrl_reg_1);                                   // Point to mode control register
  Wire.write(0b00010110);                                           // Output=16-bits; Measurements = 100Hz continuous
  Wire.endTransmission();
  Serial.println("done.");
  delay(100);                                                       // Wait for mode change
}

Welcome to the forum.

How do you know that is a MPU-9250 ?
It is no longer made and its replacement, the ICM20948, costs 4.46 Euros (If you buy 1000).
You could try to read the ID and check if that matches with the datasheet.
Then you could try to use a library.
If everything seems to work, you still might have a counterfeit sensor that is not accurate.

Accept your loss and toss it away.
Buy a real 9 DOF sensor: https://www.adafruit.com/search?q=9dof&c=641
Preferably one of the LSM sensors or a BNO sensor. The BNO sensors have a processor on the module. They can speed up the development of a project, but they are not suitable for every project.
Don't buy a module that has multiple sensors on a module, that is what they did 10 years ago. Don't buy a TDK/Invense sensor, I still don't like those.
Don't buy from AliExpress/TEMU/Ebay/Amazon, they could be counterfeit or with the wrong components on the module, or without the right voltage levels for the I2C bus.

What is your project ? If it is a drone, then you probably need something faster.

Well, the board also is written: MPU-9250/6500 on one side and GY-6500 GY-9250 on the other :slight_smile:

This same board with an ESP32-S3 and un uncountable number of libaries tested before, sometimes reports uT readings, but very innacurate if we look at them in a loop. The gyro data is also very unstable. I'll buy probably the Adafruit one.

The project is for boat flaps actuation in order to keep a boat level, and enhance the get-off-the-"hole"-to-planning phases automatically. As an additional feature it would also fix an azymuth and keep the boat going straight to that heading, provided local conditions (speed, sea, wind) allow. Got the project already working and controlled through a touch-display (a Nextion NX9048 via serial) and now the gyro/magnetometer data is horribly failing.

Thanks, Koepel. what might you mean by that regarding LSM vs. BNO sensors?

Well, its a robot, should I say. But its reaction time can be high, at least 500ms.

So you are saying that they are probably counterfeit.

Distributers like mouser.com sell genuine parts.

Because the BNO sensors do the processing on the module, you get the data from the module and that is it. You can not get other data. But for your project, it might make the code a lot easier.
The LSM sensors are just sensors, you can use any library and try another library if you don't like it.

A Arduino Uno with a I2C bus to a MPU-6050 sensor and with some calculations can do 100 samples per second. If that is okay for you, then you can use a I2C bus, but a SPI bus is always better and faster.
You don't have to worry about doing calculations with a ESP32-S3. The AHRS filter is often used: https://github.com/jremington/LSM9DS1-AHRS

The filter is important. When combining the Accelerometer, the Gyro and the Magnetometer with a filter ("Sensor Fusion"), then a disadvantage of one sensor is compensated by the other two.

Those are often junk, made by unidentified Chinese manufacturers, and as noted above, the MPU-9250 was discontinued by the manufacturer years ago.

You will have much better luck with modern, 9DOF sensor modules made by companies that actually support their products, like Adafruit, Sparkfun, Pololu, Conrad, etc. They are more expensive, but they actually work, or are quickly replaced if they don't.

Thank ou very much, Koepel.

If with a BNO085-breakout board we can get processed data for gyro/accelerations and magnetometer, well it will surely let me more time for other stuff. Earlier tried also (cheap) mpu-6050 boards and ended up buying this 9250. Lost days so far trying to get stable roll/pitch angles and a correct heading (with the 9250). Will check the BNO085 libraries and examples to see if that fits my project, most probably will.

And will certainly go for SPI instead of 2-wire IIC.

Regarding pointing me out to the ESP32-S3 AHRS filter, that's also a blessing. WIll also check, alternatively, the LSM9DS1.

jremington, many thanks, will take your advice.

Guessing you are "the" jremington that published the AHRS filter in github :smiley:

I adapted already written code to several of the newer sensors. I prefer and recommend the Mahony filter, as it works very well and is the simplest and fastest.

The built in calibration for the BNO085 does not work very well.

Taking that external (coded) calibration might still be preferable.

Also, will explore the Mahony filter as soon as I get new hardware in.

Thanks again!

You MUST connect AD0/SDO to VCC or GND, using a resistor, to ensure the board I2C address (VCC 0x69 or GND 0x68). I've been fighting with this issue for the last two months. Just decided to full connect the MPU (Vin, GND, SDA, SCL) and now, AD0/SDO. All the libraries that were not workind decided to come to life. I am using a 100 ohms resistor. Connecting AGND to GND is also a good idea for more stable readings.

If you do not connect AD0/SDO, the board will answer with the other sensors, but the AK8963, missgiding you about the correcteness of the connections. The diagrams that you find on the web do not show this wiring tip.