The Challenge

We'll use some fancy math to get the precise angle that the magnetic field forms with the X and Y axes of the magnetometer. This will allow us to figure out which LED is pointing north.

We'll use the atan2 function. This function returns an angle in the -PI to PI range. The graphic below shows how this angle is measured:

Although not explicitly shown, in this graph the X axis points to the right and the Y axis points up. Note that our coordinate system is rotated 180° from this.

Here's the starter code (in templates/compass.rs). theta, in radians, has already been computed. You need to pick which LED to turn on based on the value of theta.

#![deny(unsafe_code)] #![no_main] #![no_std] use cortex_m_rt::entry; use embedded_hal::delay::DelayNs; use panic_rtt_target as _; use rtt_target::rtt_init_print; // You'll find these useful ;-). use core::f32::consts::PI; use libm::{atan2f, floorf}; use microbit::{ display::blocking::Display, hal::{Timer, twim}, pac::twim0::frequency::FREQUENCY_A, }; use lsm303agr::{Lsm303agr, MagMode, MagOutputDataRate}; #[entry] fn main() -> ! { rtt_init_print!(); let board = microbit::Board::take().unwrap(); let i2c = { twim::Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100) }; let mut timer0 = Timer::new(board.TIMER0); let mut display = Display::new(board.display_pins); let mut sensor = Lsm303agr::new_with_i2c(i2c); sensor.init().unwrap(); sensor.set_mag_mode_and_odr( &mut timer0, MagMode::HighResolution, MagOutputDataRate::Hz10, ).unwrap(); let mut sensor = sensor.into_mag_continuous().ok().unwrap(); let mut leds = [[0u8; 5]; 5]; // Indexes of the 16 LEDs to be used in the display, and their // compass directions. #[rustfmt::skip] let indices = [ (2, 0) /* W */, (3, 0) /* W-SW */, (3, 1) /* SW */, (4, 1) /* S-SW */, (4, 2) /* S */, (4, 3) /* S-SE */, (3, 3) /* SE */, (3, 4) /* E-SE */, (2, 4) /* E */, (1, 4) /* E-NE */, (1, 3) /* NE */, (0, 3) /* N-NE */, (0, 2) /* N */, (0, 1) /* N-NW */, (1, 1) /* NW */, (1, 0) /* W-NW */, ]; loop { // Measure the magnetic field. let (x, y) = todo!(); // Get an angle between -180° and 180° from the x axis. let theta = atan2f(y as f32, x as f32); // Figure out what LED index to blink. let index = todo!(); // Blink the given LED. let (r, c) = indices[index]; leds[r][c] = 255u8; display.show(&mut timer0, leds, 50); leds[r][c] = 0u8; display.show(&mut timer0, leds, 50); } }

Suggestions/tips:

  • A whole circle rotation equals 360 degrees.
  • PI radians is equivalent to 180 degrees.
  • If theta is zero, which direction are you pointing at?
  • If theta is instead very close to zero, which direction are you pointing at?
  • If theta keeps increasing, at what value should you change the direction