HMC5883L compass problem?

Hello to all!

I have problem with HMC5883L compass sensor.
Code work, direction shows good…but…degrees are very unstable. Degrees varying of + -1 degree, even without moving the sensor.
My platform is Arduino Uno.

Here is code (not finish yet only with LCD):

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
#include <LiquidCrystal.h>
    LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
byte up[8] = { 0x04, 0x0E, 0x15, 0x04, 0x04, 0x04, 0x04, 0x00 };
byte down[8] = { 0x04, 0x04, 0x04, 0x04, 0x15, 0x0E, 0x04, 0x00 };
/* Assign a unique ID to this sensor at the same time */
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
void displaySensorDetails(void)
{
  sensor_t sensor;
  mag.getSensor(&sensor);
}
void setup(void) 
{ 
      lcd.begin(20, 2);
          lcd.createChar(2, up);
  lcd.createChar(1, down);
  Serial.begin(9600); 
  lcd.setCursor(0, 0);
  lcd.print("    9A9Y Kompas");
  lcd.setCursor(0, 1);
  lcd.print("  v.1.0 - 2014. AD");
  delay(5000);
  lcd.clear();
  /* Initialise the sensor */
  if(!mag.begin())
  {
    while(1);
  }
  /* Display some basic information on this sensor */
  displaySensorDetails();
  
}
void loop(void) 
{
  /* Get a new sensor event */ 
  sensors_event_t event; 
  mag.getEvent(&event);
  // Hold the module so that Z is pointing 'up' and you can measure the heading with x&y
  // Calculate heading when the magnetometer is level, then correct for signs of axis.
  float heading = atan2(event.magnetic.y, event.magnetic.x);
  // 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/
  // Mine is: 3* 36' E, which is ~5.8 Degrees, or (which we need) 0.0586431 radians (Rovisce)
  // If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
  float declinationAngle = 0.05864;
  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; 
  Serial.print("Stupnjevi: "); Serial.println(headingDegrees);
    if((headingDegrees < 22.5) || (headingDegrees > 337.5 ))
        Serial.println("SJEVER  ");
    if((headingDegrees > 22.5) && (headingDegrees < 67.5 ))
        Serial.println("SJEVEROISTOK  ");
    if((headingDegrees > 67.5) && (headingDegrees < 112.5 ))
        Serial.println("ISTOK  ");
    if((headingDegrees > 112.5) && (headingDegrees < 157.5 ))
        Serial.println("JUGOISTOK  ");
    if((headingDegrees > 157.5) && (headingDegrees < 202.5 ))
        Serial.println("JUG  ");
    if((headingDegrees > 202.5) && (headingDegrees < 247.5 ))
        Serial.println("JUGOZAPAD  ");
    if((headingDegrees > 247.5) && (headingDegrees < 292.5 ))
        Serial.println("ZAPAD  ");
    if((headingDegrees > 292.5) && (headingDegrees < 337.5 ))
        Serial.println("SJEVEROZAPAD  ");
  
  /*
 The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 */
// initialize the library with the numbers of the interface pins
  // set up the LCD's number of columns and rows:
  // Print a message to the LCD.
  lcd.setCursor(0, 0);
  if ( headingDegrees < 10 )
  lcd.print("Stupnjevi: "),lcd.print(headingDegrees),lcd.setCursor(15,0),lcd.print((char)223),lcd.print("    ");
  else if ( headingDegrees < 100 )
  lcd.print("Stupnjevi: "),lcd.print(headingDegrees),lcd.setCursor(16,0),lcd.print((char)223),lcd.print("   ");
  else if ( headingDegrees > 100 )
  lcd.print("Stupnjevi: ");lcd.print(headingDegrees),lcd.setCursor(17,0),lcd.print((char)223),lcd.print("  ");
  lcd.setCursor(0, 1);
if((headingDegrees < 22.5) || (headingDegrees > 337.5 ))
lcd.print("Smjer:SJEVER "),lcd.setCursor(19,1),lcd.write(2);
if((headingDegrees > 22.5) && (headingDegrees < 67.5 ))
lcd.print("Smjer:SJEVEROISTOK "),lcd.setCursor(19,1),lcd.write(2);
if((headingDegrees > 67.5) && (headingDegrees < 112.5 ))
lcd.print("Smjer:ISTOK  "),lcd.setCursor(19,1),lcd.write(1);
if((headingDegrees > 112.5) && (headingDegrees < 157.5 ))
lcd.print("Smjer:JUGOISTOK  "),lcd.setCursor(19,1),lcd.write(1);
if((headingDegrees > 157.5) && (headingDegrees < 202.5 ))
lcd.print("Smjer:JUG    "),lcd.setCursor(19,1),lcd.print((char)127);
if((headingDegrees > 202.5) && (headingDegrees < 247.5 ))
lcd.print("Smjer:JUGOZAPAD    "),lcd.setCursor(19,1),lcd.print((char)127);
if((headingDegrees > 247.5) && (headingDegrees < 292.5 ))
lcd.print("Smjer:ZAPAD  "),lcd.setCursor(15,1),lcd.print((char)126);
if((headingDegrees > 292.5) && (headingDegrees < 337.5 ))
lcd.print("Smjer:SJEVEROZAPAD  "),lcd.setCursor(19,1),lcd.print((char)126);
  delay(500);
}

If anyone know solution for resolving unstable reading, please let me know.
Thank you very much for help.

Adafruit_HMC5883_U.rar (3.15 KB)

monitor.png

That is normal sensor noise. Be sure to calibrate the compass by adjusting the gains and offsets on each magnetometer axis, or the compass readings will be off by many degrees.

It is partly noise caused by the sensor, and also noise which actually exists because the geomagnetic field itself is not very stable and changes all the time.

Is there any software solution for noise reduction? How to do calibration for gain and offset? Is there a solution to put that in the code? Like I say, degrees reading are OK, but for any advice to improve my code I would be grateful.

To reduce noise, average a few readings.

For lots of advice on how to calibrate your compass, google "magnetometer compass calibration", or see this link for a rather more complex and accurate procedure https://forum.sparkfun.com/viewtopic.php?f=42&t=36399&p=162843