Create a new file compass.rs
under the examples
directory and copy the contents of src/main.rs
into it. Clear the super loop
. We will replace it with an example application code to test the compass section of our project.
Table of contents
Requirements
- 1 x Raspberry Pico board
- 1 x USB Cable type 2.0
- 1 x HC-05 Bluetooth module
- 1 x HMC5833L Compass Module
- 15 x M-M jumper wires
- 2 x Mini Breadboards
Implementation
Connections Diagram
Raw Data
Under loop
we'll read from the HMC5883L's data registers and wait for interrupts.
From the Compass' manual we find that we only have to point to the address of DATA_OUTPUT_X_MSB_R
and the pointer automatically updates to read the values from the remaining data registers.
Place the constant DATA_OUTPUT_X_MSB_R
.
// Data Registers.
const DATA_OUTPUT_X_MSB_R: u8 = 0x03;// HMC5883L
❗ Please note the order in which the magnetometer produces the output: x, z and then y.
// Read raw data from compass.
// Point to the address of DATA_OUTPUT_X_MSB_R
writebuf = [DATA_OUTPUT_X_MSB_R];
i2c_write(&mut i2c0, HMC5883L_ADDR, &mut writebuf).unwrap();
// Read the output of the HMC5883L
// All six registers are read into the
// rawbuf buffer
let mut rawbuf: [u8; 6] = [0; 6];
i2c_read(&mut i2c0, HMC5883L_ADDR, &mut rawbuf).unwrap();
let x_h = rawbuf[0] as u16;
let x_l = rawbuf[1] as u16;
let z_h = rawbuf[2] as u16;
let z_l = rawbuf[3] as u16;
let y_h = rawbuf[4] as u16;
let y_l = rawbuf[5] as u16;
let x = ((x_h << 8) + x_l) as i16;
let y = ((y_h << 8) + y_l) as i16;
let z = ((z_h << 8) + z_l) as i16;
// Prints raw data
writeln!(serialbuf, "x: {} y: {} z: {}", x, y, z ).unwrap();
transmit_uart_data(uart_data.as_mut().unwrap(), serialbuf);
Building and loading the program into the Pico will give us raw data from the magnetometer.
cargo run --example compass
Raw compass data should be regularly sent to the Serial console.
Calibration
We can conduct a simple test for our magnetometer to check for accuracy. On a flat surface turn the magnetometer through 360 degrees while recording the output values.
For more reliable results try and conduct the test away from permanent magnets like those in speakers to avoid too much hard iron distortion.
Have the printed raw data into an Excel sheet or similar software and plot the x
, y
and z
points on a scatter graph. A reasonable circle should result. If the circle is not centered at the graph's origin the compass requires calibration.
A quick and dirty solution to this is to introduce offsets that will roughly bring the circle's center to the origin of the graph. Perform the test again until a reasonable circle is achieved. For my case I found that I had to add 75, 185 and 115 to the x, y and z coordinates.
Note that these values will be different at different locations. This lopsidedness of the circle is due to magnetic noise from the vicinity of the magnetometer. Introducing a permanent magnet near it for example will result in different values.
Before deploying the project we should remember to conduct another calibration while the magnet sits in its final position.
Correct the printed raw data.
// Prints raw data
writeln!(serialbuf, "x: {} y: {} z: {}", x + 75, y + 185, z + 115).unwrap();
transmit_uart_data(uart_data.as_mut().unwrap(), serialbuf);
Magnetic Heading & Strength
We can now calculate the magnetic heading from the raw data above. There are a number of configurations with which to translate this raw data.
We shall use the North-Clockwise convention as it has the x-axis pointing North which aligns with our final project design.
// North-Clockwise convention
let mut heading = (y as f32 + 185.).atan2(x as f32 + 75.);
Continue by adding the magnetic declination to the heading
value, converting to radians, handling any instances of overflow and finally converting back to degrees for printing.
Do not forget to declare the declination constant. This value is different for every location. You can check for your area here.
const MAGNETIC_DECLINATION: f32 = 1.667;
heading += MAGNETIC_DECLINATION*(PI/180.);// add declination in radians
// Check for sign
if heading < 0. {
heading += 2.*PI;
}
// Check for value wrap
if heading > 2.*PI {
heading -= 2.*PI;
}
heading *= 180./PI;
From the raw magnetic data the magnetic strength can also be calculated. We first factor in the Magnetometer Gain we set in the earlier part.
// Calculating mag strength
let x = (f32::from(x) + 75.)/ 1090.;
let y = (f32::from(y) + 185.)/ 1090.;
let z = (f32::from(z) + 115.)/ 1090.;
let mag = (x*x + y*y + z*z).sqrt();
Printing the results...
writeln!(serialbuf, "{} deg. {} uT", heading.round(), (mag*100.).round()).unwrap();
transmit_uart_data(uart_data.as_mut().unwrap(), serialbuf);
Results
Here is the final code of this part.
Flashing into the Pico we should be able to see the printed values of the raw magnetic values the heading and magnetic strength.
In the next part of the series we'll apply the above to our larger project.
Top comments (0)