DEV Community

Ian Ndeda
Ian Ndeda

Posted on

Self-Aligning Satellite Dish in Rust: Compass Application

In the previous part we were able to receive raw magnetometer data from the HMC5833L module and process it to acquire magnetic heading and strength. We will now apply those functions to our self aligning dish project.

The magnetic heading is important for the project as it allows our mock dish to ascertain its orientation in its attempts to align to the look angles.

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
  • 1 x Neo-6M GPS Module
  • Serial Bluetooth App

Implementation

Connections Diagram

setup

Functions

Let's organize the compass configuration code we developed earlier under a function: configure_compass.

fn configure_compass(i2c0: &mut I2C0, uart: &UART0) { // Configure and confirm HMC5833L
    let mut writebuf: [u8; 1];// buffer to hold 1 byte
    let mut writebuf2: [u8; 2];// buffer to hold 2 bytes
    let mut readbuf: [u8; 1] = [0; 1];
    let serialbuf = &mut String::<164>::new();

    // ID the compass
    // Read ID REG A
    writebuf = [IDENTIFICATION_REG_A];
    i2c_write(i2c0, HMC5883L_ADDR, &mut writebuf).unwrap();
    i2c_read(i2c0, HMC5883L_ADDR, &mut readbuf).unwrap();
    let id_a = readbuf[0];
    writeln!(serialbuf, "Id reg a: 0x{:02X}", id_a).unwrap();
    transmit_uart_data(&uart, serialbuf);

    // Read ID REG B 
    writebuf = [IDENTIFICATION_REG_B];
    i2c_write(i2c0, HMC5883L_ADDR, &mut writebuf).unwrap();
    i2c_read(i2c0, HMC5883L_ADDR, &mut readbuf).unwrap();
    let id_b = readbuf[0];
    writeln!(serialbuf, "Id reg b: 0x{:02X}", id_b).unwrap();
    transmit_uart_data(&uart, serialbuf);

    // Read ID REG C
    writebuf = [IDENTIFICATION_REG_C];
    i2c_write(i2c0, HMC5883L_ADDR, &mut writebuf).unwrap();
    i2c_read(i2c0, HMC5883L_ADDR, &mut readbuf).unwrap();
    let id_c = readbuf[0];
    writeln!(serialbuf, "Id reg c: 0x{:02X}", id_c).unwrap();
    transmit_uart_data(&uart, serialbuf);

    if id_a == 0x48 && id_b == 0x34 && id_c == 0x33 {
        writeln!(serialbuf, "Magnetometer ID confirmed!").unwrap();
        transmit_uart_data(&uart, serialbuf);
    }

    // Set compass in continuous mode & confirm
    writebuf2 = [MODE_R, 0x0];
    i2c_write(i2c0, HMC5883L_ADDR, &mut writebuf2).unwrap();

    writebuf = [MODE_R];
    i2c_write(i2c0, HMC5883L_ADDR, &mut writebuf).unwrap();
    i2c_read(i2c0, HMC5883L_ADDR, &mut readbuf).unwrap();

    let mode = readbuf[0];
    writeln!(serialbuf, "Mode reg: 0b{:08b}", mode).unwrap();
    transmit_uart_data(&uart, serialbuf);

    //let mode = readbuf[0];
    if (mode & 1 << 1) == 0 && (mode & 1 << 0) == 0  { 
        writeln!(serialbuf, "continuous mode set.").unwrap();
        transmit_uart_data(&uart, serialbuf);
    } else if (mode & 1 << 1) == 0 && (mode & 1 << 0) != 0 {
        writeln!(serialbuf, "single-measurement mode set.").unwrap();
        transmit_uart_data(&uart, serialbuf);
    } else {
        writeln!(serialbuf, "device in idle mode.").unwrap();
        transmit_uart_data(&uart, serialbuf);
    }

    // Set data output rate & number of samples & confirm
    // sample avg = 8; data output rate = 15Hz; normal measurement cfgn
    writebuf2 = [CFG_REG_A, 0b01110000];
    i2c_write(i2c0, HMC5883L_ADDR, &mut writebuf2).unwrap();
    writebuf = [CFG_REG_A];
    i2c_write(i2c0, HMC5883L_ADDR, &mut writebuf).unwrap();
    i2c_read(i2c0, HMC5883L_ADDR, &mut readbuf).unwrap();

    let cfg_a = readbuf[0];
    writeln!(serialbuf, "cfg reg a: 0b{:08b}", cfg_a).unwrap();
    transmit_uart_data(&uart, serialbuf);

    // Set Gain & confirm
    // gain = 1090 LSB/Gauss
    writebuf2 = [CFG_REG_B, 0b00100000];
    i2c_write(i2c0, HMC5883L_ADDR, &mut writebuf2).unwrap();

    writebuf = [CFG_REG_B];
    i2c_write(i2c0, HMC5883L_ADDR, &mut writebuf).unwrap();
    i2c_read(i2c0, HMC5883L_ADDR, &mut readbuf).unwrap();

    let cfg_b = readbuf[0];
    writeln!(serialbuf, "cfg reg b: 0b{:08b}", cfg_b).unwrap();
    transmit_uart_data(&uart, serialbuf);

    if (cfg_a == 0b01110000) && (cfg_b == 0b00100000)   { 
        writeln!(serialbuf, "cfg regs set.").unwrap();
        transmit_uart_data(&uart, serialbuf);
    }
}
Enter fullscreen mode Exit fullscreen mode

Create another function get_magnetic_heading which returns the heading. We shall sample a number of readings to get a more accurate reading.

fn get_magnetic_heading(i2c0: &mut I2C0) -> f32 { 
    // Read raw data from compass.
    // Capture 50 heading samples and return the average
    let mut count = 0.;
    let samples = 50.;
    let mut headings = 0.;

    while count != samples {
        // Point to the address of DATA_OUTPUT_X_MSB_R
        let mut writebuf = [DATA_OUTPUT_X_MSB_R];
        i2c_write(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(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;

        // North-Clockwise convention for getting the heading
        let mut heading = (y as f32 + 185.).atan2(x as f32 + 75.);

        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;

        headings += heading;

        count += 1.;
    }

    headings/samples
}
Enter fullscreen mode Exit fullscreen mode

Under the auto arm in the application code we can add the code below to print the magnetic heading.

heading = get_magnetic_heading(i2c.as_mut().unwrap());
writeln!(&mut serialbuf, "heading: {}", heading.round()).unwrap();
transmit_uart_data(uart_data.as_mut().unwrap(), serialbuf);
Enter fullscreen mode Exit fullscreen mode

This will print out the current heading of our mock dish during operation.

Results

Organizing all the code thus far we'll have this final copy.

Loading and running the program in the Pico, we can now be able to see the magnetic heading of our system.

compass-app-results

In the next part we look at how we can actuate the PTZ kit so that it aligns with a chosen satellite.

Image of AssemblyAI tool

Transforming Interviews into Publishable Stories with AssemblyAI

Insightview is a modern web application that streamlines the interview workflow for journalists. By leveraging AssemblyAI's LeMUR and Universal-2 technology, it transforms raw interview recordings into structured, actionable content, dramatically reducing the time from recording to publication.

Key Features:
🎥 Audio/video file upload with real-time preview
🗣️ Advanced transcription with speaker identification
⭐ Automatic highlight extraction of key moments
✍️ AI-powered article draft generation
📤 Export interview's subtitles in VTT format

Read full post

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay