Embedded Rust Training for Espressif

repository·main·Indexed 20 days ago

https://github.com/esp-rs/std-training

A collection of training materials, examples, and a structured curriculum for Embedded Rust development on the ESP32-C3 microcontroller. The content is divided into introductory examples (Hardware Check, HTTP Client/Server, MQTT) and advanced examples (I2C Driver, SHTC3 and ICM42670P sensor reading, GPIO/Button Interrupts, and RGB LED). It includes common utility crates such as get-uuid, mqtt-messages, rgb-led, and wifi.

Tokens
22.9K
Snippets
71
Records
132
Agent score
73%

What's inside esp-rs-std-training

  1. Overview of the Advanced Workshop

    main

    The Advanced Workshop focuses on low-level embedded Rust development, specifically targeting hardware-proximate I/O. Unlike high-level abstraction training, this workshop covers manual configuration of hardware components, direct register manipulation, and navigating ownership and memory safety challenges in an embedded context.

    Key learning objectives include:

    • Manual pin configuration.
    • Direct register access and register discovery.
    • Handling button interrupts.
    • Reading sensor values via the I²C bus.
    • Developing custom hardware drivers.
  2. Overview of the Embedded Rust Training curriculum

    main

    The std-training repository provides a structured learning path for Embedded Rust on Espressif hardware. The curriculum is divided into several stages:

    1. Preparations: Setting up hardware, software, and the workshop repository.
    2. Intro Workshop: Focuses on high-level application development including project organization, generating new projects, HTTP/HTTPS clients, simple HTTP servers, and IoT using MQTT.
    3. Advanced Workshop: Focuses on low-level hardware interaction, including I2C (sensor reading and driver development) and Interrupt handling.
    4. Reference: A collection of reference materials.
  3. Overview of Introductory Examples

    main

    The intro directory contains introductory-level examples designed for Rust development on the ESP32-C3. These examples are structured to facilitate learning through practice: each example includes an exercise (an unfinished code state for you to complete) and a solution (the fully completed implementation).

    For structured teaching material to accompany these examples, refer to the book.

  4. Getting started with Embedded Rust Trainings for Espressif

    main

    This repository provides training material for learning Embedded Rust specifically using the Espressif ESP32-C3.

    To begin learning, it is recommended to follow the structured curriculum in the published book.

    The training is divided into several categories:

    Introductory Examples

    • Hardware Check: Basic verification of hardware functionality.
    • HTTP Client: Implementing a client to make HTTP requests.
    • HTTP Server: Implementing a server to handle HTTP requests.
    • MQTT Client: Implementing an MQTT client for messaging.

    Advanced Examples

    • I2C Driver: Low-level I2C implementation.
    • I2C Sensor Reading: Using I2C to read from sensors.
    • GPIO/Button Interrupts: Handling hardware interrupts via GPIO.
    • RGB LED: Driving an RGB LED (WS2812).
    • Low-level GPIO and Interrupts in General.

    Common Utility Crates

    Several helper crates are provided for common tasks:

    • get-uuid: Provides a compile-time generated UUID.
    • mqtt-messages: Helper functions for MQTT.
    • rgb-led: Support for WS2812 RGB LEDs.
    • wifi: Helper functions for WiFi connectivity.
  5. Understand the repository structure

    main

    The repository is organized into several directories based on the course level and shared resources:

    • advanced/: Code examples and exercises for the advanced course.
    • book/: Markdown source files for the training book.
    • common/: Code shared between both the introductory and advanced courses.
    • common/lib/: Support crates used by the workshop.
    • intro/: Code examples and exercises for the introduction course.
  6. Understand the scope of the Embedded Rust on Espressif training

    main

    This training material is divided into two distinct workshop levels:

    1. Introductory Workshop: Focuses on the basics of embedded development, including interacting with the outside world (reacting to commands and sending sensor data).
    2. Advanced Workshop: Dives into interrupt handling, low-level peripheral access, and writing custom drivers.

    Important Ecosystem Note: This training uses the std ecosystem, specifically the following crates:

    • esp-idf-hal
    • esp-idf-sys
    • esp-idf-svc

    These crates are community-maintained and are not officially supported by Espressif. If you require the officially supported approach, you should use the no_std approach with esp-hal.

  7. Understand the role of `unsafe {}` blocks in interrupt handling

    main

    Interrupt-driven code in embedded Rust frequently requires the use of unsafe {} blocks.

    In this context, unsafe does not necessarily mean the code is memory-unsafe; rather, it indicates that the Rust compiler cannot automatically verify the safety guarantees of the operations being performed. It is the programmer's responsibility to ensure memory safety.

    Common reasons for using unsafe in this domain include:

    • Accessing shared mutable state between the main thread and an interrupt handler.
    • Calling C bindings (as Rust cannot guarantee the safety of underlying C code).
  8. Understand the concept of Interrupts

    main

    An interrupt is a request for the processor to suspend its current execution to process a specific event in a timely manner. When an interrupt is accepted, the processor saves its current state and executes an interrupt handler function.

    In embedded systems, interrupts are typically triggered by hardware devices to signal state changes that require immediate attention, such as a button press.

    The Challenge in Embedded Rust

    Because interrupt handlers can be triggered at any arbitrary time, they present a specific memory management challenge: they require access to statically allocated mutable memory that is shared with the main execution loop. Ensuring this shared memory is always accessible and safe to mutate is a core concern when implementing interrupts in Rust.

  9. Understand Active High and Active Low logic

    main

    Digital signals exist in two states: high and low. The definition of which state is 'active' is arbitrary:

    • Active High: The logic level is considered active when voltage is applied to the pin.
    • Active Low: The logic level is considered active when the voltage is at the inactive state (typically ground).

    Note for Embedded Rust users: Rust abstractions typically represent the logic level rather than the raw voltage level. If you are using an active low pin to control an LED, you must set the pin to its inactive logic state to make the LED light up.

  10. Represent Sensor Registers using Enums

    main
    In the driver, sensor registers are represented as enums. Each variant in the enum corresponds to a specific register address. The Register type implements a method to expose the underlying address of the variant, allowing the driver to communicate with specific hardware locations.
  11. Represent and Access Sensor Registers

    main

    Sensor registers should be managed using an enum where each variant represents a specific register's address.

    For the ICM-42670P, you must implement:

    • A register enum containing the WhoAmI register.
    • A method on this enum to expose the register's address as a u8.

    When performing I²C reads, use the write_read pattern from embedded-hal. This is necessary because you must first write the register address you wish to access before the device can return the data from that register.

  12. How get-uuid generates stable identifiers

    main

    To overcome Cargo limitations and ensure the same UUID is used across different projects, get-uuid uses a build.rs script.

    When the build script runs, it checks for the existence of a uuid.toml file in the package. If it is missing, the script generates both uuid.toml and _uuid.rs, ensuring both files contain the exact same UUID. The uuid.toml file is provided so that non-Rust programs (such as a Python MQTT client) can access the same identifier used by the Rust code.