Skip to content

Commit

Permalink
edited ch 10
Browse files Browse the repository at this point in the history
  • Loading branch information
BartMassey committed Jul 12, 2024
1 parent 9f7c1ad commit 3edb6df
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 126 deletions.
26 changes: 15 additions & 11 deletions mdbook/src/09-led-compass/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@ Magnetic fields have both a magnitude, measured in Gauss or Teslas, and a *direc
magnetometer on the micro:bit measures both the magnitude and the direction of an external magnetic
field, but it reports back the *decomposition* of said field along *its axes*.

The magnetometer has three axes associated to it. When the board is held flat with the LEDs facing
uupward and the logo facing forward, the X and Y axes span the plane that is the floor. The X axis
points to the top (logo) edge of the board. The Y axis points to the left edge. The Z axis points
"into the floor", so downwards: "upside down" since the chip is mounted on the back. This is a
"right-handed" coordinate system. It's all a bit confusing, since the reported field strengths are
differences from the magnetic field vector.

You should already be able to write a program that continuously prints the magnetometer
data on the RTT console from the [I2C chapter](../08-i2c/index.md). After you wrote that
program, locate where north is at your current location. Then line up your micro:bit with
that direction and observe how the sensor's measurements look.
The magnetometer has three axes associated with it. When the board is held flat with the LEDs facing
uupward and the logo facing forward, the X and Y axes span the plane that is the floor. The X axis
points to the left edge of the board. The Y axis points to the bottom (card connector) edge of the
board. The Z axis points "into the floor", so downwards: "upside down" since the chip is mounted on
the back. This is a "right-handed" coordinate system. It's all a bit confusing, since the reported
field strengths are components of the magnetic field vector.

<p align="center">
<img title="MB2 Axes" src="../assets/mb2-axes.jpg" width="500" />
</p>

You should already be able to write a program that continuously prints the magnetometer data on the
RTT console from the [I2C chapter](../08-i2c/index.md). After you write that program, locate where
north is at your current location. Then line up your micro:bit with that direction and observe how
the sensor's X and Y measurements look.

Now rotate the board 90 degrees while keeping it parallel to the ground. What X, Y and Z values do
you see this time? Then rotate it 90 degrees again. What values do you see?
Expand Down
4 changes: 2 additions & 2 deletions mdbook/src/10-punch-o-meter/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[build]
target = "thumbv7em-none-eabihf"

[target.'cfg(all(target_arch = "arm", target_os = "none"))']
[target.thumbv7em-none-eabihf]
runner = "probe-rs run --chip nRF52833_xxAA"
rustflags = [
"-C", "linker=rust-lld",
"-C", "link-arg=-Tlink.x",
]
9 changes: 7 additions & 2 deletions mdbook/src/10-punch-o-meter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ actually the maximum acceleration that you can reach because acceleration is wha
measure. Strength and acceleration are proportional though so it's a good approximation.

As we already know from previous chapters the accelerometer is built inside the LSM303AGR package.
And just like the magnetometer, it is accessible using the I2C bus. It also has the same coordinate
system as the magnetometer.
And just like the magnetometer, it is accessible using the
I2C bus.

The accelerometer also has the same coordinate system as the magnetometer. Here's a reminder:

<p align="center">
<img title="MB2 Axes" src="../assets/mb2-axes.jpg" width="500" />
</p>
37 changes: 37 additions & 0 deletions mdbook/src/10-punch-o-meter/examples/show-accel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#![deny(unsafe_code)]
#![no_main]
#![no_std]

use cortex_m_rt::entry;
use rtt_target::{rtt_init_print, rprintln};
use panic_rtt_target as _;

use microbit::{
hal::{Timer, twim},
pac::twim0::frequency::FREQUENCY_A,
};

use lsm303agr::{AccelMode, AccelOutputDataRate, Lsm303agr};

#[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 sensor = Lsm303agr::new_with_i2c(i2c);
sensor.init().unwrap();
sensor.set_accel_mode_and_odr(
&mut timer0,
AccelMode::HighResolution,
AccelOutputDataRate::Hz10,
).unwrap();
loop {
if sensor.accel_status().unwrap().xyz_new_data() {
let (x, y, z) = sensor.acceleration().unwrap().xyz_mg();
rprintln!("Acceleration: x {} y {} z {}", x, y, z);
}
}
}
23 changes: 13 additions & 10 deletions mdbook/src/10-punch-o-meter/gravity-is-up.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,28 @@ What's the first thing we'll do?

Perform a sanity check!

You should already be able to write a program that continuously prints the accelerometer
data on the RTT console from the [I2C chapter](../08-i2c/index.md). Do you observe something
interesting even when holding the board parallel to the floor with the LED side facing down?
You should already be able to write a program that continuously prints the accelerometer data on the
RTT console from the [I2C chapter](../08-i2c/index.md). Mine is in `examples/show-accel.rs`. Do you
observe something interesting even when holding the board parallel to the floor with the back side
facing up? (Remember that the accelerometer is mounted on the back of the board, so holding it
upside-down like this makes the Z axis point up.)

What you should see like this is that both the X and Y values are rather close to 0, while the
Z value is at around 1000. Which is weird because the board is not moving yet its acceleration is
non-zero. What's going on? This must be related to the gravity, right? Because the acceleration of
gravity is `1 g` (aha, `1 g` = 1000 from the accelerometer). But the gravity pulls objects downwards
so the acceleration along the Z axis should be negative not positive
What you should see when holding the board like this is that both the X and Y values are rather
close to 0, while the Z value is at around 1000. Which is weird: the board is not moving, yet its
acceleration is non-zero. What's going on? This must be related to the gravity, right? Because the
acceleration of gravity is `1 g` (aha, `1 g` = -1000 from the accelerometer). But the gravity pulls
objects downwards so the acceleration along the Z axis should be positive, not negative.

Did the program get the Z axis backwards? Nope, you can test rotating the board to align the gravity
to the X or Y axis but the acceleration measured by the accelerometer is always pointing up.

What happens here is that the accelerometer is measuring the *proper acceleration* of the board not
What happens here is that the accelerometer is measuring the *proper acceleration* of the board, not
the acceleration *you* are observing. This proper acceleration is the acceleration of the board as
seen from an observer that's in free fall. An observer that's in free fall is moving toward the
center of the Earth with an acceleration of `1g`; from its point of view the board is actually
moving upwards (away from the center of the Earth) with an acceleration of `1g`. And that's why the
proper acceleration is pointing up. This also means that if the board was in free fall, the
accelerometer would report a proper acceleration of zero. Please, don't try that at home.
accelerometer would report a proper acceleration of zero. Please, don't try that at home. Or do, if
you're willing to risk your MB2 by dropping it.

Yes, physics is hard. Let's move on.
91 changes: 4 additions & 87 deletions mdbook/src/10-punch-o-meter/my-solution.md
Original file line number Diff line number Diff line change
@@ -1,91 +1,8 @@
# My solution

``` rust
#![deny(unsafe_code)]
#![no_main]
#![no_std]

use cortex_m_rt::entry;
use rtt_target::{rtt_init_print, rprintln};
use panic_rtt_target as _;

use microbit::{
hal::twim,
pac::twim0::frequency::FREQUENCY_A,
};

use lsm303agr::{
AccelScale, AccelOutputDataRate, Lsm303agr,
};

use microbit::hal::timer::Timer;
use microbit::hal::prelude::*;
use nb::Error;

#[entry]
fn main() -> ! {
const THRESHOLD: f32 = 0.5;

rtt_init_print!();
let board = microbit::Board::take().unwrap();
Here's my solution (`src/main.rs`). Note that you can get quite high G values by rapping the edge of
your MB2 on a table. Note also that this can break the accelerometer, so probably don't?

let i2c = { twim::Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100) };

let mut countdown = Timer::new(board.TIMER0);
let mut delay = Timer::new(board.TIMER1);
let mut sensor = Lsm303agr::new_with_i2c(i2c);
sensor.init().unwrap();
sensor.set_accel_odr(AccelOutputDataRate::Hz50).unwrap();
// Allow the sensor to measure up to 16 G since human punches
// can actually be quite fast
sensor.set_accel_scale(AccelScale::G16).unwrap();

let mut max_g = 0.;
let mut measuring = false;

loop {
while !sensor.accel_status().unwrap().xyz_new_data {}
// x acceleration in g
let g_x = sensor.accel_data().unwrap().x as f32 / 1000.0;

if measuring {
// Check the status of our contdown
match countdown.wait() {
// countdown isn't done yet
Err(Error::WouldBlock) => {
if g_x > max_g {
max_g = g_x;
}
},
// Countdown is done
Ok(_) => {
// Report max value
rprintln!("Max acceleration: {}g", max_g);

// Reset
max_g = 0.;
measuring = false;
},
// Since the nrf52 and nrf51 HAL have Void as an error type
// this path cannot occur, as Void is an empty type
Err(Error::Other(_)) => {
unreachable!()
}
}
} else {
// If acceleration goes above a threshold, we start measuring
if g_x > THRESHOLD {
rprintln!("START!");

measuring = true;
max_g = g_x;
// The documentation notes that the timer works at a frequency
// of 1 Mhz, so in order to wait for 1 second we have to
// set it to 1_000_000 ticks.
countdown.start(1_000_000_u32);
}
}
delay.delay_ms(20_u8);
}
}
``` rust
{{#include src/main.rs}}
```
65 changes: 61 additions & 4 deletions mdbook/src/10-punch-o-meter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,74 @@
#![no_main]
#![no_std]

use cortex_m::asm::wfi;
const TICKS_PER_SEC: u32 = 400;
const THRESHOLD: f32 = 1.5;

use cortex_m::asm::nop;
use cortex_m_rt::entry;
use rtt_target::{rtt_init_print, rprintln};
use panic_rtt_target as _;
use rtt_target::rtt_init_print;

use microbit::{
hal::{Timer, twim},
pac::twim0::frequency::FREQUENCY_A,
};

use lsm303agr::{AccelMode, AccelOutputDataRate, AccelScale, Lsm303agr};

#[entry]
fn main() -> ! {
rtt_init_print!();
let _board = microbit::Board::take().unwrap();
let board = microbit::Board::take().unwrap();

let i2c = { twim::Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100) };

let mut delay = Timer::new(board.TIMER0);
let mut sensor = Lsm303agr::new_with_i2c(i2c);
sensor.init().unwrap();
sensor.set_accel_mode_and_odr(
&mut delay,
AccelMode::Normal,
AccelOutputDataRate::Hz400,
).unwrap();
// Allow the sensor to measure up to 16 G since human punches
// can actually be quite fast
sensor.set_accel_scale(AccelScale::G16).unwrap();

let mut max_g = 0.;
let mut countdown_ticks = None;

loop {
wfi();
while !sensor.accel_status().unwrap().xyz_new_data() {
nop();
}
// x acceleration in g
let (x, _, _) = sensor.acceleration().unwrap().xyz_mg();
let g_x = x as f32 / 1000.0;

if let Some(ticks) = countdown_ticks {
if ticks > 0 {
// countdown isn't done yet
if g_x > max_g {
max_g = g_x;
}
countdown_ticks = Some(ticks - 1);
} else {
// Countdown is done: report max value
rprintln!("Max acceleration: {}g", max_g);

// Reset
max_g = 0.;
countdown_ticks = None;
}
} else {
// If acceleration goes above a threshold, we start measuring
if g_x > THRESHOLD {
rprintln!("START!");

max_g = g_x;
countdown_ticks = Some(TICKS_PER_SEC);
}
}
}
}
14 changes: 4 additions & 10 deletions mdbook/src/10-punch-o-meter/the-challenge.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,7 @@ Here's what the punch-o-meter must do:

Give it a try and let me know how hard you can punch `;-)`.

> **NOTE** There are two additional APIs that should be useful for this task we haven't discussed yet.
> First the [`set_accel_scale`] one which you need to measure high g values.
> Secondly the [`Countdown`] trait from `embedded_hal`. If you decide to use this to keep your measurement
> intervals you will have to pattern match on the [`nb::Result`] type instead of using the `block!` macro
we have seen in previous chapters.


[`set_accel_scale`]: https://docs.rs/lsm303agr/0.2.2/lsm303agr/struct.Lsm303agr.html#method.set_accel_scale
[`Countdown`]: https://docs.rs/embedded-hal/0.2.6/embedded_hal/timer/trait.CountDown.html
[`nb::Result`]: https://docs.rs/nb/1.0.0/nb/type.Result.html
> **NOTE** There is an additional API that should be useful for this task that we haven't
> discussed yet: the [`set_accel_scale`] one which you need to measure high g values.
>
> [`set_accel_scale`]: https://docs.rs/lsm303agr/1.1.0/lsm303agr/struct.Lsm303agr.html#method.set_accel_scale
Binary file added mdbook/src/assets/mb2-axes.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 3edb6df

Please sign in to comment.