HMC5883L compass on GY801 module keeps reading one value

Hi,
I am testing the modules of GY801 and they all work except for the compass. I am able to communicate to it over the i2c but it keeps returning an identical value no matter which direction I point it in. I am using the libraries and demo program from this bildr tutorial. I have also tried other demo code with the same problem but everyone seems to be using the bildr example.

This is the output I am getting:
Constructing new HMC5883L
Setting scale to +/- 1.3 Ga
Setting measurement mode to continous.
Raw: 739 0 512 Scaled: 679.88 0.00 471.04 Heading: 0.05 Radians 2.62 Degrees
Raw: 513 3072 27136 Scaled: 471.96 2826.24 24965.12 Heading: 1.45 Radians 83.14 Degrees
Raw: 513 3084 27136 Scaled: 471.96 2837.28 24965.12 Heading: 1.45 Radians 83.17 Degrees
Raw: 513 3084 27136 Scaled: 471.96 2837.28 24965.12 Heading: 1.45 Radians 83.17 Degrees
Raw: 513 3084 27136 Scaled: 471.96 2837.28 24965.12 Heading: 1.45 Radians 83.17 Degrees
Raw: 513 3084 27136 Scaled: 471.96 2837.28 24965.12 Heading: 1.45 Radians 83.17 Degrees

One suggested solution is connecting a set of 4.7k pull ups on the SDA and SCL lines but I am able to communicate to the other modules and the GY801 has 4.7ks built in, so this is not the problem

Does anyone know what could be causing this?

The HMC5883L is not working properly.

Perhaps you need to send a reset command and at very least, you should run a self-test. The details are in the HMC5883L data sheet.

There are other examples of code to exercise that chip, for example for the Sparkfun breakout.

Thank you for the recommendation. I tried the sparkfun script that doesn't use the HMC5883L library and the sensor works fine!

I went back through the library code it seems to be a problem in the read function. The following trimmed example returns a fixed value

void setup()
{
  // Initialize the serial port.
  Serial.begin(9600);
  Wire.begin(); // Start the I2C interface.
  compass = HMC5883L(); // Construct a new HMC5883 compass.
  Serial.println("Setting measurement mode to continous.");
  compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
}

// Our main program loop.
void loop()
{
  delay(250);
  uint8_t* buffer = compass.Read(0x03, 6);
  Serial.println(buffer[1]);
}

Whereas if I use the read in the sparkfun example it works fine. It is strange that there would be an error in such a popular library, maybe something changed since it was written.

  uint8_t* buffer = compass.Read(0x03, 6);
  Serial.println(buffer[1]);

It is hard to see how that code is supposed to work. Where is "buffer" supposed to point?

The compass.Read function will pull 6 bytes from the 0x03 register if I understand correctly. each value is made up of 2 bytes, the MSB and the LSB, all of these values are reading either 0 or 255 when I look at them individually.

For that to work, "buffer" has to be allocated space, somewhere in valid read/write memory. Obviously, that wasn't done.

The code you posted is not even similar to the bildr example.

Hi,
First of all I would like to thank you for helping me work through this example. I appreciate your help in getting my to understand why the sparkfun code works but the library doesn't.

I wanted to simplify the bildr code to see where the error is introduced as superficially they do exactly the same thing, i.e., set the mode register to continuous mode, select register 3 and read the 6 bytes of values.

I first tested the bildr select continuous mode function by combining the setup from the bildr code with the loop from the sparkfun code. This worked perfectly so there is no problem with the library constructor or the setMeasurementMode function

#include <Wire.h> //I2C Arduino Library
#include <HMC5883L.h>
#define address 0x1E //0011110b, I2C 7bit address of HMC5883
HMC5883L compass;

void setup()
{
  // Initialize the serial port.
  Serial.begin(9600);
  Wire.begin(); // Start the I2C interface.
  compass = HMC5883L(); // Construct a new HMC5883 compass.
  Serial.println("Setting measurement mode to continous.");
  compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
}

void loop() {

  int x, y, z; //triple axis data

  //Tell the HMC5883 where to begin reading data
  Wire.beginTransmission(address);
  Wire.write(0x03); //select register 3, X MSB register
  Wire.endTransmission();


  //Read data from each axis, 2 registers per axis
  Wire.requestFrom(address, 6);
  if (6 <= Wire.available()) {
    x = Wire.read() << 8; //X msb
    x |= Wire.read(); //X lsb
    z = Wire.read() << 8; //Z msb
    z |= Wire.read(); //Z lsb
    y = Wire.read() << 8; //Y msb
    y |= Wire.read(); //Y lsb
  }

  //Print out values of each axis
  Serial.print("x: ");
  Serial.print(x);
  Serial.print("  y: ");
  Serial.print(y);
  Serial.print("  z: ");
  Serial.println(z);

  delay(250);
}

I then tried to call the readRawAxis function in HMC5883L.cpp as this most closely matched the sparkfun code and it returned fixed values. I then looked inside the ReadRawAxis function to see if it was a problem with the buffer or how it parses the buffer.

MagnetometerRaw HMC5883L::ReadRawAxis()
{
  uint8_t* buffer = Read(DataRegisterBegin, 6);
  MagnetometerRaw raw = MagnetometerRaw();
  raw.XAxis = (buffer[0] << 8) | buffer[1];
  raw.ZAxis = (buffer[2] << 8) | buffer[3];
  raw.YAxis = (buffer[4] << 8) | buffer[5];
  return raw;
}

ReadRawAxis calls the Read function which seems to do exactly what the sparkfun code does. Specify the address you want to read from and then read a specified amount of bytes, which are returned as a pointer to a byte array.

uint8_t* HMC5883L::Read(int address, int length)
{
  Wire.beginTransmission(HMC5883L_Address);
  Wire.write(address);
  Wire.endTransmission();
  
  Wire.beginTransmission(HMC5883L_Address);
  Wire.requestFrom(HMC5883L_Address, length);

  uint8_t buffer[length];
  if(Wire.available() == length)
  {
  for(uint8_t i = 0; i < length; i++)
  {
  buffer[i] = Wire.read();
  }
  }
  Wire.endTransmission();

  return buffer;
}

I copied the code directly from this to see if the byte array was being populated and it looks like it is not, but this could be a result of me using cpp code in an arduino environment.

I tried pre-specifying a byte array of size 6

byte my_array[6];
  my_array = compass.Read(0x03, 6);

but when I call compasss.Read I get the following error:
Arduino: 1.8.1 (Windows 10), Board: "Arduino/Genuino Uno"

C:\Users\umg\Documents\Arduino\hmclibtest\hmclibtest.ino: In function 'void loop()':

hmclibtest:26: error: incompatible types in assignment of 'uint8_t* {aka unsigned char*}' to 'byte [6] {aka unsigned char [6]}'

incompatible types in assignment of 'uint8_t* {aka unsigned char*}' to 'byte [6] {aka unsigned char [6]}'

or when I tried an unsigned char array:
incompatible types in assignment of 'uint8_t* {aka unsigned char*}' to 'unsigned char [6]'

Apologies if I am doing this wrong by mixing cpp and arduino code.

Arduino code is C/C++.

The Read function posted in the bildr example returns a pointer to a locally defined array, which goes out of scope upon return. The data at that address may or may not be overwritten in a later step.

In my book this is very bad programming practice.

I decided to test it out by passing a serial monitor into the library and it looks like that is exactly what is happening. If I output the buffer in the Read function the values are changing but as soon as it is returned to the ReadRawAxis the values are fixed.

Thank you for your help

I have the same problem in my HMC5883L compass on GY271. I'm using a library from Love Electronics (https://sklep.avt.pl/product/attachment/8c1745fc3b5d59c6142ee292398b33d5/pl_PL/ard-6476-hmc5883l-manual.pdf) - this is pdf file with the code.

Can you share your solution to get actual angle of heading of the compass instead of fixed values?

Thank you in advance :slight_smile: