Bad values provided by LIS3MDL

Hello,

I read bad values from the register of the magnetometer LIS3MDL.
For example, the Y value of the magnetic field is always negative. For this sone, i have checked the value in the register MSB OUT_Y_H = 0x2B and it contains always high values, so negative value are really provided by LISMDL.
The heading atan2 (Y/X) is also wrong.

I have tried different conf but i get always the same isssue.
My standard config is:

// FS = 10 (?12 gauss full scale ie 2281 LSB/gauss); REBOOT=0; SOFT_RST=0
// TEMP_EN = 1; OM = 11 (ultra-high-performance mode for X and Y); DO = 111 (80 Hz ODR); FAST_ODR=0; ST=0
// OMZ = 11 (ultra-high-performance mode for Z); BLE=0 (data LSb at lower address)
// LP=0; SIM =0; MD = 00 (continuous-conversion mode)
CTRL_REG1: 0xFC
CTRL_REG2: 0x40
CTRL_REG3: 0x0
CTRL_REG4: 0xC
CTRL_REG5: 0x0
Init OK, Address: 0x1E

I didn’t calibrate but it should not impac some much.
The offset registers at 0x05, 0x06, 0x07,0x08,0x09 and 0x0A are = 0
below a numerical example:

msb: 0
lsb: 1101011
mag_x: 107
msb: 11101010
lsb: 111
mag_y: -5625
msb: 11000
lsb: 10100110
mag_z: 6310

There is no I2C error as I trap all errors , below the focntion that read the registers

// Read a 16-bit register low byte first and then high byte
int16_t LIS3MDLClass::LIS3MDL_readReg16BitLH(uint8_t reg)
{
  int16_t value;
  uint8_t regstatus = 0x00;

  while (regstatus == 0x00)  // wait for values overwritten
  {
    uint8_t b = LIS3MDL_readReg(STATUS_REG);
    if (_last_status > 0) return _last_status;
    regstatus = b & 0x80;
  }
  
  Wire.beginTransmission(_address);
  Wire.write(reg);
  _last_status = Wire.endTransmission();
  if (_last_status > 0) return _last_status;

  _last_nb_receive = Wire.requestFrom(_address, (uint8_t)2);
  if (_last_nb_receive != 2) {_last_status=WIRE_REQUEST_ERROR;  Serial.print("WIRE_REQUEST_ERROR 2, _last_nb_receive: ");Serial.println(_last_nb_receive);return _last_status;}
    
  uint8_t lsb = Wire.read(); // value low byte
  uint8_t msb = Wire.read(); // value high byte
    Serial.print("msb: ");Serial.println(msb,BIN);
      Serial.print("lsb: ");Serial.println(lsb,BIN);
  value  = (((int16_t)msb) << 8) | lsb;
  
  return value;
}

double LIS3MDLClass::LIS3MDL_getMag_y()
{
  
  int16_t mag_y = LIS3MDL_readReg16BitLH(OUT_Y_L);
  if (_last_status >0) return (double)_last_status;
  Serial.print("mag_y: ");Serial.println(mag_y);
  return (((double)mag_y / 2281.0) - _my_zero);  // Full Scale Range ?12 gauss => 2281 LSB/gauss
}

You are mistaken.

Calibration is absolutely necessary, after mounting the module in its final resting place. See this post for how far off magnetometers can be, and what to do about it: Correcting the Balboa magnetometer

Hello, robotedh.

I am sorry to hear that you are getting values from your magnetometer that do not seem right. Where and how is _my_zero defined? Also, is there a reason that you are not using our LIS3MDL library?

-Jon

Hello,
my_zero are set to zero in the init function.

uint8_t LIS3MDLClass::LIS3MDL_init()
{
    uint8_t testReg = 0;
       
    _mx_zero = 0.0;
    _my_zero = 0.0;
    _mz_zero = 0.0;
    _last_status = 0;
    _last_nb_receive = 0;
      
    // check high address
    testReg = LIS3MDL_testReg(LIS3MDL_HIGH_ADDRESS);
    if (testReg == 0)
    {
        _address = LIS3MDL_HIGH_ADDRESS;
    }
    // check low address
    else if (_last_status >0) // try second address
    {
         testReg = LIS3MDL_testReg(LIS3MDL_LOW_ADDRESS);
         if (testReg == 0)
         {
           _address = LIS3MDL_LOW_ADDRESS;
         }
         else if (_last_status >0) return _last_status;    
    }
    
    if (testReg != 0)
    {
        Serial.print("Bad ID: 0x");Serial.print(testReg,HEX); Serial.print("Should be: 0x"); Serial.print(LIS3MDL_ID,HEX);
    }
        
/* Configure the LIS3MDL's magnetometer */
   
    // 0x00 = 0b0 10 0 0 0 00
    // FS = 10 (?12 gauss full scale ie 2281 LSB/gauss); REBOOT=0; SOFT_RST=0
    LIS3MDL_writeReg(CTRL_REG2, 0x40);
    if (_last_status > 0) return _last_status;

    // 0xF0 = 0b1 11 111 0 0
    // TEMP_EN = 1; OM = 11 (ultra-high-performance mode for X and Y); DO = 111 (80 Hz ODR); FAST_ODR=0; ST=0
    LIS3MDL_writeReg(CTRL_REG1, 0xFC);
    if (_last_status > 0) return _last_status;

    // 0x0C = 0b0000 11 0 0
    // OMZ = 11 (ultra-high-performance mode for Z); BLE=0 (data LSb at lower address)
    LIS3MDL_writeReg(CTRL_REG4, 0x0C);
    if (_last_status > 0) return _last_status;
        
    // 0x00 = 0b00 0 00 0 00
    // LP=0; SIM =0; MD = 00 (continuous-conversion mode)
    LIS3MDL_writeReg(CTRL_REG3, 0x00);
    if (_last_status > 0) return _last_status;
  
  uint8_t a = LIS3MDLClass::LIS3MDL_readReg(0x05);
  Serial.print("0x05: ");Serial.println(a,HEX);
   
  uint8_t b = LIS3MDLClass::LIS3MDL_readReg(0x06);
  Serial.print("0x06: ");Serial.println(b,HEX);
   
  uint8_t c = LIS3MDLClass::LIS3MDL_readReg(0x07);
  Serial.print("0x07: ");Serial.println(c,HEX);
   
  uint8_t d = LIS3MDLClass::LIS3MDL_readReg(0x08);
  Serial.print("0x08: ");Serial.println(d,HEX);
   
  uint8_t e = LIS3MDLClass::LIS3MDL_readReg(0x09);
  Serial.print("0x09: ");Serial.println(e,HEX);
   
  uint8_t h = LIS3MDLClass::LIS3MDL_readReg(0x0A);
  Serial.print("0x0A: ");Serial.println(h,HEX);
  
  uint8_t f;
    f = LIS3MDLClass::LIS3MDL_readReg(CTRL_REG1);
  Serial.print("CTRL_REG1: 0x");Serial.println(f,HEX);
     f = LIS3MDLClass::LIS3MDL_readReg(CTRL_REG2);
  Serial.print("CTRL_REG2: 0x");Serial.println(f,HEX);
     f = LIS3MDLClass::LIS3MDL_readReg(CTRL_REG3);
  Serial.print("CTRL_REG3: 0x");Serial.println(f,HEX);
     f = LIS3MDLClass::LIS3MDL_readReg(CTRL_REG4);
  Serial.print("CTRL_REG4: 0x");Serial.println(f,HEX);
     f = LIS3MDLClass::LIS3MDL_readReg(CTRL_REG5);
  Serial.print("CTRL_REG5: 0x");Serial.println(f,HEX);
    return 0;   
}

I use my lib instead of yours because i want to trap all the I2C errors.
note that in your lib the code while (Wire.available() < 6) may cause infinite loop becasue in case of I2C bus errror the TWI bufer will never be full, i prefer to check the result of requestFrom.
in all case, my lib read the register as your lib starting by LSB first .

Jim_Remington replied that i need to calibrate, I will try