Recreating the LSM303 heading example for MinIMU-9


I teach a high school project course which used the LSM303 as a simple compass. The LSM303 has been discontinued, so we are looking at alternatives- the first one we are trying is the MinIMU-9 v5. The old LSM303 library had an example called heading that generated a single angle, which is meant to be the heading compared to North. This uses both the magnetometer and accelerometer functions, as well as a simple calibration based off scaling.

The MinIMU-9 v5 does not have a similar built-in example. I am trying to create a substitute, but I am encountering issues integrating the LIS3MDL magnetometer readings and the LSM6 accelerometer readings in a way that matches what heading does. I have made an attempt at a calibration using only the magnetometer min and max values from the LS3MDL calibrate sketch, but this creates a poor calibration.

Has anyone already made a sketch that does what heading does- generating a single angle measure, such that you can control which axis is used as the reference- but for the MiniMU-9? Alternatively, is there another chip that has this functionality already available? If I could get a handle on how to do the vector math portions in a sketch (rather than a library), I could try that as well. I need a single sketch I can hand over to students who have not touched C++ before and not ask much beyond changing calibration values, so Heading was wonderful, and we sorely miss its loss.

This is Heading from the LSM303 library:

Returns the angular difference in the horizontal plane between the
"from" vector and north, in degrees.

Description of heading algorithm:
Shift and scale the magnetic reading based on calibration data to find
the North vector. Use the acceleration readings to determine the Up
vector (gravity is measured as an upward acceleration). The cross
product of North and Up vectors is East. The vectors East and North
form a basis for the horizontal plane. The From vector is projected
into the horizontal plane and the angle between the projected vector
and horizontal north is returned.
template <typename T> float LSM303::heading(vector<T> from)
    vector<int32_t> temp_m = {m.x, m.y, m.z};

    // subtract offset (average of min and max) from magnetometer readings
    temp_m.x -= ((int32_t)m_min.x + m_max.x) / 2;
    temp_m.y -= ((int32_t)m_min.y + m_max.y) / 2;
    temp_m.z -= ((int32_t)m_min.z + m_max.z) / 2;

    // compute E and N
    vector<float> E;
    vector<float> N;
    vector_cross(&temp_m, &a, &E);
    vector_cross(&a, &E, &N);

    // compute heading
    float heading = atan2(vector_dot(&E, &from), vector_dot(&N, &from)) * 180 / PI;
    if (heading < 0) heading += 360;
    return heading;

template <typename Ta, typename Tb, typename To> void LSM303::vector_cross(const vector<Ta> *a, const vector<Tb> *b, vector<To> *out)
  out->x = (a->y * b->z) - (a->z * b->y);
  out->y = (a->z * b->x) - (a->x * b->z);
  out->z = (a->x * b->y) - (a->y * b->x);

template <typename Ta, typename Tb> float LSM303::vector_dot(const vector<Ta> *a, const vector<Tb> *b)
  return (a->x * b->x) + (a->y * b->y) + (a->z * b->z);


Here is a sketch for the LIS3MDL and LSM6 that should work about the same way as the LSM303 library’s Heading example:

LIS3MDL-LSM6-Heading.ino (3.8 KB)

Please let me know how it works for you. We’ve been considering adding it as an example to the LIS3MDL library, since some other people have asked for something like this in the past, so if it seems fine I’ll go ahead and do that.


Thank you so much- this is exactly what I was trying to make. It acts just like heading, except that you alter the angle inside the function computeHeading(), rather than setting it in loop.

If I may make a small suggestion: allow computeHeading() to take the desired reference vector as an input. Though it would not be difficult to make additional functions for the different axis (computeHeadingX(), computeHeadingY(), etc), this change would allow you to take angle measures across a range of references without making a new function for every direction.

Other than that small suggestion, this is just what the doctor ordered. Thank you again!

Honestly, I think this should be added as-is, since it can use the same syntax as the LSM303 Heading. Just the same as the old code, you just directly call the template, rather than the function that calls the template.

Thank you again, I recommend adding this code to the library!


I’m glad you found the code useful, and I’ll work on adding it to the library. There are already two versions of the computeHeading() function, one that takes no arguments and uses the +X axis by default and another that takes a reference vector of your choice (the comments starting on line 59 explain how to use them). Could you see if that is what you’re looking for?


I do see it, it works much the same in the original Heading code. Thank you again for your help, this will help a ton.