SparkFun Micro Magnetometer - MMC5983MA (Qwiic)

Hello all,

First time here.

I have a “SparkFun Micro Magnetometer - MMC5983MA (Qwiic)”.

It is connected to an Arduino Pro Mini 3.3V via Qwiic breadboard cable.

running ‘Simple_measurement’ or ‘Digital_ compass’ examples the results do not change if the device is moved.

Heading values 129 -133 however the device is oriented.

Anybody any ideas.

Thanks

Vaughn

Magnetometers all need to be calibrated before they can be used as a compass. There are many tutorials on line for how to do that, for example

Best and most comprehensive: https://thecavepearlproject.org/2015/05 … r-arduino/

Detailed example: https://forum.pololu.com/t/correcting-t … eter/14315

The MMC5983MA has an internal function that allows to calculate the three offsets automatically, although this may not be sufficient to obtain accurate heading measurements. See the Sparkfun library example 7 (I2C sensor offset).

Thank you @jremington. I have read the cavepearl project, I think I will get one of the sensors that has been used. Once I have better understanding I’ll try my MMC5983MA again.

Once again

Thank You

Vaughn

The magnetometer you have should work fine.

In any case, it would be helpful for others if you would run the library example “I2C sensor offset” and tell us what offsets you get. One or more of them is likely to be very different than the value assumed by the compass application.

You will have to add a bit of code to the example, to print out those offsets.

Hello,

The attachment shows the output from example 7 offsets.

.

I read from the data sheet the details of set/reset which are in the initial comments of the .ino file.

I ran code that would output rawX, HX, normalisedX, and GaussX. none of the values changed when the sensor was moved.

I found the following video on the MEMSIC MMC5983MA which shows the sensor working straight out of the box within 10 degrees. No Calibration!

That is what I was expecting to see, not just the lsb changing by one or two when I move it.

https://youtu.be/mlgslhsQ-L8

I have it setup with a ‘Nano Every’ exactly as the video, minus the pullups as they are on the Sparkfun Qwiic.

I get values from 155 - 159 on compass heading however it is oriented?

Something is not right!!

The unmodified example 7 output does not show the offsets. Please try this slightly modified code and post the offsets that the program calculates and prints.

/*
  Removing the bridge offset from the MMC5983MA
  By: Paul Clark
  SparkFun Electronics
  Date: February 9th, 2023
  License: SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT).

  Feel like supporting our work? Buy a board from SparkFun!
  https://www.sparkfun.com/products/19034

  This example shows how to remove the sensor (bridge) offset.
  Press a key (send any character) to perform the set-reset and update the offset.

  Hardware Connections:
  Plug a Qwiic cable into the sensor and a RedBoard
  If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper
  (https://www.sparkfun.com/products/17912) Open the serial monitor at 115200 baud to see the output

  From the datasheet:

  USING SET AND RESET TO REMOVE BRIDGE OFFSET
  
  The integrated SET and RESET functions of the MMC5893VA enables the user to remove the error
  associated with bridge Offset change as a function of temperature, thereby enabling more precise heading
  measurements over a wider temperature than competitive technologies. The SET and RESET
  functions effectively alternately flip the magnetic sensing polarity of the sensing elements of the device.
  
  1) The most accurate magnetic field measurements can be obtained by using the protocol described
  as follows: Perform SET. This sets the internal magnetization of the sensing resistors in the
  direction of the SET field.
  
  2) Perform MEASUREMENT. This measurement will contain not only the sensors response to the
  external magnetic field, H, but also the Offset; in other words,
  Output1 = +H + Offset.
  
  3) Perform RESET. This resets the internal magnetization of the sensing resistors in the
  direction of the RESET field, which is opposite to the SET field (180o opposed).
  
  4) Perform MEASUREMENT. This measurement will contain both the sensors response to the
  external field and also the Offset. In other words,
  Output2 = -H + Offset.
  
  5) Finally, calculate H by subtracting the two measurements and dividing by 2. This procedure
  effectively eliminates the Offset from the measurement and therefore any changes in the
  Offset over temperature.
  
  Note: To calculate and store the offset; add the two measurements and divide by 2. This calculated offset
  value can be subtracted from subsequent measurements to obtain H directly from each measurement.
*/

#include <Wire.h>

#include <SparkFun_MMC5983MA_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_MMC5983MA

SFE_MMC5983MA myMag;

void setup()
{
  Serial.begin(115200);
  
  // Most Serial prints have been commented out - to allow the data to be plotted nicely using Tools \ Serial Plotter
  //Serial.println("MMC5983MA Example");
  
  Wire.begin();
  
  if (myMag.begin() == false)
  {
    Serial.println("MMC5983MA did not respond - check your wiring. Freezing.");
    while (true)
      ;
  }
  
  myMag.softReset();
  
  //Serial.println("MMC5983MA connected");
  //Serial.println("Press a key (send any character) to perform the set-reset and update the offset.");
  
  while (Serial.available())
    Serial.read(); // Empty the Serial RX buffer
}

void loop()
{
  // The magnetic field values are 18-bit unsigned. The zero (mid) point is 2^17 (131072).
  // Use static variables to hold the offset. Set to 131072 initially.
  static uint32_t offsetX = 131072;
  static uint32_t offsetY = 131072;
  static uint32_t offsetZ = 131072;

  // Has the user pressed a key?
  if (Serial.available())
  {
    updateOffset(&offsetX, &offsetY, &offsetZ); // Update the offsets

    while (Serial.available())
      Serial.read(); // Empty the Serial RX buffer
  }

  uint32_t currentX = 0;
  uint32_t currentY = 0;
  uint32_t currentZ = 0;

  // This reads the X, Y and Z channels simultaneously
  myMag.getMeasurementXYZ(&currentX, &currentY, &currentZ);

  // The magnetic field values are 18-bit unsigned.
  // The zero (mid) point should be 2^17 (131072).
  // Here we subtract the offset, then normalize each field to +/- 1.0,
  // then multiply by 8 to convert to Gauss
  double normalizedX = (double)currentX - (double)offsetX; // Convert to double _before_ subtracting
  normalizedX /= 131072.0;
  normalizedX *= 8.0;
  double normalizedY = (double)currentY - (double)offsetY; // Convert to double _before_ subtracting
  normalizedY /= 131072.0;
  normalizedY *= 8.0;
  double normalizedZ = (double)currentZ - (double)offsetZ; // Convert to double _before_ subtracting
  normalizedZ /= 131072.0;
  normalizedZ *= 8.0;

  // Print the three channels with commas in between so the Serial Plotter can plot them
  Serial.print(normalizedX, 5); // Print with 5 decimal places
  Serial.print(",");
  Serial.print(normalizedY, 5);
  Serial.print(",");
  Serial.println(normalizedZ, 5);
}

bool updateOffset(uint32_t *offsetX, uint32_t *offsetY, uint32_t *offsetZ) // Update the offsets
{
  bool success = true; // Use AND (&=) to record if any one command fails

  success &= myMag.performSetOperation(); // Perform the SET operation

  uint32_t setX = 131072;
  uint32_t setY = 131072;
  uint32_t setZ = 131072;

  success &= myMag.getMeasurementXYZ(&setX, &setY, &setZ); // Read all three channels
  success &= myMag.getMeasurementXYZ(&setX, &setY, &setZ); // Do it twice - just in case there is noise on the first

  success &= myMag.performResetOperation(); // Perform the RESET operation

  uint32_t resetX = 131072;
  uint32_t resetY = 131072;
  uint32_t resetZ = 131072;

  success &= myMag.getMeasurementXYZ(&resetX, &resetY, &resetZ); // Read all three channels
  success &= myMag.getMeasurementXYZ(&resetX, &resetY, &resetZ); // Do it twice - just in case there is noise on the first

  // Calculate the offset - as per the datasheet.
  // The measurements are 18-bit so it's OK to add them directly.
  if (success)
  {
    *offsetX = (setX + resetX) / 2;
    *offsetY = (setY + resetY) / 2;
    *offsetZ = (setZ + resetZ) / 2;
	Serial.print("Offsets: ")
	Serial.print((setX + resetX) / 2);
	Serial.print(", ");
	Serial.print((setY + resetY) / 2);
	Serial.print(", ");
	Serial.print((setZ + resetZ) / 2);
	Serial.println();
  }

  return success;
}

Sorry.

I realised it wasn’t what you asked for when I re-read the sketch but had no time to do it again.

However, I now have what I believe you asked for, but with a bit extra.


The point at which X goes from -ve to +ve is after the sensor has been rotated thro’ 180 degrees.

Having noticed the large change in Gauss this time, I decided to re-run Digital Compass.

It is now functioning more like the output in the video.

I have not set up a proper jig to test it, but the heading values I am getting range from 24 - 350.

It must have needed a lot of degaussing to start with.

Now I know it is operational I can start doing some testing/calibrating to really understand what I am doing with it.

As I suspected, the calculated offsets are VERY different than what the compass program assumes (especially in Y and Z), which are as follows. That alone is enough to completely invalidate the assumptions made in the compass program.

   normalizedX = (double)rawValueX - 131072.0;
    normalizedX /= 131072.0;

    normalizedY = (double)rawValueY - 131072.0;
    normalizedY /= 131072.0;

    normalizedZ = (double)rawValueZ - 131072.0;
    normalizedZ /= 131072.0;

You should get much better (that is, actually useful) results if you calibrate the compass using the procedures I linked earlier.

Thank you again for all your help and guidance. I understand it all a lot better now.

By the way, it is NOT CORRECT to divide the raw value by the offset, and call this “normalized”.

Normalized values are calculated as follows, where the float values x, y, and z are the offset-corrected magnetometer measurements:

float magnitude = sqrt(x*x + y*y + z*z);
normx = x/magnitude;
normy = y/magnitude;
normz = z/magnitude;

Thanks for that. So the library example is wrong! Who can you trust when you are learning?

You are saying if say x=y=z=5 then sqrt(25+25+25)=8.660254 so norm x =5/8.660254 =0.5771979

so x is 5 and normx is 0.6 ??? I do not understand. what is normalizing supposed to do?

“Normalizing” converts an arbitrary vector into a unit vector, with length=1. Doing so is required for some types of vector operations, such as calculating angles.

Hi James,

Instead of “normalized”, what term should we be using? “Scale” perhaps? The intent is to scale the values to a more useable +/- 1.0.

Best wishes,

Paul

Mapping or scaling would be less confusing. The other issue is that in general, each axis needs a separate scale factor to make them consistent, and the present example code might suggest to some that the offset value accomplishes this as well.

Code to correct magnetometer data, in its simplest form, requires six parameters, for example something like this:

magx_corrected = (magx_raw - x_offset)*x_scale;
magy_corrected = (magy_raw - y_offset)*y_scale;
magz_corrected = (magz_raw - z_offset)*z_scale;

OK - thanks! I’ll update the examples and add some additional comments - when I get time.

Best wishes,

Paul

Thanks James - this is done:

https://github.com/sparkfun/SparkFun_MM … tag/v1.1.2

Best wishes,

Paul

Looks good, and the added comments regarding offsets and scaling will be very helpful, thanks!

Hello James & Paul,

I have been following your conversation I understand it all now. Thank you for the new documented examples.

I am now very familiar with my little magnetometer .

I have tried Compass example as is i.e. dividing by 131072, and I tried it with the mag=sqrt(xx + yy) adjusted X= x/mag, adjusted Y=y/mag. As the numbers are pretty much the same and both are constants, the outcomes are not dissimilar. I am still wading through all the documentation from Sailboat instruments and the cave pearl project about calibration.

Keeps a 71 year old off the streets!

Thanks again,

Vaughn

As the numbers are pretty much the same and both are constants, the outcomes are not dissimilar. 

I don’t understand what you mean.

For a compass using just X and Y data (must be held level with Z vertical), the adjusted values of magX and magY should fall on a circle when plotted.

If the calibration is correct, the circle will be centered on (0, 0). Hopefully you will to see something like the plot below.

Heading in degrees is then given by 180.0*atan2(-magX, magY)/3.14159, with 0 degrees for X pointing at magnetic North.

Capture.PNG