GY-273 QMC5883L clone HMC5883L magnetometer for Arduino, esp8266 and esp32

Spread the love

GY-273 QMC5883L HMC5883L magnetometer sensor Arduino esp8266 esp32
GY-273 QMC5883L HMC5883L magnetometer sensor Arduino esp8266 esp32

magnetometer is a device that measures magnetic field or magnetic dipole moment. Some magnetometers measure a magnetic field’s direction, strength, or relative change at a particular location.

For example, a compass is a simple magnetometer

Stanley compass
Stanley compass

With this principle, a device can understand if It’s changed its position or direction on the 3 axis x, y, and z.

I bought this sensor (and others of the same type) for my projects, and found that it is not that simple to use.

First, because many of that isn’t an HMC5883L but a QMC5883L, and the library and address aren’t the same.

Wiring

Here are some simple wiring examples.

esp32:

esp32 dev kit and gy 273 wiring
esp32 dev kit and gy 273 wiring

esp8266, WeMos D1 mini:

esp8266 WeMos D1 and gy 273 wiring
esp8266 WeMos D1 and gy 273 wiring

Arduino UNO:

Arduino UNO and gy 273 wiring
Arduino UNO and gy 273 wiring

Start prototyping

esp32 lolin32 and gy 273 QMC5883L HMC5883L on breadboard
esp32 lolin32 and gy 273 QMC5883L HMC5883L on breadboard

Check variant

The simple way to identify if It’s a QMC5883L variant is to use an i2c scanner. The variant had an address (0x0D) different from the HMC5883L original one.

/*
 * i2c scanner
 * Renzo Mischianti www.mischianti.org
 */
#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(115200);
  while (!Serial) {delay(100);};
  Serial.println();
  Serial.println("I2C Scanner");
}

void loop() {
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for(address = 1; address < 127; 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.println(address,HEX);
      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);
}

if you obtain this output, you have a QMC5883L variant

Scanning...
I2C device found at address 0x0D
done

Scanning...
I2C device found at address 0x0D
done

Library

I use QMC5883LCompass library that you can find on GitHub, or you can directly download from Arduino Libray manager.

GY-273 QMC5883L HMC5883L magnetometer Arduino library manager
GY-273 QMC5883L HMC5883L magnetometer Arduino library manager

Code

Here is a small code that I wrote, taking parts from some examples and adding the print of the coordinates calculated by the azimuth.

Conversione tra azimut e bearing
Conversione tra azimut e bearing

So in the code, first, It asks to calibrate the device and after calibration, start to output the values.

/*
 * Simple sketch that use
 * QMC5883LCompass.h from https://github.com/mprograms/QMC5883LCompass
 * library.
 * First the sketch ask to calibrate device so move It, then
 * start to output the data
 *
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org
 *
 */

#include <QMC5883LCompass.h>

QMC5883LCompass compass;

int calibrationData[3][2];
bool changed = false;
bool done = false;
int t = 0;
int c = 0;

bool calibrated = false;

void setup() {
  Serial.begin(9600);
  // Initialize device with i2c 0x0D address
  compass.init();
}

void loop() {
	if (!calibrated){
		// If not calibrated
	  int x, y, z;

	  // Read compass values
	  compass.read();

	  // Return XYZ readings
	  x = compass.getX();
	  y = compass.getY();
	  z = compass.getZ();

	  changed = false;

	  if(x < calibrationData[0][0]) {
	    calibrationData[0][0] = x;
	    changed = true;
	  }
	  if(x > calibrationData[0][1]) {
	    calibrationData[0][1] = x;
	    changed = true;
	  }

	  if(y < calibrationData[1][0]) {
	    calibrationData[1][0] = y;
	    changed = true;
	  }
	  if(y > calibrationData[1][1]) {
	    calibrationData[1][1] = y;
	    changed = true;
	  }

	  if(z < calibrationData[2][0]) {
	    calibrationData[2][0] = z;
	    changed = true;
	  }
	  if(z > calibrationData[2][1]) {
	    calibrationData[2][1] = z;
	    changed = true;
	  }

	  if (changed && !done) {
	    Serial.println("CALIBRATING... Keep moving your sensor around.");
	    c = millis();
	  }
	    t = millis();


	  if ( (t - c > 5000) && !done) {
	    done = true;
	    Serial.println("DONE.");
	    Serial.println();

	    Serial.print("compass.setCalibration(");
	    Serial.print(calibrationData[0][0]);
	    Serial.print(", ");
	    Serial.print(calibrationData[0][1]);
	    Serial.print(", ");
	    Serial.print(calibrationData[1][0]);
	    Serial.print(", ");
	    Serial.print(calibrationData[1][1]);
	    Serial.print(", ");
	    Serial.print(calibrationData[2][0]);
	    Serial.print(", ");
	    Serial.print(calibrationData[2][1]);
	    Serial.println(");");

	    compass.setCalibration(	calibrationData[0][0], calibrationData[0][1], calibrationData[1][0],
	    						calibrationData[1][1], calibrationData[2][0], calibrationData[2][1]);
	    calibrated = true;
	    }
	}else{
		// If calibrating
		  int x, y, z;

		  // Read compass values
		  compass.read();

		  // Return XYZ readings
		  x = compass.getX();
		  y = compass.getY();
		  z = compass.getZ();

		  int azimut = compass.getAzimuth();

		  float bearing = compass.getBearing(azimut);

		  Serial.println();

		  // Write direction
		  if((azimut < 22.5)  || (azimut > 337.5 ))  Serial.print("North     ");
		  if((azimut > 22.5)  && (azimut < 67.5 ))   Serial.print("North-East");
		  if((azimut > 67.5)  && (azimut < 112.5 ))  Serial.print("East      ");
		  if((azimut > 112.5) && (azimut < 157.5 ))  Serial.print("South-East");
		  if((azimut > 157.5) && (azimut < 202.5 ))  Serial.print("South     ");
		  if((azimut > 202.5) && (azimut < 247.5 ))  Serial.print("SOuth-West");
		  if((azimut > 247.5) && (azimut < 292.5 ))  Serial.print("West      ");
		  if((azimut > 292.5) && (azimut < 337.5 ))  Serial.print("North-West");

		  Serial.print(" Azimuth: ");Serial.print(azimut);
		  Serial.print(" Bearing: ");Serial.print(bearing);

		  Serial.print(" - X: ");
		  Serial.print(x);
		  Serial.print(" Y: ");
		  Serial.print(y);
		  Serial.print(" Z: ");
		  Serial.print(z);

		  delay(250);
	}
}

Here is the serial output result

CALIBRATING... Keep moving your sensor around.
[...]
CALIBRATING... Keep moving your sensor around. 
DONE. Copy the line below and paste it into your projects sketch.); 
 
compass.setCalibration(0, 1372, -1106, 0, 0, 966); 
 
North-West Azimuth: 321 Bearing: 14.00 - X: 1130 Y: -948 Z: 492 
North-West Azimuth: 320 Bearing: 14.00 - X: 1035 Y: -881 Z: 432 
North-West Azimuth: 320 Bearing: 14.00 - X: 941 Y: -814 Z: 374 
North-West Azimuth: 319 Bearing: 14.00 - X: 845 Y: -747 Z: 316 
North-West Azimuth: 318 Bearing: 14.00 - X: 752 Y: -681 Z: 257 
North-West Azimuth: 317 Bearing: 14.00 - X: 656 Y: -614 Z: 197 
North-West Azimuth: 316 Bearing: 14.00 - X: 561 Y: -550 Z: 138 
North-West Azimuth: 314 Bearing: 13.00 - X: 466 Y: -483 Z: 80 
North-West Azimuth: 312 Bearing: 13.00 - X: 372 Y: -417 Z: 22 
North-West Azimuth: 312 Bearing: 13.00 - X: 372 Y: -416 Z: 21 
North-West Azimuth: 312 Bearing: 13.00 - X: 373 Y: -416 Z: 22 
North-West Azimuth: 314 Bearing: 13.00 - X: 383 Y: -399 Z: 24 
North-West Azimuth: 319 Bearing: 14.00 - X: 383 Y: -335 Z: 60 
North-West Azimuth: 326 Bearing: 14.00 - X: 369 Y: -257 Z: 107 
North-West Azimuth: 333 Bearing: 14.00 - X: 348 Y: -179 Z: 165 
North      Azimuth: 346 Bearing: 15.00 - X: 328 Y: -83 Z: 227 
North      Azimuth: 4 Bearing: 0.00 - X: 307 Y: 25 Z: 292 
North-East Azimuth: 26 Bearing: 1.00 - X: 289 Y: 142 Z: 356 
North-East Azimuth: 43 Bearing: 1.00 - X: 272 Y: 260 Z: 420 
North-East Azimuth: 55 Bearing: 2.00 - X: 253 Y: 363 Z: 484 
North-East Azimuth: 61 Bearing: 2.00 - X: 226 Y: 418 Z: 513 
North-East Azimuth: 63 Bearing: 2.00 - X: 223 Y: 447 Z: 515 
North-East Azimuth: 63 Bearing: 2.00 - X: 230 Y: 458 Z: 515 
North-East Azimuth: 63 Bearing: 2.00 - X: 230 Y: 458 Z: 514 
North-East Azimuth: 59 Bearing: 2.00 - X: 247 Y: 425 Z: 496 
North-East Azimuth: 53 Bearing: 2.00 - X: 246 Y: 328 Z: 444 
North-East Azimuth: 40 Bearing: 1.00 - X: 246 Y: 208 Z: 391 
North      Azimuth: 18 Bearing: 0.00 - X: 245 Y: 83 Z: 326 
North      Azimuth: 354 Bearing: 15.00 - X: 259 Y: -31 Z: 264 
North-West Azimuth: 332 Bearing: 14.00 - X: 277 Y: -148 Z: 202 
North-West Azimuth: 320 Bearing: 14.00 - X: 294 Y: -252 Z: 144 
North-West Azimuth: 313 Bearing: 13.00 - X: 312 Y: -338 Z: 99

Thanks


Spread the love

6 Responses

  1. severiano quevedo artigues says:

    Good morning,
    I would be interested if someone could help me explain how to control the rotation speed of a 24V drive motor and keep its speed constant.
    That is, a gyroscope or magnetic sensor would control the speed and it would always be constant.
    I can provide my email address if anyone wants to help me.
    With ESP32.
    If anyone is interested we could discuss more about the system I am setting up. I don’t mind if there is a cost, I am open to a dialogue to specify details.
    Thank you very much.

    Buenos dias,
    Estaría interesado si alguien pudiera ayudarme en explicarme cómo controlar la velocidad e giro de un motor de accionamiento a 24V y que su velocidad sea constante.
    Es decir, que un giroscopio o sensor magnético controlara la velocidad y que siempre fuera constante.
    Paso mi correo electrónico si alguien quisiera ayudarme.
    Con ESP32.
    Si hay alguien interesado podríamos hablar más del sistema que estoy montando. No me importa si tiene coste, admito diálogo para concretar.
    Muchas gracias.

    • Hi Severiano,
      I think you need a function like this that calculate speed and then control the motor to set a constant speed.

      float calculateSpeed(int16_t x, int16_t y, int16_t z){
        // calculate the magnitude of the magnetic field
        float magnitude = sqrt((x * x) + (y * y) + (z * z));
      
        // map the magnetic field strength to a motor speed
        // assuming a maximum field strength of 2000 and maximum motor speed of 100 (this will depend on your specific hardware)
        float maxFieldStrength = 2000.0;
        float maxMotorSpeed = 100.0;
      
        float speed = (magnitude / maxFieldStrength) * maxMotorSpeed;
      
        // constrain the speed to the maximum
        if (speed > maxMotorSpeed){
          speed = maxMotorSpeed;
        }
      
        return speed;
      }
      

      Bye Renzo

  2. Tommie Berry says:

    Thanks for the code, it is the first compass code that has worked for me !!
    I just have an operational question, how should I move the GY-271 during the calibration ?
    Does it need to start oriented in a certain way ?

  3. Shahansha Mahabub says:

    I have a i2c device that starts with 0x0E address, what changes should I make in my code to make it work with esp32?

Leave a Reply

Your email address will not be published. Required fields are marked *