DEV Community

Cover image for Self-Aligning Satellite Dish in Rust: Compass Example
Ian Ndeda
Ian Ndeda

Posted on

Self-Aligning Satellite Dish in Rust: Compass Example

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

compass-setup

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
Enter fullscreen mode Exit fullscreen mode

❗ 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);
Enter fullscreen mode Exit fullscreen mode

Building and loading the program into the Pico will give us raw data from the magnetometer.

cargo run --example compass
Enter fullscreen mode Exit fullscreen mode

Raw compass data should be regularly sent to the Serial console.

compss-raw-data

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.

uncalibrated-raw-data

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.

calibrated-raw-data

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);
Enter fullscreen mode Exit fullscreen mode

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.);
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

Printing the results...

            writeln!(serialbuf, "{} deg. {} uT", heading.round(), (mag*100.).round()).unwrap();
            transmit_uart_data(uart_data.as_mut().unwrap(), serialbuf);
Enter fullscreen mode Exit fullscreen mode

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.

compass-example-results

In the next part of the series we'll apply the above to our larger project.

Top comments (0)