Hardware | Digitial Compass - HMC5883L 사용기 - 1

|

1. 시작하기


지구의 자력을 측정할 수 있는 digital compass 센서가 있다는 소문을 들었습니다.


센서 내부에 코일을 감은 자석에 +/- 극을 지속적으로 변화시켜,

자기장의 변화에 따른 저항을 측정하는 방식으로 방위를 파악한다고 합니다.



* Magnetoresistance

https://en.wikipedia.org/wiki/Magnetoresistance



지표를 기준으로 기울기까지 파악할 수 있습니다.

X/Y 로만 측정하면 방위를 측정할 수 있겠죠.



지구는 위의 그림처럼 자기작을 가지고 있습니다.

위의 그림의 출처는 다음에 있는 블로그를 참조하였습니다.


* Arduino: simple compass with HMC5883L + Library

http://bluelemonlabs.blogspot.kr/2013/08/arduino-simple-compass-with-hmc5883l.html


정확히는 "Anisotropic Magnetoresistance" 현상을 이용한 것이라고 합니다.

더 자세히 알고싶으시면 구글링~.





2. 센서 구입


이런 현상을 이용하여 digital compass 를 쉽게 만들 수 있는 센서를 구입하지 않는 이유를 찾을 수 없었습니다.


* GY-273 3V-5V HMC5883L Triple Axis Compass Magnetometer Sensor Module Three Axis Magnetic Field Module For Arduino

https://ko.aliexpress.com/item/GY-273-3V-5V-HMC5883L-Triple-Axis-Compass-Magnetometer-Sensor-Module-Three-Axis-Magnetic-Field-Module/32826264150.html



센서류 치고는 좀 비싸지만 그렇게 많이는 아니여서 망설임 없이 구입합니다.

(이게 나중에 화근이 됩니다)





3. 도착


무난하게 도착하였습니다.



뽁뽁이 봉지에 잘 넣어서 왔으며, 전자파 방지 비닐에 싸아서 왔습니다.



Header pin 은 납땜되지 않은 채로 왔습니다.

아무래도 운송시 휠수도 있고, 튀어나온 곳으로 힘을 받을 수 있으니까요.



중국 제조사의 이름은 GY-273 인듯 합니다.





4. Layout


Pinout 정보는 다음과 같습니다.


  HMC5883L  | Arduino Nano
---------------------------
    VCC     |     3.3V
    GND     |     GND
    SCL     |     A5
    SDA     |     A4
---------------------------


회로 배선은 다음과 같습니다.



실제 배선은 다음과 같습니다.






5. WTF


순서상으론 이 부분에서 소스를 보여줘야 하나, 다음과 같이 정상적으로 구동이 되지 않는 화면을 먼저 보여드립니다.



Arduino 자체가 망가졌나 해서, 가지고 있던 다른 Arduino Nano 클론 및 Arduino Micro 도 사용해 봤습니다.



가능한 모든 방법을 동원해 봤습니다.

그래도 안되더군요.


정말 가끔 있는 breakout 보드가 망가졌나 했습니다.


한가지 의심스러운 것은 HMC5883L breakout 보드의 I2C address 가, 보통 sample sketch 에서는 "0x1E" 으로 표현되어 있는데,

제가 구입한 보드는 "0x0D" 라고 표시되는 것이였습니다.


뭐, 중국 카피품일 경우, 이 address 가 다르게 나오는 경우가 있어, sketch 에서 변경만 하고 실행했더랬습니다.



위에서 3c 는 OLED 를 같이 접속시켜 확인한 결과 입니다.

문제 없이 "0d" 부분에 연결이 되어 있다고 뜹니다.


인터넷에서 가능한 다른 sample sketch 를 구해서 해봤으나 실패했습니다.





6. i2c scanner


혹여 i2c scanner 의 문제인가 하고, 다른 i2c scanner 도 구해서 해봤으나, 결과는 같았습니다.


// --------------------------------------
// i2c_scanner
//
// Version 1
//    This program (or code that looks like it)
//    can be found in many places.
//    For example on the Arduino.cc forum.
//    The original author is not known.
// Version 2, Juni 2012, Using Arduino 1.0.1
//     Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26  2013
//    V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
//    by Arduino.cc user Krodal.
//    Changes by louarnold removed.
//    Scanning addresses changed from 0...127 to 1...119,
//    according to the i2c scanner by Nick Gammon
//    http://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
//    As version 4, but address scans now to 127.
//    A sensor seems to use address 120.
// 
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//

#include "Wire.h"

void setup() {
	Wire.begin();
	
	Serial.begin(9600);
	Serial.println("\nI2C Scanner");
}

void loop() {
	byte error, address;
	int nDevices;
	
	Serial.println("Scanning...");
	
	nDevices = 0;
	
	for(address = 1; address < 127; address++ ) {
		// The i2c_scanner uses the return value of
		// the Write.endTransmisstion to see if
		// a device did acknowledge to the address.
		
		Wire.beginTransmission(address);
		error = Wire.endTransmission();
		
		if (error == 0) {
			Serial.print("I2C device found at address 0x");
			if (address<16) Serial.print("0");
			Serial.print(address,HEX);
			Serial.println("  !");
			
			nDevices++;
		} else if (error==4) {
			Serial.print("Unknow error at address 0x");
			if (address<16) Serial.print("0");
			Serial.println(address,HEX);
		}
	}
	
	if (nDevices == 0) Serial.println("No I2C devices found\n");
	else Serial.println("done\n");
	
	delay(5000);           // wait 5 seconds for next scan
}


결과는 다음과 같이 동일하게 "0x0D" 라고 명확하게 표시됩니다.



참고로 앞으론 이 i2c scanner 도 사용해봐야겠네요.
연결된 I2C 부품을 깔끔하게 HEX address 를 표현해 줍니다.





7.  QMC5883L


진심으로 보드가 고장난 것이라 생각하고 재구매를 생각하고 있었습니다.


그러다가 다음과 같은 블로그 글을 읽게 됩니다.

결론은 HMC5883L 이 모두 동일한게 아니라 중국발 QMC5883L 이라는 제품이 있고, 전혀 동일하지 않다는 내용입니다.

허거걱 !!!


* Problem with HMC5883L magnetometer

https://www.reddit.com/r/AskElectronics/comments/5xo3md/problem_with_hmc5883l_magnetometer/



* PROBLEMS WITH GY-271 MAGNETOMETER (HMC5883L != QMC5883L)

http://www.esp8266.com/viewtopic.php?f=13&t=15445


Chip 자체가 다르고 identical 하지 않다고 하니, 한번 살펴 봅니다.



사진으로는 잘 모르겠으나, 일반적인 HMC5883L 과는 다른 마킹임에는 틀림 없어 보입니다.

구매 사이트에서 사진을 퍼와 봤습니다.



완전히 다르네요. "DA 5883 6014" 라고 되어 있습니다.

Sparkfun 에서의 HMC5883L 의 제품은 다음 그림과 같습니다. "L883 2105" 라고 되어 있습니다.


* Sparkfun

https://www.sparkfun.com/products/retired/10494



AliExpress 의 구매 사이트를 더 뒤져 보니 다음과 같은 문구를 발견했습니다.

"Note: It is Domestic Chip HMC5883, the program is not compatible ...."



아놔...


어떤 사이트에는 "Bad Sensor" 라고도 올려져 있습니다.



위의 사진을 보면 정말 main chip 만 다를 뿐, 완벽하게 동일한 구성품과 회로도 입니다.


* HMC5883L(Or QMC5883L) Electronic Compass

http://wiki.epalsite.com/index.php?title=HMC5883L(Or_QMC5883L)_Electronic_Compass


QMC5883L 의 Datasheet 는 다음과 같습니다.

QMC5883L-Datasheet-1.0.pdf





8. Sketch


cpp 및 h 파일은 다음 사이트에서 가져 왔습니다.

arduino/library/ 디렉토리에 "QMC5883L" 이라고 만들고, 그 안에 cpp 와 h 파일을 넣었습니다.


* HMC5883L Compass Module Comunicating but all zero's for x,y,z

https://forum.arduino.cc/index.php?topic=482179.0


Library 소스를 보면 address 가 이미 "0x0D" 라고 박혀 있네요.


QMC5883L 의 sample sketch 를 옮겨 봅니다.


/*
QMC5883L_Example.ino - Example sketch for integration with an QMC5883L triple axis magnetometer.
Copyright (C) 2017 Andy Barnard based on an original for the QMC5883L by Love Electronics (C) 2011

This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

See .

*/

// Reference the I2C Library
#include "Wire.h"
// Reference the QMC5883L Compass Library
#include "QMC5883L.h"

// configure the compass as reqiured
#define OSR 0b00               // over sampling rate set to 512. 0b01 is 256, 0b10 is 128 and 0b11 is 64
#define RNG 0b00               // Full Scale set to +/- 2 Gauss, 0b01 is +/- 8.
#define ODR 0b00               // output data rate set to 10Hz, 0b01 is 50Hz, 0b10 is 100Hz, 0b11 is 200Hz
#define MODE 0b01              // continuous measurement mode, 0b00 is standby
#define CR2 0b00000000          // control register 2: disable soft reset and pointer rollover, interrupt pin not enabled
#define RESETPERIOD 0b00000001  // datasheet recommends this be 1, not sure why!

// Store our compass as a variable.
QMC5883L compass;
// Record any errors that may occur in the compass.
int error = 0;

// Out setup routine, here we will configure the microcontroller and compass.
void setup() {
	// Initialize the serial port.
	Serial.begin(9600);
	
	Serial.println("Starting the I2C interface.");
	Wire.begin(); // Start the I2C interface.
	TWBR = 12;    //Set the I2C clock speed to 400kHz - only works with Arduino UNO
	
	Serial.println("Constructing new QMC5883L");
	compass = QMC5883L(); // Construct a new HMC5883 compass.
	
	// Check that a device responds at the compass address - don't continue if it doesn't - 
	do {
		delay(100);
		Wire.beginTransmission(QMC5883L_Address);
		error = Wire.endTransmission();
		if (error) Serial.println("Can't find compass - is it connected and powered up?");
	} while (error);
	
	// configure the control registers using static settings above
	// compass autoranges, but starts in the mode given
	compass.dataRegister.OSR_RNG_ODR_MODE = (OSR << 6) |(RNG << 4)  | (ODR <<2) |  MODE;
	compass.dataRegister.CR2_INT_ENABLE = CR2;
	compass.dataRegister.SET_RESET_PERIOD = RESETPERIOD;
	
	Serial.println("Configuring QMC5883L - OSR 512, range +/-2 Gauss, ODR 10, Continuous");
	error = compass.Configure(compass.dataRegister); // use static settings from above - can access register data directly if required..
	if (error != 0) // If there is an error, print it out, although no way to get error with this sensor....
	Serial.println(compass.GetErrorText(error));
}

// Our main program loop.
void loop() {
	// Retrive the raw values from the compass (not scaled).
	MagnetometerRaw raw = compass.ReadRawAxis(&compass.dataRegister);
	// Retrived the scaled values from the compass (scaled to the configured scale).
	MagnetometerScaled scaled = compass.ReadScaledAxis(&compass.dataRegister);
	
	// Values are accessed like so:
	int MilliGauss_OnThe_XAxis = scaled.XAxis;     // (or YAxis, or ZAxis)
	
	// Calculate heading when the magnetometer is level, then correct for signs of axis.
	// heading (degrees): 0 = +X, 90 = +Y, 180 = -X, 270 = -Y
	float heading = atan2(scaled.YAxis, scaled.XAxis);
	
	// Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.
	// Find yours here: http://www.magnetic-declination.com/
	// Example is: 2� 37' W, which is 2.617 Degrees, or (which we need) 0.0456752665 radians, I will use 0.0457
	// If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
	// float declinationAngle = 0.0457;
	
	float declinationAngle = 0;
	heading += declinationAngle;
	
	// 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;
	
	// Convert radians to degrees for readability.
	float headingDegrees = heading * 180/M_PI;
	
	// Output the data via the serial port.
	Output(raw, scaled, heading, headingDegrees);
	
	// Normally we would either:
	// 1. delay the application by 100ms to allow the loop to run at 10Hz (default bandwidth for the QMC5883L)
	// 2. poll the dataready flag in the dataRegister.OVL_DRDY register
	// 3. set the interrupt flat and set a hardware interrupt on the DRDY pin 
	// The first of these options is the easiest.
	delay(100);
}

// Output the data down the serial port.
void Output(MagnetometerRaw raw, MagnetometerScaled scaled, float heading, float headingDegrees)
{
   Serial.print("Raw (X,Y,Z): (");
   Serial.print(raw.XAxis);
   Serial.print(", ");   
   Serial.print(raw.YAxis);
   Serial.print(", ");   
   Serial.print(raw.ZAxis);
   
   Serial.print(")\tScaled (X,Y,Z): (");
   Serial.print(scaled.XAxis, 4);
   Serial.print(", ");   
   Serial.print(scaled.YAxis, 4);
   Serial.print(", ");   
   Serial.print(scaled.ZAxis, 4);
   Serial.println(")");

 
   Serial.print("Magnitude (0.25 to 0.6 on Earth surface): ");
   Serial.print(sqrt(scaled.XAxis * scaled.XAxis + scaled.YAxis * scaled.YAxis + scaled.ZAxis * scaled.ZAxis));
   Serial.print(" Heading: ");
   Serial.print(headingDegrees);
   Serial.print(" Bearing: ");
   Serial.print(bearingDegrees(headingDegrees));
   Serial.println(" (Degrees)");
}

  // Cacluate bearing from heading.
  // bearing 0 = Y pointing North, 90 = Y pointing E, 180 = Y pointing S, 270 = Y pointing W
  float bearingDegrees(float headingDegrees) {
     
     float bearing = 450 - headingDegrees;
     if (bearing >= 360)
     {
      bearing -= 360;
     }
     return bearing;
  }





9. 결과


QMC5883L 이라고 안 이상, 그 뒤는 문제 없이 구동까지 확인할 수 있었습니다.






FIN


여기까지 걸린 시간은 대략 이틀.

구매 사이트에 처음부터 QMC5883L 이라고 표기했으면 이렇게까지 고민하지 않았을 터인데.

역시 알아서 쓰라는 AliExpress 와 대륙의 기상이 느껴집니다.


인터넷을 보면 QMC5883L 에 대해서는 꽤나 여러가지 시도가 있습니다.

사실 접근 address 및 쓰고 읽는 부분만 다를 뿐, 구동 방식은 같다고 합니다.


* QMC5883L Electronic Compass

http://wiki.epalsite.com/index.php?title=QMC5883L_Electronic_Compass



Mecha 라는 ID 를 사용하시는 분이 만든 library 도 있습니다.


* Arduino lib for QMC5883

https://github.com/mechasolution/Mecha_QMC5883L


QMC5883L 임을 안 이상, 다음에는 좀더 철저하게 사용해 보도록 하겠습니다.

And