[EN] Digital Compass Sensor

This article describes the MicroPython GY-271 digital compass sensor for use with the ESP8266 or ESP32 (we have experimented with STM32F411CEU6 with Raspberry Pi 3B+ and 4B and found that it can be used as well) to set the operation and read the X,Y and Z axis values from the sensor, then calculate it as the degree of north.

(Figure. 1 GY-271 usage)

GY-271

The GY-271 (Figure 1) is a 3-Axis Digital Compass sensor using IC number QMC5883L which is a magnetic and compass sensor. It operates up to 160Hz, connects via the I2C bus, has a 16-bit analog-to-digital conversion, uses 2.16 – 3.6VDC, consumes 75/100uA,150/250uA, 250/450uA and 450/850uA at 10Hz, 50Hz, 100Hz and 200Hz and can work at -40 to 80°C.

(Figure. 2 GY-271 digital compass module)

QMC5883L‘s registers

QMC5883L has registers for writing and reading the values as follows.

AddressDescription
0x00lower byte of axis value X (LSB of X-Axis)
0x01upper byte of axis value X (MSB of X-Axis)
0x02lower byte of axis value Y (LSB of Y-Axis)
0x03upper byte of axis value Y (MSB of Y-Axis)
0x04lower byte of axis value Z (LSB of Z-Axis)
0x05upper byte of axis value Z (MSB of Z-Axis)
0x09control register 1
0x0Acontrol register 2
0x0BSet/Reset Period Register

Note
The QMC5883L documentation suggests setting the value of register position 0x0B to 0x01.

Control register 1

Control register 1 consists of 4 registers as follows:

Bit No.Register
0..1Mode
2..3ODR (Output Data Rate)
4..5RNG (Fill scale)
6..7OSR (Over Sample Ratio)

where the values are as follows

SettingBase 2
Mode_Standby 0b00000000
Mode_Continuous0b00000001
ODR_10Hz0b00000000
ODR_50Hz0b00000100
ODR_100Hz0b00001000
ODR_200Hz0b00001100
RNG_2G0b00000000
RNG_8G0b00010000
OSR_5120b00000000
OSR_2560b01000000
OSR_1280b10000000
OSR_640b11000000

Control register 2

Contains the following registers.

Bit No.Register
0INT_ENB
6Rol_PNT
7Soft_RS

Connection

Connect via I2C bus as shown in Figure 3 and the sensor module has position value 0x0D by connecting to ESP8266 as follows.

GY-271ESP8266
Vcc3V3
GNDGND
SDAD2
SCLD1
(Figure. 3 GY-271’s pin)

Read the value

The reading is performed by reading from position 0x00 in order of low and high bytes of the X, Y, and Z axes:

buffer = i2c.readfrom_mem(QMC5883L_ADDR,RegXLo,6)
xLo = buffer[0]
xHi = buffer[1] <<8
yLo = buffer[2]
yHi = buffer[3] <<8
zLo = buffer[4]
zHi = buffer[5] <<8
x = xLo+xHi
y = yLo+yHi
z = zLo+zHi

Conversion

The conversion from the values of the X,Y and Z axes to the north direction is as follows.

heading = math.atan2(y, x)
heading = heading + declinationAngle
#Due to declination check for >360 degree
if(heading > 2*pi):
    heading = heading - 2*pi
#check for sign
if(heading < 0):
    heading = heading + 2*pi

#convert into angle
headingAngle = (heading * 180/pi)

Example Code

Converting the C++ code of e-Gizmo Mechatronix Central, C++ for Arduino, into Python for MicroPython is as follows:

###############################################################################
# GY-271/QMC5883L
# (C) 2021, JarutEx
# Ref: https://github.com/e-Gizmo/QMC5883L-GY-271-Compass-module
###############################################################################
import machine as mc
import sys
import math
from time import sleep  #import sleep

pinSDA = mc.Pin(4)
pinSCL = mc.Pin(5)
QMC5883L_ADDR = 0x0D
i2c = mc.I2C(freq=2000000, scl=pinSCL, sda=pinSDA)
devices = i2c.scan()
if not (QMC5883L_ADDR in devices):
    print("Not found GY-271 (QMC5883L)!")
    sys.exit(1)

############## Register Location
RegCTRL1 = 0x09 # Control Register--> MSB(OSR:2,RNG:2,ODR:2,MODE:2)LSB
RegCTRL2 = 0x0A # Control Register2--> MSB(Soft_RS:1,Rol_PNT:1,none:5,INT_ENB:1)LSB
RegFBR   = 0x0B # SET/RESET Period Register--> MSB(FBR:8)LSB
RegXLo   = 0x00
RegXHi   = 0x01
RegYLo   = 0x02
RegYHi   = 0x03
RegZLo   = 0x04
RegZHi   = 0x05

############## Cpntrol Register Value 
Mode_Standby    = 0b00000000
Mode_Continuous = 0b00000001
ODR_10Hz        = 0b00000000
ODR_50Hz        = 0b00000100
ODR_100Hz       = 0b00001000
ODR_200Hz       = 0b00001100
RNG_2G          = 0b00000000
RNG_8G          = 0b00010000
OSR_512         = 0b00000000
OSR_256         = 0b01000000
OSR_128         = 0b10000000
OSR_64          = 0b11000000

declinationAngle = 0.0404  
pi          = 3.14159265359

########### Init
ctrl1 = bytearray([Mode_Continuous|ODR_200Hz|RNG_8G|OSR_512])
i2c.writeto_mem(QMC5883L_ADDR,RegCTRL1, ctrl1)
i2c.writeto_mem(QMC5883L_ADDR,RegFBR, b'\x01')

########### Read
buffer = i2c.readfrom_mem(QMC5883L_ADDR,RegXLo,6)
xLo = buffer[0]
xHi = buffer[1] <<8
yLo = buffer[2]
yHi = buffer[3] <<8
zLo = buffer[4]
zHi = buffer[5] <<8
x = xLo+xHi
y = yLo+yHi
z = zLo+zHi

########### Convert
heading = math.atan2(y, x)
heading = heading + declinationAngle
#Due to declination check for >360 degree
if(heading > 2*pi):
    heading = heading - 2*pi
#check for sign
if(heading < 0):
    heading = heading + 2*pi

#convert into angle
headingAngle = (heading * 180/pi)

########### Show result
print("3-axis : x={}/{},{} y={}/{},{} z={}/{},{}".format(x,xHi,xLo,y,yHi,yLo,z,zHi,zLo))
print ("Heading Angle = {}°".format(headingAngle))

The result of running the sample code is as follows.

Conclusion

From this article, we found that Python programming has the advantage of being able to reuse code that is easier to reuse on boards that have a Python interpreter installed than it is to code in C/C++. We also found that the GY-271 module has two types of ICs, QMC5883L and HMC5883. Both of these ICs have different designs, the conversion methods are different, so the you must check the IC version on the module to make sure the code works properly. Finally, have fun with programming.

If you want to discuss or talk with us, feel free to leave comments below!

Reference

  1. QMC5883L-GY-271-Compass-module

(C) 2020-2021, By Jarut Busarathid and Danai Jedsadathitikul
Updated 2021-10-06