top of page
Search
  • Writer's pictureIfrim Ciprian

Compass - Nicla Sense ME

The Nicla Sense ME has a built in orientation algorithm that is able to use the Magnetometer with an accelerometer to calculate the heading, pitch and roll similar to that of a compass.

The simple algorithm behind the heading is the following:

angle = atan2(Y, X);

Atan is a function that calculated the inverse of the tangent in radians. The following website has a good explanation about the Inverse Trigonometry function atan: https://communityviz.city-explained.com/communityviz/s360webhelp4-1/Formulas/Function_library/Atan_function.htm#:~:text=The%20Atan%20function%20is%20a,is%20sometimes%20written%20tan%2D1.


For the tilt-compensated version, we need to project the 3D-vector [X, Y, Z] onto a plane [X2, Y2] first and then use the same formula as before.


Then we use the X, Y, and Z readings to compute the location of "north" relative to whichever way we're pointing (keeping in mind that "north" changes as much as 30 degrees depending where on earth we are (called declination - see http://www.magnetic-declination.com/), and that it's not horizontal to the ground either (confusingly, called "inclination" - see https://physics.stackexchange.com/questions/283761/if-you-hold-a-compass-needle-vertical-does-it-point-down-or-up-differently-on-wh/283782 )


Now, of course, this is already part of the algorithm itself in the orientation, however we still need to convert this to a 16-point compass with the whole 360 degree "circle", with no blind spots.

As can be seen here:

More information about the points on a compass can be found here: https://en.wikipedia.org/wiki/Points_of_the_compass.

The whole naming scheme of the half-winds is as follows: The half-winds are north-northeast (NNE), east-northeast (ENE), east-southeast (ESE), south-southeast (SSE), south-southwest (SSW), west-southwest (WSW), west-northwest (WNW) and north-northwest (NNW).


In code it looks like the following:



#include "Arduino_BHY2.h"
#include "Nicla_System.h"

SensorOrientation orientation(SENSOR_ID_ORI);
String compass_array[13];
int StringCount = 0;
String compass_heading = "";

void setup() {
  nicla::begin();
  Serial.begin(115200);
  BHY2.begin();
  orientation.begin();
}

void loop() {
  static auto lastCheck = millis();
  BHY2.update();

  if (millis() - lastCheck >= 1000) {
    lastCheck = millis();
    
    Serial.println(orientation.toString());
    
    string_separator(orientation.toString(), compass_array, 0);
    float heading = (compass_array[4]).toFloat() + 28;
    float roll = (compass_array[12]).toFloat();
    compass_heading ="";

    Serial.println(heading);
    Serial.println(roll);

    if (0 <= heading <= 11.25) compass_heading = "You are heading NORTH!";
    else if (348.75 < heading <= 360) compass_heading = "You are heading NORTH!";
    else if (78.75 < heading <= 101.25) compass_heading = "You are heading EAST!";
    else if (168.75 < heading <= 191.25) compass_heading = "You are heading SOUTH!";
    else if (258.75 < heading <= 281.25) compass_heading = "You are heading WEST!";

    else if (33.75 < heading <= 56.25) compass_heading = "You are heading NORTH-EAST!"; 
    else if (123.75 < heading <= 146.25) compass_heading = "You are heading SOUTH-EAST!";
    else if (213.75 < heading <= 236.25) compass_heading = "You are heading SOUTH-WEST!";
    else if (303.75 < heading <= 326.25) compass_heading = "You are heading NORTH-WEST!";

    else if (11.25 < heading <= 33.75) compass_heading = "You are heading NORTH-NORTH-EAST!"; 
    else if (56.25 < heading <= 78.75) compass_heading = "You are heading EAST-SOUTH-EAST!";
    else if (101.25 < heading <= 123.75) compass_heading = "You are heading EAST-SOUTH-EAST!";
    else if (146.25 < heading <= 168.75) compass_heading = "You are heading SOUTH-SOUTH-EAST!";
    else if (191.25 < heading <= 213.75) compass_heading = "You are heading SOUTH-SOUTH-WEST!";
    else if (236.25 < heading <= 258.75) compass_heading = "You are heading WEST-SOUTH-WEST!";
    else if (281.25 < heading <= 303.75) compass_heading = "You are heading WEST-NORTH-WEST!";
    else if (326.25 < heading <= 348.75) compass_heading = "You are heading NORTH-NORTH-WEST!";
    
    Serial.println(compass_heading);    
  }
}

This code uses the String Separation function that I have talked about in my previous blog post:https://ciprianaa30.wixsite.com/aurismartwatch/post/string-separation-function-arduino


Furthermore, the heading and roll values need to be optimised and compared against other accurate devices. From a brief analysis it looks like there is circa 8-35 degrees difference compared to my smartphone compass, but this value can be affected in many ways by magnetic fields generated by all kinds of devices in my household.


Therefore, I will be testing the device against my smartphone in my garden, as far away as possible from any electrical devices, of about 10 meters.

Then will be put all the points on a graph and calculate the Statistical Regression to create a function with the correct coefficients to have the right output.


Another improvement, will be to translate the if/else if statements into a for loop with 2 arrays one containing the values to check for, with the second the index of the string/voice line to output. This will make the code more readable and have a lower code footprint.


The graph will look similar to:

More on the calibration will be shown in the upcoming week.

7 views0 comments

Recent Posts

See All
bottom of page