I2C for HMC5883L Mag Sensor

Hi, I have this code working in Arduino for a Mag Sensor with I2C, but am getting tripped up by the
#include <wire.h> . I am relative newbie, so appreciate any help with this. Thanks! Here’s the code:

#include <wire.h>
#define address 0x1E //0011110b,I2C 7bit address

void setup()
{
  Serial.begin(9600);  // start serial for output
  Wire.begin();        // join i2c bus (address optional for master)

  //Put the HMC5883 IC into the correct operating mode
  Wire.beginTransmission(address); //open communication //with HMC5883L

  //Write CRA (00) write 0x3C 0x00 0x18 (1-avg, 55 Hz, normal measurement)
  Wire.write(0x3C); 
  Wire.write(0x00); 
  Wire.write(0x10); 
delay(20);
  //Write CRB (01) – write 0x3C 0x01 0x20 (Gain=1.3, the default)
  Wire.write(0x3C); 
  Wire.write(0x01); 
  Wire.write(0x20); 
delay(20);
  //Write Mode (02) – write 0x3C 0x02 0x00 (Continuous-measurement mode)
  Wire.write(0x3C); 
  Wire.write(0x02); 
  Wire.write(0x00); 
  delay(60);


  Wire.endTransmission();
}

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, 7);
  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);
} 

Hi @SparkRocks

I formatted your code to make it readable–click on the little pencil on your post to see how I did that!

You don’t need the #include for the wire library on Spark–it is built-in so you can just delete that line.

One other problem I noticed is that the code uses the int type which is 16-bits on Arduino and 32-bits on Spark. You should change:

int x,y,z;

to this:

int16_t x,y,z;
4 Likes

@bko Thanks for your suggestions which allowed the code to compile! The problem is that my code must not be addressing the mag chip properly, as I just get the same values over and over even when I move the Adafruit HMC5883Lsensor.

I had the Adafruit HMC5883L working well on my Arduino Uno with the Adafruit code written for Arduino, but would love to get it going on my Spark Core. I tried compiling the Adafruit Code (main file and two libraries) on Spark, removing wire.h, and got tons of errors. Would you or anyone else on the community have an interest in helping me modify the Adafruit Code for the HMC5883L? This seems to be a popular product for Adafruit, so I image many others would find this useful. Thanks again!

Here’s the main Adafruit code for the sensor:

/***************************************************************************
  This is a library example for the HMC5883 magnentometer/compass

Designed specifically to work with the Adafruit HMC5883 Breakout
  http://www.adafruit.com/products/1746
 
  *** You will also need to install the Adafruit_Sensor library! ***

These displays use I2C to communicate, 2 pins are required to interface.

Adafruit invests time and resources providing this open source code,
  please support Adafruit andopen-source hardware by purchasing products
  from Adafruit!

Written by Kevin Townsend for Adafruit Industries with some heading example from
  Love Electronics (loveelectronics.co.uk)
 
 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.

You should have received a copy of the GNU General Public License
 along with this program.  If not, see http://www.gnu.org/licenses/.

***************************************************************************/

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>

/* 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);
  Serial.println("------------------------------------");
  Serial.print  ("Sensor:       "); Serial.println(sensor.name);
  Serial.print  (“Driver Ver:   “); Serial.println(sensor.version);
  Serial.print  (“Unique ID:    “); Serial.println(sensor.sensor_id);
  Serial.print  (“Max Value:    “); Serial.print(sensor.max_value); Serial.println(” uT”);
  Serial.print  (“Min Value:    “); Serial.print(sensor.min_value); Serial.println(” uT”);
  Serial.print  (“Resolution:   “); Serial.print(sensor.resolution); Serial.println(” uT”);  
  Serial.println(”------------------------------------”);
  Serial.println(””);
  delay(500);
}

void setup(void)
{
  Serial.begin(9600);
  Serial.println(“HMC5883 Magnetometer Test”); Serial.println("");
 
  /* Initialise the sensor /
  if(!mag.begin())
  {
    /
There was a problem detecting the HMC5883 … check your connections /
    Serial.println(“Ooops, no HMC5883 detected … Check your wiring!”);
    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);
 
  /
Display the results (magnetic vector values are in micro-Tesla (uT)) */
  Serial.print("X: “); Serial.print(event.magnetic.x); Serial.print(”  ");
  Serial.print("Y: “); Serial.print(event.magnetic.y); Serial.print(”  ");
  Serial.print("Z: “); Serial.print(event.magnetic.z); Serial.print(”  ");Serial.println(“uT”);

// 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: -13* 2’ W, which is ~13 Degrees, or (which we need) 0.22 radians
  // If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
  float declinationAngle = 0.22;
  heading += declinationAngle;
 
  // Correct for when signs are reversed.
  if(heading < 0)
    heading += 2PI;
    
  // 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("Heading (degrees): "); Serial.println(headingDegrees);
 
  delay(500);
}

@SparkRocks, are you powering the sensor from the Spark 3.3V line? The Adafruit_Sensor library is available on the web IDE and I can port the HMC5883 Adafruit library if you need it. I should have it for your by tomorrow. :smile:

1 Like

Sorry for the bad post again. I think anything that starts with “#” is causing a problem.

This list of the errors might be more helpful:

In file included from Adafruit_HMC5883_U.cpp:8:0:
Adafruit_HMC5883_U.h:35:5: error: ‘int8_t’ does not name a type
int8_t status;
^
Adafruit_HMC5883_U.h:36:5: error: ‘uint8_t’ does not name a type
uint8_t reserved[3];
^
Adafruit_HMC5883_U.h:57:5: error: ‘int32_t’ does not name a type
int32_t version; /< must be sizeof(struct sensors_event_t) */
^
Adafruit_HMC5883_U.h:58:5: error: ‘int32_t’ does not name a type
int32_t sensor_id; /
< unique sensor identifier */
^
Adafruit_HMC5883_U.h:59:5: error: ‘int32_t’ does not name a type
int32_t type; /< sensor type */
^
Adafruit_HMC5883_U.h:60:5: error: ‘int32_t’ does not name a type
int32_t reserved0; /
< reserved */
^
Adafruit_HMC5883_U.h:61:5: error: ‘int32_t’ does not name a type
int32_t timestamp; /< time is in milliseconds */
^
Adafruit_HMC5883_U.h:85:5: error: ‘int32_t’ does not name a type
int32_t version; /
< version of the hardware + driver */
^
Adafruit_HMC5883_U.h:86:5: error: ‘int32_t’ does not name a type
int32_t sensor_id; /< unique sensor identifier */
^
Adafruit_HMC5883_U.h:87:5: error: ‘int32_t’ does not name a type
int32_t type; /
< this sensor’s type (ex. SENSOR_TYPE_LIGHT) */
^
Adafruit_HMC5883_U.h:91:5: error: ‘int32_t’ does not name a type
int32_t min_delay; /**< min delay in microseconds between events. zero = not a constant rate */
^
Adafruit_HMC5883_U.cpp:25:6: error: ‘Adafruit_HMC5883_Unified’ has not been declared
void Adafruit_HMC5883_Unified::write8(byte address, byte reg, byte value)
^
Adafruit_HMC5883_U.cpp:25:39: error: variable or field ‘write8’ declared void
void Adafruit_HMC5883_Unified::write8(byte address, byte reg, byte value)
^
Adafruit_HMC5883_U.cpp:25:39: error: ‘byte’ was not declared in this scope
Adafruit_HMC5883_U.cpp:25:53: error: ‘byte’ was not declared in this scope
void Adafruit_HMC5883_Unified::write8(byte address, byte reg, byte value)
^
Adafruit_HMC5883_U.cpp:25:63: error: ‘byte’ was not declared in this scope
void Adafruit_HMC5883_Unified::write8(byte address, byte reg, byte value)
^
Adafruit_HMC5883_U.cpp:38:1: error: ‘byte’ does not name a type
byte Adafruit_HMC5883_Unified::read8(byte address, byte reg)
^
Adafruit_HMC5883_U.cpp:56:6: error: ‘Adafruit_HMC5883_Unified’ has not been declared
void Adafruit_HMC5883_Unified::read()
^
Adafruit_HMC5883_U.cpp: In function ‘void read()’:
Adafruit_HMC5883_U.cpp:59:3: error: ‘Wire’ was not declared in this scope
Wire.beginTransmission((byte)HMC5883_ADDRESS_MAG);
^
Adafruit_HMC5883_U.cpp:59:27: error: ‘byte’ was not declared in this scope
Wire.beginTransmission((byte)HMC5883_ADDRESS_MAG);
^
Adafruit_HMC5883_U.cpp:60:16: error: ‘HMC5883_REGISTER_MAG_OUT_X_H_M’ was not declared in this scope
Wire.write(HMC5883_REGISTER_MAG_OUT_X_H_M);
^
Adafruit_HMC5883_U.cpp:62:26: error: expected ‘)’ before 'HMC5883_ADDRESS_MAG’
Wire.requestFrom((byte)HMC5883_ADDRESS_MAG, (byte)6);
^
Adafruit_HMC5883_U.cpp:62:53: error: expected ‘)’ before numeric constant
Wire.requestFrom((byte)HMC5883_ADDRESS_MAG, (byte)6);
^
Adafruit_HMC5883_U.cpp:67:5: error: ‘uint8_t’ was not declared in this scope
uint8_t xhi = Wire.read();
^
Adafruit_HMC5883_U.cpp:67:13: error: expected ‘;’ before 'xhi’
uint8_t xhi = Wire.read();
^
Adafruit_HMC5883_U.cpp:68:13: error: expected ‘;’ before 'xlo’
uint8_t xlo = Wire.read();
^
Adafruit_HMC5883_U.cpp:69:13: error: expected ‘;’ before 'zhi’
uint8_t zhi = Wire.read();
^
Adafruit_HMC5883_U.cpp:70:13: error: expected ‘;’ before 'zlo’
uint8_t zlo = Wire.read();
^
Adafruit_HMC5883_U.cpp:71:13: error: expected ‘;’ before 'yhi’
uint8_t yhi = Wire.read();
^
Adafruit_HMC5883_U.cpp:72:13: error: expected ‘;’ before 'ylo’
uint8_t ylo = Wire.read();
^
Adafruit_HMC5883_U.cpp:75:3: error: ‘_magData’ was not declared in this scope
_magData.x = (int16_t)(xlo | ((int16_t)xhi << 8));
^
Adafruit_HMC5883_U.cpp:75:17: error: ‘int16_t’ was not declared in this scope
_magData.x = (int16_t)(xlo | ((int16_t)xhi << 8));
^
Adafruit_HMC5883_U.cpp:75:26: error: ‘xlo’ was not declared in this scope
_magData.x = (int16_t)(xlo | ((int16_t)xhi << 8));
^
Adafruit_HMC5883_U.cpp:75:42: error: expected ‘)’ before ‘xhi’
_magData.x = (int16_t)(xlo | ((int16_t)xhi << 8));
^
Adafruit_HMC5883_U.cpp:76:26: error: ‘ylo’ was not declared in this scope
_magData.y = (int16_t)(ylo | ((int16_t)yhi << 8));
^
Adafruit_HMC5883_U.cpp:76:42: error: expected ‘)’ before ‘yhi’
_magData.y = (int16_t)(ylo | ((int16_t)yhi << 8));
^
Adafruit_HMC5883_U.cpp:77:26: error: ‘zlo’ was not declared in this scope
_magData.z = (int16_t)(zlo | ((int16_t)zhi << 8));
^
Adafruit_HMC5883_U.cpp:77:42: error: expected ‘)’ before ‘zhi’
_magData.z = (int16_t)(zlo | ((int16_t)zhi << 8));
^
Adafruit_HMC5883_U.cpp: At global scope:
Adafruit_HMC5883_U.cpp:92:1: error: ‘Adafruit_HMC5883_Unified’ does not name a type
Adafruit_HMC5883_Unified::Adafruit_HMC5883_Unified(int32_t sensorID) {
^
Adafruit_HMC5883_U.cpp:105:6: error: ‘Adafruit_HMC5883_Unified’ has not been declared
bool Adafruit_HMC5883_Unified::begin()
^
Adafruit_HMC5883_U.cpp: In function ‘bool begin()’:
Adafruit_HMC5883_U.cpp:108:3: error: ‘Wire’ was not declared in this scope
Wire.begin();
^
Adafruit_HMC5883_U.cpp:111:10: error: ‘HMC5883_ADDRESS_MAG’ was not declared in this scope
write8(HMC5883_ADDRESS_MAG, HMC5883_REGISTER_MAG_MR_REG_M, 0x00);
^
Adafruit_HMC5883_U.cpp:111:31: error: ‘HMC5883_REGISTER_MAG_MR_REG_M’ was not declared in this scope
write8(HMC5883_ADDRESS_MAG, HMC5883_REGISTER_MAG_MR_REG_M, 0x00);
^
Adafruit_HMC5883_U.cpp:111:66: error: ‘write8’ was not declared in this scope
write8(HMC5883_ADDRESS_MAG, HMC5883_REGISTER_MAG_MR_REG_M, 0x00);
^
Adafruit_HMC5883_U.cpp:114:31: error: ‘HMC5883_REGISTER_MAG_CRA_REG_M’ was not declared in this scope
write8(HMC5883_ADDRESS_MAG, HMC5883_REGISTER_MAG_CRA_REG_M, 0x18);
^
Adafruit_HMC5883_U.cpp:118:14: error: ‘HMC5883_MAGGAIN_1_3’ was not declared in this scope
setMagGain(HMC5883_MAGGAIN_1_3);
^
Adafruit_HMC5883_U.cpp:118:33: error: ‘setMagGain’ was not declared in this scope
setMagGain(HMC5883_MAGGAIN_1_3);
^
Adafruit_HMC5883_U.cpp: At global scope:
Adafruit_HMC5883_U.cpp:128:6: error: ‘Adafruit_HMC5883_Unified’ has not been declared
void Adafruit_HMC5883_Unified::setMagGain(hmc5883MagGain gain)
^
Adafruit_HMC5883_U.cpp:128:43: error: variable or field ‘setMagGain’ declared void
void Adafruit_HMC5883_Unified::setMagGain(hmc5883MagGain gain)
^
Adafruit_HMC5883_U.cpp:128:43: error: ‘hmc5883MagGain’ was not declared in this scope
Adafruit_HMC5883_U.cpp:10:14: warning: ‘_hmc5883_Gauss_LSB_XY’ defined but not used [-Wunused-variable]
static float _hmc5883_Gauss_LSB_XY = 1100.0F; // Varies with gain
^
Adafruit_HMC5883_U.cpp:11:14: warning: ‘_hmc5883_Gauss_LSB_Z’ defined but not used [-Wunused-variable]
static float _hmc5883_Gauss_LSB_Z = 980.0F; // Varies with gain
^
make: *** [Adafruit_HMC5883_U.o] Error 1

Error: Could not compile. Please review your code.

Hi @peekay123, thanks for your reply and kind offer of help. Great community :smile:
Yes, I am running the sensor off the 3V3 pin and also tried 3V3* with no luck.
Cheers!

@SparkRocks, I’ll have the complete library ported tomorrow :smile:

1 Like

@peekay123, the Adafruit breakout board for the HMC5883L is designed to work with either 3…3V or 5V. It works great on my Arduino Uno on both 3 and 5V.

Awesome, many thanks @peekay123 !

@SparkRocks, I have create a github repo with the ported libraries and demo app here. Copy each file to a tab in the web IDE and it should compile. If you use the Spark CLI, copy all the files to a single directory and compile the directory. Let me know how it goes :smile:

2 Likes

Hi @peekay123, thanks for getting to this so quickly. I copied each file to the web IDE and went to compile. On my first attempt, an error came up saying there was an include wire.h in the Adafruit_HMC5883_U.h file, so I excluded it with // and tried a second compile, which had several errors, which I’ll paste below. Thanks again for your help:

In file included from …/inc/spark_wiring.h:30:0,
from …/inc/application.h:29,
from Adafruit_HMC5883_U.h:20,
from Adafruit_HMC5883_U.cpp:29:
…/…/core-common-lib/SPARK_Firmware_Driver/inc/config.h:12:2: warning: #warning “Defaulting to Release Build” [-Wcpp]
#warning “Defaulting to Release Build”
^
In file included from …/inc/spark_wiring.h:37:0,
from …/inc/application.h:29,
from Adafruit_HMC5883_U.h:20,
from Adafruit_HMC5883_U.cpp:29:
…/inc/spark_wiring_ipaddress.h: In member function ‘IPAddress::operator uint32_t()’:
…/inc/spark_wiring_ipaddress.h:53:52: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
operator uint32_t() { return ((uint32_t)_address); };
^
…/inc/spark_wiring_ipaddress.h: In member function ‘bool IPAddress::operator==(const IPAddress&)’:
…/inc/spark_wiring_ipaddress.h:54:72: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
bool operator==(const IPAddress& addr) { return (((uint32_t)_address)) == (((uint32_t)addr._address)); };
^
…/inc/spark_wiring_ipaddress.h:54:105: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
bool operator==(const IPAddress& addr) { return (((uint32_t)_address)) == (((uint32_t)addr._address)); };
^
In file included from …/inc/spark_wiring.h:30:0,
from …/inc/application.h:29,
from Adafruit_Sensor.h:23,
from Adafruit_Sensor.cpp:1:
…/…/core-common-lib/SPARK_Firmware_Driver/inc/config.h:12:2: warning: #warning “Defaulting to Release Build” [-Wcpp]
#warning “Defaulting to Release Build”
^
In file included from …/inc/spark_wiring.h:37:0,
from …/inc/application.h:29,
from Adafruit_Sensor.h:23,
from Adafruit_Sensor.cpp:1:
…/inc/spark_wiring_ipaddress.h: In member function ‘IPAddress::operator uint32_t()’:
…/inc/spark_wiring_ipaddress.h:53:52: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
operator uint32_t() { return ((uint32_t)_address); };
^
…/inc/spark_wiring_ipaddress.h: In member function ‘bool IPAddress::operator==(const IPAddress&)’:
…/inc/spark_wiring_ipaddress.h:54:72: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
bool operator==(const IPAddress& addr) { return (((uint32_t)_address)) == (((uint32_t)addr._address)); };
^
…/inc/spark_wiring_ipaddress.h:54:105: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
bool operator==(const IPAddress& addr) { return (((uint32_t)_address)) == (((uint32_t)addr._address)); };
^
In file included from …/inc/spark_wiring.h:30:0,
from …/inc/application.h:29,
from magsensor.cpp:2:
…/…/core-common-lib/SPARK_Firmware_Driver/inc/config.h:12:2: warning: #warning “Defaulting to Release Build” [-Wcpp]
#warning “Defaulting to Release Build”
^
In file included from …/inc/spark_wiring.h:37:0,
from …/inc/application.h:29,
from magsensor.cpp:2:
…/inc/spark_wiring_ipaddress.h: In member function ‘IPAddress::operator uint32_t()’:
…/inc/spark_wiring_ipaddress.h:53:52: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
operator uint32_t() { return ((uint32_t)_address); };
^
…/inc/spark_wiring_ipaddress.h: In member function ‘bool IPAddress::operator==(const IPAddress&)’:
…/inc/spark_wiring_ipaddress.h:54:72: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
bool operator==(const IPAddress& addr) { return (((uint32_t)_address)) == (((uint32_t)addr._address)); };
^
…/inc/spark_wiring_ipaddress.h:54:105: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
bool operator==(const IPAddress& addr) { return (((uint32_t)_address)) == (((uint32_t)addr._address)); };
^
magsensor.cpp: In function ‘void loop()’:
magsensor.cpp:93:59: error: ‘atan2’ was not declared in this scope
Serial.print("X: “); Serial.print(event.magnetic.x); Serial.print(” ");
^
magsensor.cpp:104:18: error: ‘PI’ was not declared in this scope
// If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
^
magsensor.cpp:107:18: error: ‘PI’ was not declared in this scope

^
magsensor.cpp:111:40: error: ‘M_PI’ was not declared in this scope

^
make: *** [magsensor.o] Error 1

Error: Could not compile. Please review your code.

@SparkRocks, well that was weird! I think I goofed on the git update cause the repo was not correct! I updated the code and it should compile now. The changed files show “Fixes for Compile”. Let me know how it goes :smile:

Hi @peekay123, it’s working now, thanks. I really appreciate your help!

1 Like

Yesterday, I wanted to test one of these Magnetometer modules, but I didn’t get far:

I downloaded all necessary libraries from Github with the links in the IDE and used them in Particle Dev with the included example.

Originally it did not compile as it found math functions unknown to the Particles. (atan etc…)
So, I blanked out the second part in the sketch where the heading is calculated.
Now it compiles and the serial monitor shows a very fast datastream of x,y and z values.

But the heading is the most interesting part…

How can I adapt the sketch to work on Particles?

    /***************************************************************************
      This is a library example for the HMC5883 magnentometer/compass
    
      Designed specifically to work with the Adafruit HMC5883 Breakout
      http://www.adafruit.com/products/1746
    
      *** You will also need to install the Adafruit_Sensor library! ***
    
      These displays use I2C to communicate, 2 pins are required to interface.
    
      Adafruit invests time and resources providing this open source code,
      please support Adafruit andopen-source hardware by purchasing products
      from Adafruit!
    
      Written by Kevin Townsend for Adafruit Industries with some heading example from
      Love Electronics (loveelectronics.co.uk)
    
     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.
    
     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
     ***************************************************************************/
    
    // #include <Wire.h> // Probably no need for Particles?
    #include "application.h" // TRY! I added this to try and get math functions like "atan"...
    
    #include "Adafruit_Sensor.h"
    #include "Adafruit_HMC5883.h"
    
    /* 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);
      Serial.println("------------------------------------");
      Serial.print  ("Sensor:       "); Serial.println(sensor.name);
      Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
      Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
      Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" uT");
      Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" uT");
      Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" uT");
      Serial.println("------------------------------------");
      Serial.println("");
      delay(500);
    }
    
    void setup(void)
    {
      Serial.begin(9600);
      Serial.println("HMC5883 Magnetometer Test"); Serial.println("");
    
      /* Initialise the sensor */
      if(!mag.begin())
      {
        /* There was a problem detecting the HMC5883 ... check your connections */
        Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
        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);
    
      /* Display the results (magnetic vector values are in micro-Tesla (uT)) */
      Serial.print("X: "); Serial.print(event.magnetic.x); Serial.print("  ");
      Serial.print("Y: "); Serial.print(event.magnetic.y); Serial.print("  ");
      Serial.print("Z: "); Serial.print(event.magnetic.z); Serial.print("  ");Serial.println("uT");
    
    // REMARK from @Fidel: The next part doesn't work. It has probably not been ported to Particles (Math functions)
    
      // 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: -13* 2' W, which is ~13 Degrees, or (which we need) 0.22 radians
      // If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
      float declinationAngle = 0.22;
      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("Heading (degrees): "); Serial.println(headingDegrees);
    
      delay(500);
    
    }

:hand: :older_man:

If you just need atan and other arithmetic functions, those are in the math library:

#include "math.h"

1 Like

Thanks @bko!
After I added "math.h", the sketch compiled partially in the web IDE, but not at all in Particle DEV: Red error...
Here's a screenshot:

in the web IDE, I get errors concerning "PI":

magsensor.cpp:101:18: error: 'PI' was not declared in this scope
magsensor.cpp:104:18: error: 'PI' was not declared in this scope

These are the lines with "PI"

          // 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;

So, how is "PI" used in the "math.h" library?

PS: I thought "math.h" was also integrated in "application.h"...
My misunderstanding! So, in above sketch we don't need application.h?
I'm always hesitating when to do that and when not...
And should I copy "math.h" in the project folder in Particle DEV?
Particle world is a maze, but I'm learning every day!

The math include is separate from the application include so you need both if you are not using the preprocessor.

The constant PI is defined in Arduino land as:

#define PI 3.1415926535897932384626433832795
1 Like

OK @bko, that’s also what I just did, but slightly less decimals… :wink:
The good news is also that It now compiles as well in the web IDE as in Particle DEV!

PI does strange things :space_invader:

I want to share the working sketch just in case somebody also wants to try this out:

    /***************************************************************************
      This is a an example for the Adafruit HMC5883 magnetometer/compass library
      Note from @Fidel: The example was not ported with the library, so I tried it with some help and now it works, but I found the calculated heading is far from correct...
    
      Connections:
    
      Module	Photon
    	DRDY	  Data Ready pin: "To speed up readings"...
    	SDA	    SDA (D0)
    	SCL	    SCL (D1)
    	Gnd	    Gnd
    	Vcc	    3v3
     ***************************************************************************/
    
    // Libraries include for use with Particle Lib (not Web IDE)
    #include "math.h"
    #include "Adafruit_Sensor.h"
    #include "Adafruit_HMC5883.h"
    
    #define PI 3.1415926535897932384626433832795
    
    /* 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);
      Serial.println("------------------------------------");
      Serial.print  ("Sensor:       "); Serial.println(sensor.name);
      Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
      Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
      Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" uT");
      Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" uT");
      Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" uT");
      Serial.println("------------------------------------");
      Serial.println("");
      delay(500);
    }
    
    void setup(void)
    {
      Serial.begin(9600);
      Serial.println("HMC5883 Magnetometer Test"); Serial.println("");
    
      /* Initialise the sensor */
      if(!mag.begin())
      {
        /* There was a problem detecting the HMC5883 ... check your connections */
        Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
        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);
    
      /* Display the results (magnetic vector values are in micro-Tesla (uT)) */
      Serial.print("X: "); Serial.print(event.magnetic.x); Serial.print("  ");
      Serial.print("Y: "); Serial.print(event.magnetic.y); Serial.print("  ");
      Serial.print("Z: "); Serial.print(event.magnetic.z); Serial.print("  ");Serial.println("uT");
    
      // 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);
    
      // Add your 'Declination Angle' to your heading, ('Error' of the magnetic field in your location) Find yours here: http://www.magnetic-declination.com/ In Belgium: +0° 47' E = 0.015 rad (360° = 2 PI rad)
      float declinationAngle = 0.015;
      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/PI;
    
      Serial.print("Heading (degrees): "); Serial.println(headingDegrees);
    
      delay(500); // Slow down serial monitor readings!
    }

FYI: I was planning to use this sensor to stabilize a boat radar, but now that I played with it, the accuracy of the calculated heading is very disappointing…
:clap: :older_man:

@FiDel and @bko, for some obscure reason the gcc math.h defines M_PI instead of PI.

If you use that instead, you can forget your own #define

1 Like