DIY Arduino Compass
Today, everyone has a compass on their smartphone devices. With various software, that has powerful features. Therefore, in this article, I will share how to make an Arduino compass using MPU9250 Magnetometer and I2C OLED SSD1306 with u8g2 Library.
MPU-9250 Magnetometer
MPU-9260 is an IC or chip in which there are 4 sensors, Accelerometer, Gyroscope, Magnetometer, and Temperature. For making this compass, we only use one sensor, the Magnetometer.
The magnetometer is a component that is able to read the direction of the North and South magnetic fields of the Earth by sensing the Earth’s magnetic lines of force. The magnetometer of the MPU9250 will provide 3 coordinates of the magnetic field namely X, Y, and Z.
Magnetometer Orientation:
The orientation of the axis direction to the IC position can be illustrated as shown in the following figure:
Magnetics Declination
After we get the values of the X, Y, and Z orientations, then we must know the Declination angle and why we need it.
By default, the magnetometer will only read Earth’s magnetic north and south, not Earth’s true north and south. So here it should be noted that:
- Earth’s north ≠ Earth’s magnetic north
- Earth’s south ≠ Earth’s magnetic south
The difference between Earth’s north and Earth’s magnetic north is depicted in angles, as well as south. Well, this angle is called the Magnetic Declination angle.
To find the magnetic declination value, you can read it in this article.
Wiring
Attention !!! The MPU9250 uses 3.3V. Make sure you connect to Arduino 3.3V.
In addition, connecting the MPU9250 Magnetometer and OLED SSD1306 I2C sensors to Arduino is very easy. Only by using 2 data cables are SDA and SDC connected together. Look at the picture below:
Arduino Code
The arduino code that I use below requires two libraries, namely Sparkfun and U8g2. To be able to add these two libraries has been explained in the following article : Tutorial MPU9250 Arduino.
If the two libraries have been added to Arduino Ide, you can use the following program code & and upload it to your Arduino Board.
#include "quaternionFilters.h"
#include "MPU9250.h"
#include <U8g2lib.h>
#define I2Cclock 400000
#define I2Cport Wire
#define MPU9250_ADDRESS MPU9250_ADDRESS_AD0
MPU9250 myIMU(MPU9250_ADDRESS, I2Cport, I2Cclock);
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
//This line contain direction logo/bitmap
#define direction_width 9
#define direction_height 13
static const unsigned char direction_bits[] U8X8_PROGMEM = {
0x10, 0xfe, 0x10, 0xfe, 0x10, 0xfe, 0x10, 0xfe, 0x38, 0xfe, 0x38, 0xfe,
0x38, 0xfe, 0x7c, 0xfe, 0x7c, 0xfe, 0x7c, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe
};
const int centreX = 32;
const int centreY = 32;
const int radius = 23;
int xn, yn, xe, ye, xs, ys, xw, yw;
//offset setting for Display N-E-S-W
int offsetRadius = 5;
int offsetCentreX = -3;
int offsetCentreY = 1;
//offset setting for North
float offsetCalibration = -0.5;
void setup() {
Wire.begin();
u8g2.begin();
myIMU.initMPU9250();
byte d = myIMU.readByte(AK8963_ADDRESS, WHO_AM_I_AK8963);
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_5x7_tr);
u8g2.setCursor(0, 10);
if (d == 0x48) {
u8g2.print("Magnetometer Connected!");
} else {
u8g2.print("Magnetometer Failed!");
}
} while (u8g2.nextPage());
delay(2000);
myIMU.initAK8963(myIMU.factoryMagCalibration);
myIMU.getMres();
for (int i = 8; i > 0; i--) {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_5x7_tr);
u8g2.setCursor(0, 10);
u8g2.print("Need to calibrate ...");
u8g2.setCursor(0, 22);
u8g2.print("Please Wave device in ");
u8g2.setCursor(0, 32);
u8g2.print("a figure 8 until done!");
u8g2.setCursor(0, 42);
u8g2.print("Calibration will start in");
u8g2.setFont(u8g2_font_victoriabold8_8r);
u8g2.setCursor(60, 52);
u8g2.print(i);
} while (u8g2.nextPage());
delay(1000);
}
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_5x7_tr);
u8g2.setCursor(0, 10);
u8g2.print("Wave now !");
u8g2.setCursor(0, 22);
u8g2.print("Data is being collected...");
} while (u8g2.nextPage());
// The next call delays for 4 seconds, and then records about 15 seconds of
// data to calculate bias and scale.
myIMU.magCalMPU9250(myIMU.magBias, myIMU.magScale);
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_victoriabold8_8r);
u8g2.setCursor(0, 32);
u8g2.print("Calibration Done!");
} while (u8g2.nextPage());
delay(5000);
}
void loop() {
myIMU.readMagData(myIMU.magCount);
myIMU.mx = (float)myIMU.magCount[0] * myIMU.mRes * myIMU.factoryMagCalibration[0] - myIMU.magBias[0];
myIMU.my = (float)myIMU.magCount[1] * myIMU.mRes * myIMU.factoryMagCalibration[1] - myIMU.magBias[1];
myIMU.mz = (float)myIMU.magCount[2] * myIMU.mRes * myIMU.factoryMagCalibration[2] - myIMU.magBias[2];
myIMU.updateTime();
// Sensors x (y)-axis of the accelerometer is aligned with the y (x)-axis of
// the magnetometer; the magnetometer z-axis (+ down) is opposite to z-axis
// (+ up) of accelerometer and gyro! We have to make some allowance for this
// orientationmismatch in feeding the output to the quaternion filter. For the
// MPU-9250, we have chosen a magnetic rotation that keeps the sensor forward
// along the x-axis just like in the LSM9DS0 sensor. This rotation can be
// modified to allow any convenient orientation convention. This is ok by
// aircraft orientation standards! Pass gyro rate as rad/s
MahonyQuaternionUpdate(myIMU.ax, myIMU.ay, myIMU.az, myIMU.gx * DEG_TO_RAD, myIMU.gy * DEG_TO_RAD, myIMU.gz * DEG_TO_RAD, myIMU.my, myIMU.mx, myIMU.mz, myIMU.deltat);
float heading = atan2(myIMU.mx, myIMU.my) + offsetCalibration;
// How to get the magnetic declination angle
// is available in the article https://bit.ly/3Zd6e2z
float declination = -0.7;
heading += declination;
// Correct for when signs are reversed.
if (heading < 0)
heading += 2 * PI;
// Check for wrap due to addition of declination.
if (heading > 2 * PI)
heading -= 2 * PI;
float angle = (heading * 180 / M_PI);
//Serial.println(angle);
u8g2.firstPage();
do {
//calculate X, Y position of West
xw = ((radius + offsetRadius) * -cos((angle - 0) * 3.14 / 180)) + centreX + offsetCentreX;
yw = ((radius + offsetRadius) * sin((angle - 0) * 3.14 / 180)) + centreY + offsetCentreY;
//calculate X, Y position of North
xn = ((radius + offsetRadius) * -cos((angle - 90) * 3.14 / 180)) + centreX + offsetCentreX;
yn = ((radius + offsetRadius) * sin((angle - 90) * 3.14 / 180)) + centreY + offsetCentreY;
//calculate X, Y position of East
xe = ((radius + offsetRadius) * -cos((angle - 180) * 3.14 / 180)) + centreX + offsetCentreX;
ye = ((radius + offsetRadius) * sin((angle - 180) * 3.14 / 180)) + centreY + offsetCentreY;
//calculate X, Y position of South
xs = ((radius + offsetRadius) * -cos((angle - 270) * 3.14 / 180)) + centreX + offsetCentreX;
ys = ((radius + offsetRadius) * sin((angle - 270) * 3.14 / 180)) + centreY + offsetCentreY;
//Draw Circle
u8g2.drawCircle(centreX, centreY, radius, U8G2_DRAW_ALL);
//Show Direction Icon, by tutorial here https://bit.ly/3VAOlrm
u8g2.drawXBMP(28, 16, direction_width, direction_height, direction_bits);
//Show angle value on OLED
u8g2.setFont(u8g2_font_victoriabold8_8r);
u8g2.setCursor(73, 32);
u8g2.print((String(angle, 0) + " Deg"));
//Draw points of the compass every angle
u8g2.setFontPosCenter();
u8g2.setDrawColor(1);
u8g2.setBitmapMode(0);
u8g2.drawStr(xn, yn, "N");
u8g2.drawStr(xe, ye, "E");
u8g2.drawStr(xs, ys, "S");
u8g2.drawStr(xw, yw, "W");
} while (u8g2.nextPage());
delay(150);
}
Result
If the code upload is successful, you can see the results on your OLED screen as shown in the following video:
I hope this Arduino Compass Using MPU9250 Magnetometer & OLED article will be useful for you.
May be you like: