Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use mdBook anchors #221

Merged
merged 2 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions advanced/button-interrupt/examples/solution_led.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ fn main() -> Result<()> {
esp_idf_svc::sys::link_patches();

let peripherals = Peripherals::take()?;
// ANCHOR: led
let mut led = WS2812RMT::new(peripherals.pins.gpio2, peripherals.rmt.channel0)?;
// ANCHOR_END: led

// Configures the button
let mut button = PinDriver::input(peripherals.pins.gpio9)?;
Expand All @@ -33,6 +35,7 @@ fn main() -> Result<()> {
})?;
}

// ANCHOR: loop
loop {
// Enable interrupt and wait for new notificaton
button.enable_interrupt()?;
Expand All @@ -41,9 +44,11 @@ fn main() -> Result<()> {
// Generates random rgb values and sets them in the led.
random_light(&mut led);
}
// ANCHOR_END: loop
}

#[allow(unused)]
// ANCHOR: random_light
fn random_light(led: &mut WS2812RMT) {
let mut color = RGB8::new(0, 0, 0);
unsafe {
Expand All @@ -56,3 +61,4 @@ fn random_light(led: &mut WS2812RMT) {

led.set_pixel(color).unwrap();
}
// ANCHOR_END: random_light
10 changes: 10 additions & 0 deletions advanced/i2c-driver/src/icm42670p_solution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use embedded_hal::blocking::i2c;

/// ICM42670P device driver.
/// Datasheet: https://invensense.tdk.com/wp-content/uploads/2021/07/DS-000451-ICM-42670-P-v1.0.pdf
// ANCHOR: struct
#[derive(Debug)]
pub struct ICM42670P<I2C> {
// The concrete I²C device implementation.
Expand All @@ -12,17 +13,21 @@ pub struct ICM42670P<I2C> {
// Device address
address: DeviceAddr,
}
// ANCHOR_END: struct

// See Table 3.3.2 in Documentation
/// Contains the possible variants of the devices addesses as binary numbers.
#[derive(Debug, Clone, Copy, PartialEq)]
// ANCHOR: device_addr
pub enum DeviceAddr {
/// 0x68
AD0 = 0b110_1000,
/// 0x69
AD1 = 0b110_1001,
}
// ANCHOR_END: device_addr

// ANCHOR: impl
impl<I2C, E> ICM42670P<I2C>
where
I2C: i2c::WriteRead<Error = E> + i2c::Write<Error = E>,
Expand All @@ -31,6 +36,7 @@ where
pub fn new(i2c: I2C, address: DeviceAddr) -> Result<Self, E> {
Ok(Self { i2c, address })
}
// ANCHOR_END: impl

/// Returns the device's ID `0x67
//(if it doesn't, something is amiss)
Expand All @@ -39,6 +45,7 @@ where
self.read_register(Register::WhoAmI)
}

// ANCHOR: read_write
/// Writes into a register
// This method is not public as it is only needed inside this file.
#[allow(unused)]
Expand All @@ -56,10 +63,12 @@ where
.write_read(self.address as u8, &[register.address()], &mut data)?;
Ok(u8::from_le_bytes(data))
}
// ANCHOR_END: read_write
}

// See Table 14.1 in documentation
/// This enum represents the device's registers
// ANCHOR: register
#[derive(Clone, Copy)]
pub enum Register {
WhoAmI = 0x75,
Expand All @@ -70,3 +79,4 @@ impl Register {
*self as u8
}
}
// ANCHOR_END: register
4 changes: 4 additions & 0 deletions advanced/i2c-sensor-reading/examples/part_2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ use esp_idf_svc::hal::{
peripherals::Peripherals,
prelude::*,
};
// ANCHOR: include
use icm42670::{Address, Icm42670, PowerMode as imuPowerMode};
// ANCHOR_END: include
// ANCHOR: shared_bus
use shared_bus::BusManagerSimple;
// ANCHOR_END: shared_bus
use shtcx::{self, PowerMode as shtPowerMode};

// Goals of this exercise:
Expand Down
13 changes: 12 additions & 1 deletion book/src/02_0_preparations.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,23 @@ This chapter contains information about the course material, the required hardwa

We use Icons to mark different kinds of information in the book:
* ✅ Call for action.
* ⚠️ Warnings, details that require special attention.
* ⚠️ Warnings, and details that require special attention.
* 🔎 Knowledge that dives deeper into a subject but which you aren't required to understand, proceeding.
* 💡 Hints that might help you during the exercises

> Example note: Notes like this one contain helpful information

## Code Annotations

In some Rust files, you can find some anchor comments:
```rust,ignore
// ANCHOR: test
let foo = 1;
...
// ANCHOR_END: test
```
Anchor comments can be ignored, they are only used to introduce those parts of code in this book. See [`mdBook` documentation](https://rust-lang.github.io/mdBook/format/mdbook.html#including-portions-of-a-file)

## Required Hardware

- [Rust ESP Board](https://github.com/esp-rs/esp-rust-board): available on Mouser, Aliexpress. [Full list of vendors](https://github.com/esp-rs/esp-rust-board#where-to-buy).
Expand Down
2 changes: 1 addition & 1 deletion book/src/02_2_software.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ to compile the binaries for the Espressif target. Flashing binaries from contain

✅ Install [`Docker`](https://docs.docker.com/get-docker/) for your operating system.

✅ Get the docker image: There are 2 ways of getting the Docker image:
✅ Get the Docker image: There are 2 ways of getting the Docker image:
- Build the Docker image from the `Dockerfile`:
```console
docker image build --tag rust-std-training --file .devcontainer/Dockerfile .
Expand Down
8 changes: 4 additions & 4 deletions book/src/03_2_cargo_generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ More information on generating projects can be found in the [Writing Your Own Ap
>
✅ Install `cargo-generate`:

```shell
```console
cargo install cargo-generate
```

✅ Change to the `intro` directory and run `cargo generate` with the [`esp-idf` template](https://github.com/esp-rs/esp-idf-template):

```shell
```console
cd intro
cargo generate esp-rs/esp-idf-template cargo
```
Expand Down Expand Up @@ -59,14 +59,14 @@ channel = "nightly-2023-11-14" # change this line

✅ Run your project by using the following command out of the `hello-world` directory.

```shell
```console
cd hello-world
cargo run
```

✅ The last lines of your output should look like this:

```shell
```console
(...)
I (268) cpu_start: Starting scheduler.
Hello, world!
Expand Down
3 changes: 1 addition & 2 deletions book/src/03_3_2_http_client.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ The `get` function uses [as_ref()](https://doc.rust-lang.org/std/convert/trait.A


```rust
let request = client.get(url.as_ref())?;
let response = request.submit()?;
{{#include ../../intro/http-client/examples/http_client.rs:request}}
```

A successful response has [a status code in the 2xx range](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). Followed by the raw html of the website.
Expand Down
8 changes: 2 additions & 6 deletions book/src/03_3_3_https_client.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@ You will now make changes to your HTTP client files so that it also works for en

`intro/http-client/examples/http_client.rs` contains the solution. You can run it with the following command:

```shell
```console
cargo run --example https_client
```

Create a custom client configuration to use an `esp_idf_svc::http::client::EspHttpConnection` which enables the use of these certificates and uses default values for everything else:

```rust
let connection = EspHttpConnection::new(&Configuration {
use_global_ca_store: true,
crt_bundle_attach: Some(esp_idf_sys::esp_crt_bundle_attach),
..Default::default()
}
{{#include ../../intro/http-client/examples/https_client.rs:connection}}
```

✅ Initialize your HTTP client with this new configuration and verify HTTPS works by downloading from an `https` resource, e.g. `https://espressif.com/`. the download will show as raw HTML in the terminal output.
Expand Down
6 changes: 3 additions & 3 deletions book/src/04_3_1_i2c.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ HUM: [local humidity] %
Using a bus manager, implement the second sensor. Read out its values and print the values from both sensors.


Continue with your own solution from part one. Alternatively you can start with the provided partial solution of Part 1: `i2c-sensor-reading/examples/part_1.rs`.
Continue with your own solution from part one. Alternatively, you can start with the provided partial solution of Part 1: `i2c-sensor-reading/examples/part_1.rs`.

`i2c-sensor-reading/examples/part_2.rs` contains a working solution of Part 2. You can consult it if you need help, by running:

Expand All @@ -84,7 +84,7 @@ cargo run --example part_2
✅ Import the driver crate for the ICM42670p.

```rust
use icm42670::{Address, Icm42670, PowerMode as imuPowerMode};
{{#include ../../advanced/i2c-sensor-reading/examples/part_2.rs:include}}
```

✅ Create an instance of the sensor.
Expand All @@ -101,7 +101,7 @@ This is an ownership issue. Every place in memory needs to be owned by something
✅ Import the bus manager crate.

```rust
use shared_bus::BusManagerSimple;
{{#include ../../advanced/i2c-sensor-reading/examples/part_2.rs:shared_bus}}
```

✅ Create an instance of a simple bus manager. Make two proxies and use them instead of the original I²C instance to pass to the sensors.
Expand Down
55 changes: 7 additions & 48 deletions book/src/04_3_2_i2c.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,21 @@ We're not going to write an entire driver, merely the first step: the `hello wor
To use a peripheral sensor first you must get an instance of it. The sensor is represented as a struct that contains both its device address, and an object representing the I²C bus itself. This is done using traits defined in the [`embedded-hal`](https://docs.rs/embedded-hal/latest/embedded_hal/) crate. The struct is public as it needs to be accessible from outside this crate, but its fields are private.

```rust
#[derive(Debug)]
pub struct ICM42670P<I2C> {
/// The concrete I²C device implementation.
i2c: I2C,

/// Device address
address: DeviceAddr,
}

// ...
{{#include ../../advanced/i2c-driver/src/icm42670p_solution.rs:struct}}
```

We add an `impl` block that will contain all the methods that can be used on the sensor instance. It also defines the Error Handling. In this block, we also implement an instantiating method. Methods can also be public or private. This method needs to be accessible from outside, so it's labelled `pub`. Note that written this way, the sensor instance takes ownership of the I²C bus.

```rust
impl<I2C, E>ICM42670P<I2C>
where
I2C: i2c::WriteRead<Error = E> + i2c::Write<Error = E>,
{
/// Create a new instance of the ICM42670P.
pub fn new(i2c: I2C, address: DeviceAddr) -> Result<Self, E> {
Ok(Self{ i2c, address })
}
{{#include ../../advanced/i2c-driver/src/icm42670p_solution.rs:impl}}
// ...
```
### Device Address

- The device's addresses are available in the code:

```rust
AD0 = 0b110_1000, // or 0x68
AD1 = 0b110_1001, // or 0x69
{{#include ../../advanced/i2c-driver/src/icm42670p_solution.rs:device_addr}}
```

- This I²C device has two possible addresses - `0x68` and `0x69`.
Expand All @@ -56,16 +39,7 @@ More information is available in the [datasheet, section 9.3](https://invensense
The sensor's registers are represented as enums. Each variant has the register's address as value. The type `Register` implements a method that exposes the variant's address.

```rust
#[derive(Clone, Copy)]
pub enum Register {
WhoAmI = 0x75,
}

impl Register {
fn address(&self) -> u8 {
*self as u8
}
}
{{#include ../../advanced/i2c-driver/src/icm42670p_solution.rs:register}}

```

Expand All @@ -74,24 +48,9 @@ impl Register {
We define a _read_ and a _write_ method, based on methods provided by the `embedded-hal` crate. They serve as helpers for more specific methods and as an abstraction that is adapted to a sensor with 8-bit registers. Note how the `read_register()` method is based on a `write_read()` method. The reason for this lies in the characteristics of the I²C protocol: We first need to write a command over the I²C bus to specify which register we want to read from. Helper methods can remain private as they don't need to be accessible from outside this crate.

```rust
impl<I2C, E>ICM42670P<I2C>
where
I2C: i2c::WriteRead<Error = E> + i2c::Write<Error = E>,
{
//...
fn write_register(&mut self, register: Register, value: u8) -> Result<(), E> {
let byte = value as u8;
self.i2c
.write(self.address as u8, &[register.address(), byte])
}

fn read_register(&mut self, register: Register) -> Result<u8, E> {
let mut data = [0];
self.i2c
.write_read(self.address as u8, &[register.address()], &mut data)?;
Ok(u8::from_le_bytes(data))
}
}
{{#include ../../advanced/i2c-driver/src/icm42670p_solution.rs:impl}}
// ...
{{#include ../../advanced/i2c-driver/src/icm42670p_solution.rs:read_write}}
```

✅ Implement a public method that reads the `WhoAmI` register with the address `0x75`. Make use of the above `read_register()` method.
Expand Down
34 changes: 9 additions & 25 deletions book/src/04_4_3_interrupts.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

1. Initialize the LED peripheral and switch the LED on with an arbitrary value just to see that it works.
```rust
let mut led = WS2812RMT::new(peripherals.pins.gpio2, peripherals.rmt.channel0)?;
{{#include ../../advanced/button-interrupt/examples/solution_led.rs:led}}

led.set_pixel(RGB8::new(20, 0, 20)).unwrap(); // Remove this line after you tried it once
```
Expand Down Expand Up @@ -31,28 +31,12 @@

4. **Optional**: If you intend to reuse this code in another place, it makes sense to put it into its own function. This lets us explore, in detail, which parts of the code need to be in `unsafe` blocks.

```rust
// ...
loop {
// enable_interrupt should also be called after each received notification from non-ISR context
button.enable_interrupt()?;
notification.wait(esp_idf_svc::hal::delay::BLOCK);
println!("Button pressed!");
// Generates random rgb values and sets them in the led.
random_light(&mut led);
}
// ...
fn random_light(led: &mut WS2812RMT) {
let mut color = RGB8::new(0, 0, 0);
unsafe {
let r = esp_random() as u8;
let g = esp_random() as u8;
let b = esp_random() as u8;

color = RGB8::new(r, g, b);
}

led.set_pixel(color).unwrap();
}
```
```rust
// ...
{{#include ../../advanced/button-interrupt/examples/solution_led.rs:loop}}

// ...
{{#include ../../advanced/button-interrupt/examples/solution_led.rs:random_light}}

```

2 changes: 2 additions & 0 deletions intro/http-client/examples/http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ fn get(url: impl AsRef<str>) -> Result<()> {

// 3. Open a GET request to `url`
let headers = [("accept", "text/plain")];
// ANCHOR: request
let request = client.request(Method::Get, url.as_ref(), &headers)?;
// ANCHOR_END: request

// 4. Submit the request and check the status code of the response.
// Successful http status codes are in the 200..=299 range.
Expand Down
2 changes: 2 additions & 0 deletions intro/http-client/examples/https_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ fn main() -> Result<()> {

fn get(url: impl AsRef<str>) -> Result<()> {
// 1. Create a new EspHttpClient. (Check documentation)
// ANCHOR: connection
let connection = EspHttpConnection::new(&Configuration {
use_global_ca_store: true,
crt_bundle_attach: Some(esp_idf_svc::sys::esp_crt_bundle_attach),
..Default::default()
})?;
// ANCHOR_END: connection
let mut client = Client::wrap(connection);

// 2. Open a GET request to `url`
Expand Down
Loading