I have a magnetometer from where I get X, Y and Z which point to the direction of the detectable magnetic fields.

I'm using these values to get the angle between X and Y using atan2. I am a bit suprised of the result because North is not "aligned" with South. In fact there is not an angle of 90° between any "axes" (N, S, E or W). I can reproduce it with my phone. Can someone explain me why ? Am I Wrong ? Am I missing something ?

In fact my real problem is that my angle is not "linear". For example, in the image below, between North and East, with atan2 I get 270° which is obviously wrong regarding to the direction.

Here below, what it looks like :

Edit : diagram starts with East : angle = 0°. South is draw when angle = 90°.

magnetic cardinal points


My magnetometer is a IIS2MDC mems from STMicroelectronic. I am getting values from registers with a nordic MCU (but it would be the same with a ST MCU)

Edit :

My algorithm is copied from this post : https://arduino.stackexchange.com/questions/18625/converting-three-axis-magnetometer-to-degrees

Here is my function which converts from X, Y and Z raw values to angles in degrees :

static double main_calculate_angle_degree(int16_t x, int16_t y){
    double angle;
    angle = atan2((double)y, (double)x);

    if (angle >= 0) {
      angle = angle * (180 / M_PI);
    else {
      angle = (angle + 2 * M_PI) * (180 / M_PI);

    if(angle > 350){ // angle 360 = 0 because it is a circle, of course !
      angle = 360 - angle;

    return angle;

Edit 2 : my algorithm

I have an offset which makes x = 0 in default position. This offset is applied to all axes. I tried without this offset and it makes no difference !

When angle = 0° : X² + Y² = 2² + 35² = 1229 (X is the average of 5 X values and the same for Y) Then I rotate my mems of 90°, the angle displayed by my device is around 70° : X² + Y² = 300² + 103² = 100609

Edit 3 : a photo of my setup I'm using the dev board of an nrf MCU and the eval board of the mems. Both are attached together. I tried to move around the big dev board alone aroung the mems and it seems to have no impact.

enter image description here

  • 1
    \$\begingroup\$ I'm not clear what that diagram is even representing, but I'm guessing the maths is wrong: failure to normalise? Degrees/radians issue? Nonrepeatability? (magnetometers aren't hugely accurate) \$\endgroup\$
    – pjc50
    Oct 20, 2020 at 9:31
  • \$\begingroup\$ The diagram starts from East (angle = 0°). South is when the angle is at 90°, West when angle is at 180°... Here is what I am doing : atan2(y,x) and after that I am converting radian to degree : angle = angle * 180 / pi. \$\endgroup\$
    – Simon F
    Oct 20, 2020 at 9:44
  • \$\begingroup\$ X, Y, and Z together form a vector that points to magnetic North; that is all the information you get from a magnetometer. I don't understand how you manage to calculate East, South and West from that and come up with strange angles. Could you edit the code and/or your calculations into your question? \$\endgroup\$
    – ocrdu
    Oct 20, 2020 at 9:53
  • \$\begingroup\$ @ocrdu I edited my post as a reply \$\endgroup\$
    – Simon F
    Oct 20, 2020 at 10:11
  • \$\begingroup\$ Might be worth mentioning your location. If you're in Hudson's Bay any answer will be wrong. \$\endgroup\$ Oct 20, 2020 at 11:20

2 Answers 2


Your approach is sound, but the device with its 50-gauss dynamic range is not ideal for measuring fields this small. It can still likely work with the proper correction. The earth's magnetic field strength is at most about 600 milligauss and is not parallel with the earth's surface but is "dipping" at some angle, and your magnetometer has a specified offset of 60 milligauss even after the IIS2MDC's "offset calibration." This is more than enough to explain your anomaly.

To get a good reading take these steps. Place the magnetometer on a flat, level surface. Take your readings and record them. Then repeat at each of your 90-degree positions (sounds like you have already done this).

For the "x" axis, look at any two readings that are 180 degrees apart. One of these readings is the local magnetic field plus the offset, and the other is the negative of the local magnetic field plus the offset. Add them together, and you have two times your offset, so divide by two. You must subtract the offset from every x axis reading. Repeat for two "y" readings 180 degrees apart for the "y" offset value. Then you can do the "z" axis if you want by flipping the board over, although as you point out, "z" is not needed if you are always keeping the board level. This will remove the inherent offset. Be careful to keep the magnetometer in the same spot as you rotate to prevent any effect from local magnetic field variance, or be sure you are in an area free from iron and DC currents. Applying these offset corrections will greatly improve your results.

Next, take two readings of "x" 90 degrees apart and perform the square root of the sum of the squares (after subtracting your offset value calculated above). Do the same for "y." This provides the amplitude of the field and these numbers should theoretically be equal. If they are not, there is a difference in sensitivity (gain) between the x and y values. Apply a correction factor to one or the other to make them equivalent - it doesn't matter which one, since you are going to be taking a ratio of the two values.

Do not initiate another hardware "offset calibration" or you will have to repeat this process. If you calibrate "z" as well, and add an accelerometer you can do some math and have a board that works even when it is not level; otherwise you need to make sure you are level and only need "x" and "y."

Good luck!

  • \$\begingroup\$ Hi ! Your idea is really inventive. If you see my answer to my own question, I explain why my value was that bad. Despite this, you solution can be used as a complementary help to get more precision after using hard-iron compensation. \$\endgroup\$
    – Simon F
    Nov 4, 2020 at 8:59

I will answer to my own question !

First of all, thank you for your help guys.

I posted the same Question directly on stm32 community forum and I got a reply from a ST employee. (https://community.st.com/s/feed/0D53W00000LcM9KSAV) The behaviour I described is in fact the exact behaviour when you don't have compensation for hard-iron effect.

I know that links to other websites are not the best but this blog is really clear on this topic : https://www.fierceelectronics.com/components/compensating-for-tilt-hard-iron-and-soft-iron-effects

The theory is when you rotate your mems around Z axe without shifting it, values of (X + Y) should always be equal. Thus you can make a graph of Y = X to see if your mems respects this.

  • If the circle is deformed (it is an elipse) then you have soft-iron effect
  • If the circle is not centered to (0, 0) then you have hard-iron effect

With IIS2MDC, the soft-iron compensation is quite automatically manage. That's why I have (almost) no problem of "elipse".

Here below you can see the difference before and after hard-iron effect compensation enter image description here

Now the real question is "How do you calculate compensation?"

If you see the linked blog, the reply is quite simple. You follow the same calibration as when using Compass with your phone : your rotate your device.

After rotation your device (I did it to 360°) you got :

  • X_compensation = (Xmax + Xmin) / 2
  • Y_compensation = (Ymax + Ymin) / 2

These compensations must be applied to all your raw X and Y values. After that you can calculate your angle like I do in my original message.

Note :

  • This is true only if mems is rotating on a plane (and one axe is perfectly vertical)
  • This won't be perfect at all. If you want to have really accurate values, you will have to use more elaborate maths.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.