Magnetometer HMC5883L Interfacing with NodeMCU

Magnetometer HMC5883L Interfacing with NodeMCU

Introduction

Magnetometer HMC5883L Module
Magnetometer HMC5883L Module
HMC5883L is a 3-axis magnetometer which is used for measuring the direction and magnitude of the Earth’s magnetic field. It is used for low cost compassing and magnetometry.
It measures the Earth’s magnetic field value along the X, Y and Z axes from milli-gauss to 8 gauss.
It can be used to find the direction of heading of the device.
It uses I2C protocol for communication with the microcontroller.
For more information about Magnetometer HMC5883L and how to use it, refer the topic HMC5883L Magnetometer Module in the sensors and modules section.
A NodeMCU can communicate with this module using I2C communication protocol. To know more about I2C functions in NodeMCU refer NodeMCU I2C with ESPlorer IDE or NodeMCU I2C with Arduino IDE

Interfacing Diagram

Magnetometer HMC5883L interface with NodeMCU
Interfacing HMC5883L Module with NodeMCU

Example

Reading x, y, z axis from the HMC5883L magnetometer module, calculate heading angle from these three axes and displaying heading angle on Serial Monitor.

First, do the connections as shown in above interfacing diagram.
Now let’s write program NodeMCU for reading values from HMC5883L magnetometer.
We can write codes for NodeMCU DevKit in either Lua Script or C/C++ language. We are using ESPlorer IDE for writing code in Lua scripts and Arduino IDE for writing code in C/C++. To know more refer Getting started with NodeMCU using ESPlorer IDE (which uses Lua scripting for NodeMCU) and Getting started with NodeMCU using Arduino IDE (which uses C/C++ language based Arduino sketches for NodeMCU).

NodeMCU functions for HMC5883L

Below are functions that are used for HMC5883L module in NodeMCU firmware. We need to add this module while building NodeMCU firmware to avail its function for use.

hmc5883l.init()
This function Initializes the HMC5883L module and sets the pin configuration.
Note: as per NodeMCU doc says that “hmc5883l.init() function is deprecated and will be removed in upcoming releases. Use hmc5883l.setup() instead”. But while testing hmc5883l.init() is working and hmc5883l.setup() not. So be sure about which function for initialize.
Syntax: hmc5883l.init(sda, scl)
Parameters:
sda: serial data pin of i2c interface.
scl: serial clock pin of i2c interface.
Returns: Nil

hmc5883l.setup()
This function initializes the module.
Syntax: hmc5883l.setup()
Parameters: None
Returns: nil
Example:
local sda, scl = 1, 2
i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once
hmc5883l.setup()

hmc5883l.read()
This function samples the sensor and returns X, Y and Z axis data.
Syntax: hmc5883l.read()
Returns: x,y,z measurements (integers) temperature multiplied with 10 (integer)
Example:
local sda, scl = 1, 2
hmc5883l.init(sda, scl)
local x, z ,y = hmc5883l.read()
print(string.format("x = %d, y = %d, z = %d", x, y, z))

let’s write lua script to read x, y, z from HMC5883l and calculate heading angle from it

Lua Script for NodeMCU

id  = 0 -- always 0
scl = 5 -- set pin 6 as scl
sda = 6 -- set pin 7 as sda


--Define declination of location from where measurement going to be done.
--e.g. here we have added declination from location Pune city, India.
--we can get it from http://www.magnetic-declination.com 
pi = 3.14159265358979323846
Declination = -0.00669


function arcsin(value)
    local val = value
    local sum = value 
    if(value == nil) then
        return 0
    end
-- as per equation it needs infinite iterations to reach upto 100% accuracy
-- but due to technical limitations we are using
-- only 10000 iterations to acquire reliable accuracy
    for i = 1, 10000, 2 do
        val = (val*(value*value)*(i*i)) / ((i+1)*(i+2))
        sum = sum + val;
    end
    return sum
end

function arctan(value)
    if(value == nil) then
        return 0
    end
    local _value = value/math.sqrt((value*value)+1)
    return arcsin(_value)
end

function atan2(y, x)
    if(x == nil or y == nil) then
        return 0
    end

    if(x > 0) then
        return arctan(y/x)
    end
    if(x < 0 and 0 <= y) then
        return arctan(y/x) + pi
    end
    if(x < 0 and y < 0) then
        return arctan(y/x) - pi
    end
    if(x == 0 and y > 0) then
        return pi/2
    end
    if(x == 0 and y < 0) then
        return -pi/2
    end
    if(x == 0 and y == 0) then
        return 0
    end
    return 0
end

hmc5883l.init(sda, scl)       --initialize hmc5883l
while true do   --read and print accelero, gyro and temperature value
    local x,z,y = hmc5883l.read()
    Heading = atan2(y, x) + Declination

    if (Heading>2*pi) then    --Due to declination check for >360 degree 
        Heading = Heading - 2*pi
    end
    if (Heading<0) then       --Check for sign
        Heading = Heading + 2*pi
    end

    Heading = Heading*180/pi  --convert radian to angle
    print(string.format("Heading angle : %d", Heading))
    tmr.delay(10000)   -- 10ms timer delay
end

ESPlorer Serial Output Window

Output window of ESPlorer IDE serial window for above lua script is shown below
ESPlorer serial window

Now let’s write same example in Arduino ide for NodeMCU

Arduino Sketch for NodeMCU

#include <Wire.h>

/* Define declination of location from where measurement going to be done. 
e.g. here we have added declination from location Pune city, India. 
we can get it from http://www.magnetic-declination.com */
#define Declination       -0.00669
#define hmc5883l_address  0x1E


void setup() {
  Serial.begin(9600); /* begin serial for debug */
  Wire.begin(D6, D5); /* join i2c bus with SDA=D6 and SCL=D5 of NodeMCU */
  hmc5883l_init();
}

void loop() {
  Serial.print("Heading Angle : ");
  Serial.println(hmc5883l_GetHeading());
  delay(150);
}

void hmc5883l_init(){   /* Magneto initialize function */
  Wire.beginTransmission(hmc5883l_address);
  Wire.write(0x00);
  Wire.write(0x70); //8 samples per measurement, 15Hz data output rate, Normal measurement 
  Wire.write(0xA0); //
  Wire.write(0x00); //Continuous measurement mode
  Wire.endTransmission();
  delay(500);
}

int hmc5883l_GetHeading(){
  int16_t x, y, z;
  double Heading;
  Wire.beginTransmission(hmc5883l_address);
  Wire.write(0x03);
  Wire.endTransmission();
  /* Read 16 bit x,y,z value (2's complement form) */
  Wire.requestFrom(hmc5883l_address, 6);
  x = (((int16_t)Wire.read()<<8) | (int16_t)Wire.read());
  z = (((int16_t)Wire.read()<<8) | (int16_t)Wire.read());
  y = (((int16_t)Wire.read()<<8) | (int16_t)Wire.read());

  Heading = atan2((double)y, (double)x) + Declination;
  if (Heading>2*PI) /* Due to declination check for >360 degree */
   Heading = Heading - 2*PI;
  if (Heading<0)    /* Check for sign */
   Heading = Heading + 2*PI;
  return (Heading* 180 / PI);/* Convert into angle and return */
}

/* Uncomment below function for reading status register */
//uint8_t readStatus(){
//  Wire.beginTransmission(hmc5883l_address);
//  Wire.write(0x09);
//  Wire.endTransmission();
//  Wire.requestFrom(hmc5883l_address, 1);
//  return (uint8_t) Wire.read();
//}

Arduino Serial Output Window

Output window of Arduino IDE serial window for above Arduino sketch is shown below
Arduino serial output window

Note that heading also gets affected by device tilt and nearby magnetic devices effect. There are compensating methods provided in the attached document.

Supporting Files
Source Code
Attached File

No comments:

Post a Comment