esp-rs
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    The Rust on ESP Book === # Introduction The goal of this book is to provide a comprehensive guide on using the [Rust Programming Language] with [Espressif] devices. [rust programming language]: https://www.rust-lang.org/ [espressif]: https://espressif.com/ Rust support for these devices is still a work in progress, and progress is being made rapidly. Because of this, parts of this documentation may be out of date or change dramatically between readings. For tools and libraries relating to Rust on ESP, please see the [esp-rs organization] on GitHub. This organization is managed by employees of Espressif as well as members of the community. [esp-rs organization]: https://github.com/esp-rs/ > **A Note on Device Support** > > The contents of this book apply to the ESP32 series of devices only; this > includes: > > - ESP32 Series > - ESP32 C-Series > - ESP32 S-Series > > The ESP8266 series is outside the scope of this book. Rust support for the > ESP8266 series is limited and is not being officially supported by Espressif. ## Who This Book is For This book is intended for people with some experience with Rust, and also assumes rudimentary knowledge of embedded development and electronics. For those without prior experience, we recommend first reading the [Assumptions and Prerequisites] and [Other Resources] sections to get up to speed. [assumptions and prerequisites]: #assumptions-and-prerequisites [other resources]: #other-resources ### Assumptions and Prerequisites - You are comfortable using the Rust Programming Language, and have written and run applications in a desktop environment. - You should be familiar with the idioms of the [2021 edition], as this book targets Rust 2021. - You are comfortable developing embedded systems in another language such as C or C++, and are familiar with concepts such as: - Cross-compilation - Common digital interfaces like `UART`, `SPI`, `I²C`, etc. - Memory-mapped peripherals - Interrupts [2021 edition]: https://doc.rust-lang.org/edition-guide/rust-2021/index.html ### Other Resources If you are unfamiliar or less experienced with anything mentioned above, or if you would just like more information about a particular topic mentioned in this book, you may find these resources helpful. | Resource | Description | | ------------------------------- | ------------------------------------------------------------------------------------ | | [The Rust Programming Language] | If you are not familiar with Rust we recommend reading this book first. | | [The Embedded Rust Book] | Here you can find several other resources provided by Rust's Embedded Working Group. | | [The Embedonomicon] | The nitty-gritty details when doing embedded programming in Rust. | | [Embedded Rust on Espressif] | Training material created in cooperation with [Ferrous Systems]. | [the rust programming language]: https://doc.rust-lang.org/book/ [the embedded rust book]: https://docs.rust-embedded.org/book/index.html [the embedonomicon]: https://docs.rust-embedded.org/embedonomicon/ [embedded rust on espressif]: https://espressif-trainings.ferrous-systems.com/ [ferrous systems]: https://ferrous-systems.com/ ### Translations This book is currently available in English only. Once the contents of the book stabilize somewhat, we plan on translating the book into additional languages. As translations become available, this section will be updated to include them. ## How to Use This Book This book generally assumes that you are reading it front-to-back; content covered in later chapters may not make sense without context from previous chapters. ## Contributing to This Book The work on this book is coordinated in [this repository]. [this repository]: https://github.com/esp-rs/book If you have trouble following the instructions in this book or find that some section of the book is not clear enough or hard to follow, then that's a bug, and it should be reported in [the issue tracker] of this book. [the issue tracker]: https://github.com/esp-rs/book/issues/ Pull requests fixing typos and adding new content are very welcome! ## Re-using This Material This book is distributed under the following licenses: - The code samples and freestanding Cargo projects contained within this book are licensed under the terms of both the [MIT License] and the [Apache License v2.0]. - The written prose, pictures, and diagrams contained within this book are licensed under the terms of the Creative Commons [CC-BY-SA v4.0] license. [mit license]: https://opensource.org/licenses/MIT [apache license v2.0]: http://www.apache.org/licenses/LICENSE-2.0 [cc-by-sa v4.0]: https://creativecommons.org/licenses/by-sa/4.0/legalcode TL;DR: If you want to use our text or images in your work, you need to: - Give the appropriate credit (i.e. mention this book on your slide, and provide a link to the relevant page) - Provide a link to the [CC-BY-SA v4.0] licence - Indicate if you have changed the material in any way, and make any changes to our material available under the same licence Please do let us know if you find this book useful! # Ecosystem Overview There are two approaches for using Rust on Espressif chips: 1. _With_ the full standard library available (`std`) 2. _Without_ the standard library available (`no_std`) Both approaches have their advantages and disadvantages, so you should make a decision based on your project's needs. This chapter contains an overview of the two approaches followed by a brief comparison between them. - [Using the Rust Standard Library (`std`)](./using-the-standard-library.html) - [Bare Metal (`no_std`)](./bare-metal.html) - [Comparing `std` and `no_std`](./comparing-std-and-no_std.html) The [esp-rs organization] on GitHub is home to a number of repositories related to running Rust on Espressif chips. Most of the required crates have their source code hosted here. > A note on the repository naming convention > In the [esp-rs organization] we use the folling wording: > - Repositories starting with `esp-idf-` are focused on `std` apporach. E.g. `esp-idf-hal` > - Repositories starting with `esp-` are focused on `no_std` apporach. E.g. `esp-hal` [esp-rs organization]: https://github.com/esp-rs/ ## Using the Rust Standard Library (`std`) Espressif provides a C-based development framework called [esp-idf] which has support for all Espressif chips starting with the ESP32; note that this framework does _not_ support the ESP8266. `esp-idf` in turn provides a [newlib] environment with enough functionality to build the Rust standard library (`std`) on top of it. This is the approach that is being taken to enable `std` support on ESP devices. ### Chip Support For applications targeting `std` to be built for ESP devices, two things are required: 1. LLVM/Clang support 2. Support for the device in `esp-idf` Refer to the table below to see if your chip is supported. | Chip | Supported? | | :------: | :--------: | | ESP32 | ✅ | | ESP32-C2 | _planned_ | | ESP32-C3 | ✅ | | ESP32-S2 | ✅ | | ESP32-S3 | ✅ | | ESP32-H2 | _planned_ | | ESP8266 | ❌ | Since `esp-idf` does not have support for the ESP8266, we unfortunately are unable to use these libraries with it. See the [Bare Metal] page for information on the `no_std` HAL for this chip. [bare metal]: ./bare-metal.md ### Standard Library Features The supported `std` features are as follows: - Threads - Mutexes and other synchronization primitives - Collections - Random number generation - Sockets In addition to the `std` features, there is an [embedded-svc] implementation for `esp-idf`, [esp-idf-svc], which adds extra support for services/modules not available in the standard library, including: - Wi-Fi management - NVS (non-volatile storage) - Networking services like `httpd` and `ping` In general, this approach should feel quite similar to developing for most normal PC environments. [esp-idf]: https://github.com/espressif/esp-idf [newlib]: https://sourceware.org/newlib/ [embedded-svc]: https://github.com/esp-rs/embedded-svc [esp-idf-svc]: https://github.com/esp-rs/esp-idf-svc ### Relevant `esp-rs` crates | Repository | Description | | --------------------- | ------------------------------------------------------------------------------------------------------------- | | [esp-rs/esp-idf-hal] | An implementation of the `embedded-hal` and other traits using the `esp-idf` framework. | | [esp-rs/esp-idf-svc] | An implementation of [embedded-svc] using `esp-idf` drivers. | | [esp-rs/esp-idf-sys] | Rust bindings to the `esp-idf` development framework. Gives raw (`unsafe`) access to drivers, Wi-Fi and more. | | [esp-rs/embedded-svc] | Abstraction traits for embedded services. (`WiFi`, `Network`, `Httpd`, `Logging`, etc.) | The aforementioned crates have interdependencies, and this relationship can be seen below. ```mermaid graph TD; esp-idf-hal --> esp-idf-sys & embedded-svc esp-idf-svc --> esp-idf-sys & esp-idf-hal & embedded-svc ``` [esp-rs/embedded-svc]: https://github.com/esp-rs/embedded-svc [esp-rs/esp-idf-svc]: https://github.com/esp-rs/esp-idf-svc [esp-rs/esp-idf-sys]: https://github.com/esp-rs/esp-idf-sys [esp-rs/esp-idf-hal]: https://github.com/esp-rs/esp-idf-hal ### FAQ #### I updated my `sdkconfig.defaults` file but it doesn't appear to have had any effect You must clean your project and rebuild for changes in the `sdkconfig.defaults` to take effect: ```shell,ignore cargo clean cargo build ``` #### The documentation for the crates mentioned on this page is out of date or missing Due to the [resource limits] imposed by [docs.rs], internet access is blocked while building documentation and as such we are unable to build the documentation for `esp-idf-sys` or any crate depending on it. Instead, we are building the documentation and hosting it ourselves on GitHub Pages: - [`esp-idf-hal` Documentation] - [`esp-idf-svc` Documentation] - [`esp-idf-sys` Documentation] [resource limits]: https://docs.rs/about/builds#hitting-resource-limits [docs.rs]: https://docs.rs [`esp-idf-hal` documentation]: https://esp-rs.github.io/esp-idf-hal/esp_idf_hal/ [`esp-idf-svc` documentation]: https://esp-rs.github.io/esp-idf-svc/esp_idf_svc/ [`esp-idf-sys` documentation]: https://esp-rs.github.io/esp-idf-sys/esp_idf_sys/ #### \*\*\*ERROR\*\*\* A stack overflow in task main has been detected. If the second-stage bootloader reports this error, you likely need to increase the stack size for the main task. This can be accomplished by adding the following to the `sdkconfig.defaults` file: ```ignore CONFIG_ESP_MAIN_TASK_STACK_SIZE=7000 ``` In this example, we are allocating 7kB for the main task's stack. #### How can I completely disable the watchdog timer(s)? Add to your `sdkconfig.defaults` file: ```ignore CONFIG_INT_WDT=n CONFIG_ESP_TASK_WDT=n ``` Recall that you must clean your project before rebuilding when modifying these configuration files. ## Bare Metal (`no_std`) Using `no_std` may be more familiar to embedded Rust developers; it does not use `std` (the Rust standard library) but instead uses a subset, the `core` library. The [official Rust embedded book] has a [great section on this]. It's important to note that in general a `no_std` crate can always compile in `std` environment but the inverse is not true. Therefore, when creating crates it's worth keeping in mind if it needs the standard library to function. [great section on this]: https://docs.rust-embedded.org/book/intro/no-std.html [official rust embedded book]: https://docs.rust-embedded.org/ ### Hardware Abstraction Layers Previously, the primary focus of `no_std` development was the ESP32 and (to a lesser extent) the ESP8266. Now there is a renewed effort to implement `no_std` support for the entire lineup of Espressif devices from the ESP32 and newer. These new HALs can be found in the [esp-rs/esp-hal] repository. There is also some level of support for Wi-Fi and Bluetooth via [esp-rs/esp-wifi] for ESP32, ESP32-C3, ESP32-S2, and ESP32-S3. [esp-rs/esp-hal]: https://github.com/esp-rs/esp-hal [esp-rs/esp-wifi]: https://github.com/esp-rs/esp-wifi ### Chip Support Chip support for `no_std` requires LLVM/Clang support just like for `std`. However, this has no dependency on `esp-idf`. In addition to compiler support, it's necessary to have peripheral access crates (PAC) and hardware abstraction layers (HAL) for your desired chip. Refer to the table below to see if your chip is supported. Please note that the `no_std` HALs are still in the early phases of development, so not all peripherals have had drivers implemented. | Chip | PAC | HAL | | :------: | :-------: | :-------: | | ESP32 | ✅ | ✅ | | ESP32-C2 | ✅ | ✅ | | ESP32-C3 | ✅ | ✅ | | ESP32-C6 | _planned_ | _planned_ | | ESP32-S2 | ✅ | ✅ | | ESP32-S3 | ✅ | ✅ | | ESP32-H2 | _planned_ | _planned_ | | ESP8266 | ✅ | ✅ | ### Relevant `esp-rs` Crates | Repository | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------ | | [esp-rs/esp-pacs] | A monorepo containing PACs for each supported device. | | [esp-rs/esp-hal] | An implementation of the `embedded-hal` traits and more for the ESP32, ESP32-C2, ESP32-C3, ESP32-S2, and ESP32-S3. | | [esp-rs/esp8266-hal] | An implementation of the `embedded-hal` traits and more for the ESP8266. | | [esp-rs/esp-alloc] | A simple no_std heap allocator. | | [esp-rs/esp-println] | Provides `print!` and `println!`. | | [esp-rs/esp-backtrace] | Backtrace support for bare-metal applications. | | [esp-rs/esp-storage] | Implementation of embedded-storage traits to access unencrypted flash memory. | | [esp-rs/esp-wifi] | _WIP_: Experimental Wifi and Bluetooth LE support. | [esp-rs/esp-pacs]: https://github.com/esp-rs/esp-pacs [esp-rs/esp-hal]: https://github.com/esp-rs/esp-hal [esp-rs/esp8266-hal]: https://github.com/esp-rs/esp8266-hal [esp-rs/esp-alloc]: https://github.com/esp-rs/esp-alloc [esp-rs/esp-backtrace]: https://github.com/esp-rs/esp-backtrace [esp-rs/esp-storage]: https://github.com/esp-rs/esp-storage [esp-rs/esp-wifi]: https://github.com/esp-rs/esp-wifi [esp-rs/esp-println]: https://github.com/esp-rs/esp-println ## Comparing `std` and `no_std` There are several factors that must be considered when choosing between `std` ([esp-idf-hal]) and `no_std` (eg. [esp-hal]). As stated previously, each approach has its own unique set of advantages and disadvantages. While we can't decide for you, this section will hopefully allow you to make an educated decision. [esp-idf-hal]: https://github.com/esp-rs/esp-idf-hal [esp-hal]: https://github.com/esp-rs/esp-hal ### Application Runtimes In the case of applications (as opposed to libraries) the standard library provides a runtime that handles setting up stack overflow protection, spawning the main thread before an application's `main` function is invoked, and handling of command-line arguments. Applications targeting `no_std` will be responsible for initializing their own runtimes instead. Runtime initialization is generally handled by an external dependency, in our case the [riscv-rt] and [xtensa-lx-rt] libraries. You can refer to their READMEs and documentation for more information. One advantage of not including the default runtime is that you're able to write applications at a lower level. This is possible because the applications will have been linked against the `core` crate instead of `std`, which makes no assumptions about the system it is running on. As such, it's possible to write applications like bootloaders, firmware, or even operating system kernels using the `no_std` approach. [riscv-rt]: https://github.com/rust-embedded/riscv-rt [xtensa-lx-rt]: https://github.com/esp-rs/xtensa-lx-rt ### `#![no_main]` Another interesting property of `no_std` applications is that we cannot use Rust's default `main` function as our entry point. It makes certain assumptions that are not necessarily valid in an embedded context (for example, it expects that command-line arguments exist). Because of this, you will often see the `#![no_main]` attribute used to instruct the Rust compiler not to use the default entry point. Runtime crates will provide an `#[entry]` attribute which can be used to mark a diverging function as the application's entry point instead. For example, a minimal application might look something like this: ```rust,ignore #![no_std] #![no_main] use riscv_rt::entry; #[entry] fn main() -> ! { loop {} } ``` ### Panic Handlers In addition to specifying the application's entry point, for `no_std` we must also define a panic handler. The default panic behaviour relies on `std`, as it prints to standard output. You can define a panic handler manually using the `#[panic_handler]` attribute. Note that this function's signature _must_ match the example below. ```rust,ignore #![no_std] use core::panic::PanicInfo; #[panic_handler] fn panic(info: &PanicInfo) -> ! { // Your implementation goes here! } ``` Alternatively, there are a number of external dependencies which define various panic handlers for us. Some possible choices are [panic-halt], [panic-semihosting], or [panic-never]. These can be used simply by installing the relevant dependency, and then importing the crate: ```rust,ignore #![no_std] use panic_halt as _; ``` Probably the most convenient panic handler for `no_std` on `esp-hal` is [esp-backtrace] [panic-halt]: https://github.com/korken89/panic-halt [panic-semihosting]: https://github.com/rust-embedded/cortex-m/tree/master/panic-semihosting [panic-never]: https://github.com/japaric/panic-never [esp-backtrace]: https://github.com/esp-rs/esp-backtrace # Setting Up a Development Environment With an understanding of the ecosystem surrounding Rust on Espressif chips, we can move on to actual development. If you are not aware of the two possible development approaches or do not understand the differences between writing `std` and `no_std` applications, please first read the [Ecosystem Overview] chapter. Let's take a moment to discuss the Rust support for the different architectures of the Espressif chips and the different approaches in more detail. At this moment, Espressif SoCs are based on two different architectures: `RISC-V` and `Xtensa`. The support for those two architectures in the Rust programming language is very different. [Ecosystem Overview]: ../overview/index.md ## RISC-V targets The `RISC-V` architecture has support in the mainline Rust compiler so, the setup is relatively simple. There are two ways of proceeding with the installation: - Using the official Rust tools - Using [`espup`, a tool that will be covered later] If you only want to use `RISC-V` targets, you can use the official Rust tools, for this approach we need [`rustup`] installed, and a [Rust nightly toolchain] with the `rust-src` [component]. We can install a nightly toolchain with the `rust-src` component via: ```bash rustup toolchain install nightly --component rust-src ``` These are the two recommended targets for most Espressif `RISC-V` chips: - For bare-metal (`no_std`) applications: `riscv32imc-unknown-none-elf` - For applications that require `std`: `riscv32imc-esp-espidf` > #### A note in RISC-V `no_std` Rust targets. > > There are [different flavors of RISC-V 32 target in Rust] covering the different [RISC-V extensions]. The bare-metal targets can be installed by running: ```bash rustup target add riscv32imc-unknown-none-elf ``` For `std` applications, the `riscv32imc-esp-espidf` target is currently [Tier 3] and does not have pre-built objects distributed through `rustup`, therefore, it does not need to be installed as the `no_std` targets. Furthermore, `std` projects, also require: - The `-Z build-std` [unstable cargo feature], this [unstable cargo feature] can also be added to `.cargo/config.toml` of your project. Our [template projects], which we will later discuss, already take care of this. - [`LLVM`] installed. - [`ldproxy`] installed. - ESP-IDF (this will be installed by automatically by [`esp-idf-sys`]). At this point, you should be ready to build Rust applications for all the Espressif chips based on `RISC-V` architecture. [`espup`, a tool that will be covered later]: #espup [`rustup`]: https://rustup.rs/ [Rust nightly toolchain]: https://rust-lang.github.io/rustup/concepts/channels.html#working-with-nightly-rust [component]: https://rust-lang.github.io/rustup/concepts/components.html [template projects]: ../writing-your-own-application/generate-project-from-template.md [unstable cargo feature]: https://doc.rust-lang.org/cargo/reference/unstable.html [`LLVM`]: https://llvm.org/ [different flavors of RISC-V 32 target in Rust]: https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-2 [RISC-V extensions]: https://en.wikichip.org/wiki/risc-v/standard_extensions [Tier 3]: https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-3 [`esp-idf-sys`]: https://github.com/esp-rs/esp-idf-sys ## Xtensa targets To this day, there is no `Xtensa` support in the mainline Rust compiler. For this reason, we maintain the [esp-rs/rust] fork that adds support for our `Xtensa` targets. The main reason for `Xtensa` not being supported on Rust mainline is because `LLVM` does not support Xtensa targets. Therefore, we also maintain a fork of `LLVM` with support for Espressif `Xtensa` targets in [espressif/llvm-project]. > #### A note in upstreaming our forks. > > We are trying to upstream the changes in our `LLVM` and Rust forks. > The first step is to upstream the `LLVM` project, this is already in progress > and you can see the status at this [tracking issue]. > If our `LLVM` changes are accepted in `LLVM` mainline, we will proceed with trying > to upstream the Rust compiler changes. Another consequence of `LLVM` not supporting our `Xtensa` targets is that we need to provide our own linker. In other words, we'll need to install a [GCC toolchain] to use it as our linker. The forked compiler can coexist with the standard Rust compiler, so it is possible to have both installed on your system. The forked compiler is invoked when using the `esp` [channel] instead of the defaults, `stable` or `nightly`. Since the installation in this scenario is slightly complex, we have created a tool called `espup` to make it easier. [esp-rs/rust]: https://github.com/esp-rs/rust [espressif/llvm-project]: https://github.com/espressif/llvm-project [GCC toolchain]: https://github.com/espressif/crosstool-NG/ [tracking issue]: https://github.com/espressif/llvm-project/issues/4 [channel]: https://rust-lang.github.io/rustup/concepts/channels.html ### espup [esp-rs/espup] is a tool for installing and maintaining the necessary ecosystem to develop applications in Rust for Espressif SoC's (both `Xtensa` and `RISC-V` targets). `espup` takes care of installing the proper Rust compiler (our fork in case of `Xtensa` targets and the `nightly` toolchain with the necessary target for `RISC-V` targets), `LLVM` toolchain, `GCC` toolchains, `rustup`, and many other things. For more details, [see Usage section of the `espup` Readme]. To install `espup`, use the following command: ```sh cargo install espup ``` You can also directly download pre-compiled [release binaries] or use [cargo-binstall]. Once that `espup` is installed, you can simply run: ```sh espup install ``` This will install all the necessary tools to develop Rust applications for all supported ESP targets. `espup` will create an export file, by default `$HOME/export-esp.sh` on Unix systems and `%USERPROFILE%\export-esp.ps1` on Windows. This file contains some environment variables required to build projects. Make sure to source this file in every terminal before building any application: ```sh # Unix . $HOME/export-esp.sh # Windows %USERPROFILE%\export-esp.ps1 ``` [esp-rs/espup]: https://github.com/esp-rs/espup [see Usage section of the `espup` Readme]: https://github.com/esp-rs/espup#usage [release binaries]: https://github.com/esp-rs/espup/releases [cargo-binstall]: https://github.com/cargo-bins/cargo-binstall ### Other installation methods - Using [esp-rs/rust-build] installation scripts. This was the recommended way in the past, but now the installation scripts are feature frozen and all new features will only be included in `espup`. See the repository README for instructions. - Building the Rust compiler with `Xtensa` support from source. This process is computationally expensive and can take one or more hours to complete depending on your system. It is not recommended unless there is a major reason to go for this approach. See instructions in the [Installing from Source section of the esp-rs/rust repository]. [esp-rs/rust-build]: https://github.com/esp-rs/rust-build [Installing from Source section of the esp-rs/rust repository]: https://github.com/esp-rs/rust#installing-from-source ## Rust with `std` runtime Regardless of the target architecture, if you want to build a project using the [`std` approach], you will also need: - [ESP-IDF]: Espressif IoT Development Framework as it's used as our hosted environment. - This is handled by [`esp-idf-sys`] (a crate that all `std` projects need to use) by default. See [ESP-IDF installation note] for details on how ESP-IDF can be installed. - [`ldproxy`] crate: Simple tool to forward linker arguments given to [`ldproxy`] to the actual linker executable. The crate can be found in the [esp-rs/embuild] repository. To install it, use the following command: - `cargo install ldproxy` [ESP-IDF]: https://github.com/espressif/esp-idf [`std` approach]: ../overview/using-the-standard-library.md [`ldproxy`]: https://github.com/esp-rs/embuild/tree/master/ldproxy [esp-rs/embuild]: https://github.com/esp-rs/embuild [ESP-IDF installation note]: https://github.com/esp-rs/espup#esp-idf-instalation ## Using Containers As an alternative to installing the environment directly on your local system, it's also possible to run it inside a container. A number of container runtimes are available, and which one you should use depends on your operating system. Some popular options are: - [Docker] (non-commercial use only without a license) - [Podman] - [Lima] Espressif provides the [idf-rust] container image which contains several tags (generated both for `linux/arm64` and `linux/amd64`) for every Rust release: - For `std` applications, the following naming convention is applied: `<chip>_<esp-idf-version>_<rust-toolchain-version>`. For example, [`esp32s3_v4.4_1.64.0.0`] contains the ecosystem for developing `std` applications based on [ESP-IDF release/v4.4] for `ESP32-S3` with the `1.64.0.0` Rust toolchain. - For `no_std` applications, the naming convention is: `<chip>_<rust-toolchain-version>`. For example, [`esp32_1.64.0.0`] contains the ecosystem for developing `non_std` applications for `ESP32` with the `1.64.0.0` Rust toolchain. There is an `all` variant of `<chip>` that contains the environment required for all the ESP targets, and a `latest` variant of `<rust-toolchain-version>` that contains the latest Xtensa Rust toolchain. [Docker]: https://www.docker.com/ [Podman]: https://podman.io/ [Lima]: https://github.com/lima-vm/lima [idf-rust]: https://hub.docker.com/r/espressif/idf-rust/tags [`esp32s3_v4.4_1.64.0.0`]: https://hub.docker.com/layers/espressif/idf-rust/esp32s3_v4.4_1.64.0.0/images/sha256-6fa1e98d770e3edc67cbd565893aa04e5573024b1e3e373fae50907435e841e4?context=explore [ESP-IDF release/v4.4]: https://github.com/espressif/esp-idf/tree/release/v4.4 [`esp32_1.64.0.0`]: https://hub.docker.com/layers/espressif/idf-rust/esp32_1.64.0.0/images/sha256-cc026ff9278a876f171d48978988e131940c07659485937a37cf750c44b28dfd?context=explore # Tooling Now that we have our required dependencies installed, we will cover some of the tools that will make our life developing Rust applications for ESP targets a lot easier. ## Text Editors and IDEs While an often contentious subject, using the right development environment can make a significant impact on your productivity with a given programming language. Below can be found a curated list of what we feel are the best options. #### Visual Studio Code One of the more common development environments is Microsoft's [Visual Studio Code] text editor along with the [Rust Analyzer] extension. Visual Studio Code is an open-source and cross-platform graphical text editor with a rich ecosystem of extensions. The [Rust Analyzer extension] provides an implementation of the [Language Server Protocol] for Rust and additionally includes features like autocompletion, go-to definition, and more. Visual Studio Code can be installed via most popular package managers, and installers are available on the official website. The [Rust Analyzer extension] can be installed in Visual Studio Code via the built-in extension manager. Alongside Rust Analyzer (RA), there are other extensions that might be very helpful: - [Even Better TOML] for editing TOML based configuration files - [crates] to help manage Rust dependencies [visual studio code]: https://code.visualstudio.com/ [rust analyzer]: https://rust-analyzer.github.io/ [Rust Analyzer extension]: https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer [language server protocol]: https://microsoft.github.io/language-server-protocol/ [Even Better TOML]: https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml [crates]: https://marketplace.visualstudio.com/items?itemName=serayuzgur.crates ###### Tips and Tricks If you are developing for a target that does not have `std` support Rust Analyzer can behave strangely, often reporting various errors. This can be resolved by creating a `.vscode/settings.json` file in your project and populating it with the following: ```json { "rust-analyzer.checkOnSave.allTargets": false } ``` If you are using a custom toolchain, as you would with Xtensa targets, you can provide some hints to `cargo` via the `rust-toolchain.toml` file to improve the user experience: ```toml [toolchain] channel = "esp" components = ["rustfmt", "rustc-dev"] targets = ["xtensa-esp32-none-elf"] ``` #### CLion [CLion] is a cross-platform IDE for C and C++ from [JetBrains]. [CLion]: https://www.jetbrains.com/clion/ [JetBrains]: https://www.jetbrains.com/ #### IntelliJ #### vim [vim] is a highly configurable text editor based on vi that also [supports Rust Analyzer]. [vim]: https://www.vim.org/ [supports Rust Analyzer]: https://rust-analyzer.github.io/manual.html##vimneovim ## espflash A serial flasher utility for ESP devices. Supports flashing _ESP32_, _ESP32-C2_, _ESP32-C3_, _ESP32-S2_, _ESP32-S3_, and _ESP8266_. The [esp-rs/espflash] repository contains two crates, `cargo-espflash` and `espflash`. You can find more information on both of these in their respective sections below. [esp-rs/espflash]: https://github.com/esp-rs/espflash #### cargo-espflash Provides a subcommand for `cargo` which handles cross-compilation and flashing. Note that this requires the unstable `build-std` cargo feature; for more information on this please refer to [the cargo documentation]. To install: ```bash cargo install cargo-espflash ``` This command must be run within a Cargo project, ie.) a directory containing a `Cargo.toml` file. For example, to build an example named 'blinky' in `release` mode, flash the resulting binary to a device, and then subsequently start a serial monitor: ```bash cargo espflash --example=blinky --release --monitor ``` For more information please see to the [cargo-espflash README]. [the cargo documentation]: https://doc.rust-lang.org/cargo/reference/unstable.html##build-std [cargo-espflash readme]: https://github.com/esp-rs/espflash/blob/master/cargo-espflash/README.md #### espflash Provides a standalone command-line application which flashes an ELF file to a device. To install: ```bash cargo install espflash ``` Assuming you have built an ELF binary by other means already, `espflash` can be used to download it to your device. For example, if you have built the `getting-started/blinky` example from [esp-idf] using `idf.py` you might run something like: ```bash espflash build/blinky ``` For more information please see to the [espflash README]. [esp-idf]: https://github.com/espressif/esp-idf [espflash readme]: https://github.com/esp-rs/espflash/blob/master/espflash/README.md ## espmonitor The [esp-rs/espmonitor] repository contains two crates, `cargo-espmonitor` and `espmonitor`. #### cargo-espmonitor ```bash cargo install cargo-espmonitor ``` #### espmonitor ```bash cargo install espmonitor ``` [esp-rs/espmonitor]: https://github.com/esp-rs/espmonitor ## Debugging Debugging Rust applications is also possible using different tools that will be covered in this chapter. ### probe-rs The probe-rs project is a set of tools to interact with embedded MCU's using various debug probes. It is similar to openOCD, PyOCD, Segger tools, etc. There is support for ARM & RISCV architectures along with a collection of tools, including but not limited to: - Debugger - GDB support. - CLI for interactive debugging. - VSCode extension. - RTT (Real Time Transfer) - Similar to app_trace component of IDF. - Flashing algorithms More info about probe-rs & how to set up a project can be found on the [probe.rs](https://probe.rs/) website. #### `USB-JTAG-SERIAL` peripheral for ESP32-C3 Starting from `probe-rs` v0.12, it is possible to flash and debug the ESP32-C3 with the builtin `USB-JTAG-SERIAL` peripheral, no need for any external hardware debugger. More info on configuring the interface can be found in the [official documentation]. #### Support for Espressif chips `probe-rs` currently only supports `ARM` & `RISC-V`, therefore this limits the number of Espressif chips that can be used at the moment. | Chip | Flashing | Debugging | | :------: | :------: | :-------: | | ESP32-C3 | ✅ | ⚠️ | **Note**: _Items marked with ⚠️ are currently work in progress, usable but expect bugs._ [official documentation]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-guides/jtag-debugging/configure-builtin-jtag.html #### Permissions - Linux On Linux, you may run into permission issues trying to interact with Espressif probes. Installing the following `udev` rules and reloading should fix that issue. ```udev ## Espressif dev kit FTDI ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="660", GROUP="plugdev", TAG+="uaccess" ## Espressif USB JTAG/serial debug unit ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1001", MODE="660", GROUP="plugdev", TAG+="uaccess" ## Espressif USB Bridge ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1002", MODE="660", GROUP="plugdev", TAG+="uaccess" ``` <!-- TODO: when probe-rs can actually debug at least a C3 with decent back traces etc, add a section here with an example config: see https://github.com/probe-rs/probe-rs/issues/877 --> ### OpenOCD Similar to [probe-rs](./probe-rs.md), OpenOCD does not have support for the Xtensa architecture. However, Espressif does maintain a fork of OpenOCD under [espressif/openocd-esp32](https://github.com/espressif/openocd-esp32) which has support for Espressif's chips. Instructions on how to install `openocd-esp32` for your platform can be found in [the Espressif documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-guides/jtag-debugging/index.html#setup-of-openocd). #### Setup for Espressif chips <!-- how to choose interface & chip --> Once installed, it's as simple as running `openocd` with the correct scripts. For chips with the builtin USB JTAG, there is normally a config that will work out of the box, for example on the ESP32-C3: ```ignore openocd -f board/esp32c3-builtin.cfg ``` For other configurations it may require specifying the chip and the interface separately, for example, ESP32 with a J-Link: ```ignore openocd -f interface/jlink.cfg -f target/esp32.cfg ``` ### Debugging in Visual Studio Code There is also a possibility to debug with graphical output directly in Visual Studio Code. ##### ESP32 ##### Hardware Setup ESP32 doesn't have a built-in JTAG interface so you have to connect an external JTAG adapter to the ESP32 board, for example, [ESP-Prog](https://docs.espressif.com/projects/espressif-esp-iot-solution/en/latest/hw-reference/ESP-Prog_guide.html) can be used. | ESP32 Pin | JTAG Signal | | :---------: | :---------: | | MTDO/GPIO15 | TDO | | MTDI/GPIO12 | TDI | | MTCK/GPIO13 | TCK | | MTMS/GPIO14 | TMS | | 3V3 | VJTAG | | GND | GND | **Note**: On Windows `USB Serial Converter A 0403 6010 00` driver should be WinUSB. ##### Set up VSCode 1. Install [Cortex-Debug](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug) extension for VScode. 2. Create the `.vscode/launch.json` file in the project tree you want to debug. [This](https://github.com/esp-rs/esp32-hal/blob/master/.vscode/launch.json) can be used as a template file. 3. Update **executable**, **svdFile**, **serverpath** paths, and **toolchainPrefix** field. ```jsonc { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { // more info at: https://github.com/Marus/cortex-debug/blob/master/package.json "name": "Attach", "type": "cortex-debug", "request": "attach", // attach instead of launch, because otherwise flash write is attempted, but fails "cwd": "${workspaceRoot}", "executable": "target/xtensa-esp32-none-elf/debug/.....", "servertype": "openocd", "interface": "jtag", "svdFile": "../../esp-pacs/esp32/svd/esp32.svd", "toolchainPrefix": "xtensa-esp32-elf", "openOCDPreConfigLaunchCommands": [ "set ESP_RTOS none" ], "serverpath": "C:/Espressif/tools/openocd-esp32/v0.11.0-esp32-20220411/openocd-esp32/bin/openocd.exe", "configFiles": [ "board/esp32-wrover-kit-3.3v.cfg" ], "overrideAttachCommands": [ "set remote hardware-watchpoint-limit 2", "mon halt", "flushregs" ], "overrideRestartCommands": [ "mon reset halt", "flushregs", "c", ] }, ] } ``` ##### ESP32-C3 Older versions with **revision < 3** **doesn't** have built-in JTAG interface. ESP32-C3 with **revision 3** **does** have a built-in JTAG interface and you don't have to connect an external device to be able to debug. To get the chip revision, run the `cargo espflash board-info` command. ###### Hardware Setup If your ESP32-C3's revision is lesser than 3, follow these instructions, if you have revision 3 you can jump to the **Set up VSCode** step. ESP32-C3 **revision 1** and **revision 2** don't have a built-in JTAG interface so you have to connect an external JTAG adapter to the ESP32-C3 board, for example, [ESP-Prog](https://docs.espressif.com/projects/espressif-esp-iot-solution/en/latest/hw-reference/ESP-Prog_guide.html) can be used. | ESP32-C3 Pin | JTAG Signal | | :----------: | :---------: | | MTDO/GPIO7 | TDO | | MTDI/GPIO5 | TDI | | MTCK/GPIO6 | TCK | | MTMS/GPIO4 | TMS | | 3V3 | VJTAG | | GND | GND | **Note**: On Windows `USB Serial Converter A 0403 6010 00` driver should be WinUSB. ##### Set up VSCode 1. Install [Cortex-Debug](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug) extension for VScode. 2. Create the `.vscode/launch.json` file in the project tree you want to debug. [This](https://github.com/esp-rs/esp32-hal/blob/master/.vscode/launch.json) can be used as a template file. 3. Update **executable**, **svdFile**, **serverpath** paths, and **toolchainPrefix** field. ```jsonc { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { // more info at: https://github.com/Marus/cortex-debug/blob/master/package.json "name": "Attach", "type": "cortex-debug", "request": "attach", // attach instead of launch, because otherwise flash write is attempted, but fails "cwd": "${workspaceRoot}", "executable": "target/riscv32imc-unknown-none-elf/debug/examples/usb_serial_jtag", // "servertype": "openocd", "interface": "jtag", "svdFile": "../../esp-pacs/esp32c3/svd/esp32c3.svd", "toolchainPrefix": "riscv32-esp-elf", "openOCDPreConfigLaunchCommands": [ "set ESP_RTOS none" ], "serverpath": "C:/Espressif/tools/openocd-esp32/v0.11.0-esp32-20220411/openocd-esp32/bin/openocd.exe", "configFiles": [ "board/esp32c3-builtin.cfg" ], "overrideAttachCommands": [ "set remote hardware-watchpoint-limit 2", "mon halt", "flushregs" ], "overrideRestartCommands": [ "mon reset halt", "flushregs", "c", ] }, ] } ``` ## Simulating Simulating projects can be very handy. It allows users to test projects using CI, try projects without having hardware available, and many other scenarios. At the moment, there are a few ways of simulating Rust projects on Espressif chips, all of them have some limitations, but it's quickly evolving and getting better every day. In this chapter, we will discuss the different ways of simulation that are available. ### Wokwi [Wokwi] is an online simulator that supports simulating Rust projects (both `std` and `no_std`) in ESP Chips, see [wokwi.com/rust] for a list of examples and a way to start new projects. Wokwi offers WiFi simulation, Virtual Logic Analyzer, and [GDB debugging] among many other features, see [Wokwi documentation] for more details. For ESP chips, there is a [table of simulation features that are currently supported]. #### Using wokwi-server [wokwi-server] is a CLI tool for launching a Wokwi simulation of your project. I.e., it allows you to build a project on your machine, or in a container, and simulate the resulting binary. [wokwi-server] also allows simulating your resulting binary on other Wokwi projects, with more hardware parts other than the chip itself. See the [corresponding section of the wokwi-server Readme] for detailed instructions. [Wokwi]: https://wokwi.com/ [wokwi.com/rust]: https://wokwi.com/rust [GDB debugging]: https://docs.wokwi.com/gdb-debugging [Wokwi documentation]: https://docs.wokwi.com/ [table of simulation features that are currently supported]: https://docs.wokwi.com/guides/esp32#simulation-features [wokwi-server]: https://github.com/MabezDev/wokwi-server [corresponding section of the wokwi-server Readme]: https://github.com/MabezDev/wokwi-server#simulating-your-binary-on-a-custom-wokwi-project ### QEMU Espressif maintains a fork of QEMU in [espressif/QEMU] with the necessary patches to make it work on Espressif chips. See the [QEMU wiki] for instructions on how to build QEMU and emulate projects with it. Once you have built QEMU, you should have `qemu-system-xtensa`. [espressif/QEMU]: https://github.com/espressif/qemu [QEMU wiki]: https://github.com/espressif/qemu/wiki #### Running our project using QEMU > *NOTE*: Only ESP32 is currently supported, so make sure you are compiling for `xtensa-esp32-espidf` target. For running our project in QEMU, we need a firmware/image with bootloader and partition table merged in it. We can use [`cargo-espflash`] to generate it: [`cargo-espflash`]: https://github.com/esp-rs/espflash/tree/main/cargo-espflash ```bash cargo espflash save-image --merge ESP32 <OUTFILE> --release ``` > If you prefer to use [`espflash`], you can achieve the same result by building the project first and then generating image: > ```bash > cargo build --release > espflash save-image --merge ESP32 <OUTFILE> target/xtensa-esp32-espidf/release/<NAME> > ``` [`espflash`]: https://github.com/esp-rs/espflash/tree/main/espflash Now, run the image in QEMU: ```sh /path/to/qemu-system-xtensa -nographic -machine esp32 -drive file=<OUTFILE>,if=mtd,format=raw ``` # Writing Your Own Application With the appropriate Rust compiler and toolchain installed, you're now ready to create an application. There are essentially two ways to do this: generating from a template or starting from scratch using only `cargo`. We strongly recomend starting projects from templates as it gives you a configured project saving all the time that would require to setup a project started from scratch with `cargo`. > If you are looking for inspiration, check the [Projects section of our Awesome ESP Rust repository] [Projects section of our Awesome ESP Rust repository]: https://github.com/esp-rs/awesome-esp-rust#projects ## Generating Projects from Templates We currently maintin two template repositories: - [esp-template] - `no_std` template. - [esp-idf-template] - `std` template. Both templates are based on [cargo-generate], a tool that allows you to create a new project based on some existing template. In our case [esp-idf-template] or [esp-template] can be used to generate an application with all the required configuration and dependencies. `cargo generate` can be installed by running: ```shell cargo install cargo-generate ``` When the `cargo generate` subcommand is invoked, you will be prompted to answer a number of questions regarding the target of your application. Upon completion of this process you will have a buildable project with all the correct configuration. The generated application can be built as normal using the appropriate toolchain and target simply by running `cargo build` when using either templates. Using `cargo run` will compile the project, flash it, and open a serial monitor with our chip. #### esp-idf-template When using the Rust standard library (`std`) you can use the [esp-idf-template] template, which will look something like: ```shell $ cargo generate --git https://github.com/esp-rs/esp-idf-template cargo 🤷 Project Name : esp-rust-app 🔧 Destination: /home/alice/esp-rust-app ... 🔧 Generating template ... ✔ 🤷 MCU · esp32 ✔ 🤷 Configure project to use Dev Containers (VS Code, GitHub Codespaces and Gitpod)? (beware: Dev Containers not available for esp-idf v4.3.2) · false ✔ 🤷 STD support · true ✔ 🤷 ESP-IDF native build version (v4.3.2 = previous stable, v4.4 = stable, mainline = UNSTABLE) · v4.4 [ 1/10] Done: .cargo/config.toml [ 2/10] Done: .cargo [ 3/10] Done: .gitignore [ 4/10] Done: .vscode [ 5/10] Done: Cargo.toml [ 6/10] Done: build.rs [ 7/10] Done: rust-toolchain.toml [ 8/10] Done: sdkconfig.defaults [ 9/10] Done: src/main.rs [10/10] Done: src 🔧 Moving generated files into: `/home/alice/esp-rust-app`... 💡 Initializing a fresh Git repository ✨ Done! New project created /home/alice/esp-rust-app ``` See [Understanding esp-idf-template] for more details on the template project. #### esp-template For bare-metal applications (`no_std`) you can instead use the [esp-template] template: ```shell cargo generate --git https://github.com/esp-rs/esp-template 🤷 Project Name : esp-rust-app 🔧 Destination: /home/alice/esp-rust-app ... 🔧 Generating template ... ✔ 🤷 Which MCU to target? · esp32c3 ✔ 🤷 Configure project to use Dev Containers (VS Code, GitHub Codespaces and Gitpod)? · false ✔ 🤷 Enable allocations via the esp-alloc crate? · false [ 1/11] Done: .cargo/config.toml [ 2/11] Done: .cargo [ 3/11] Done: .gitignore [ 4/11] Done: .vscode/settings.json [ 5/11] Done: .vscode [ 6/11] Done: Cargo.toml [ 7/11] Done: LICENSE-APACHE [ 8/11] Done: LICENSE-MIT [ 9/11] Done: rust-toolchain.toml [10/11] Done: src/main.rs [11/11] Done: src 🔧 Moving generated files into: `/home/alice/esp-rust-app`... ✨ Done! New project created /home/alice/esp-rust-app ``` See [Understanding esp-template] for more details on the template project. ###### Using Dev Containers in the templates Both template repositories have a prompt for Dev Containers support, when using Dev Containers in the templates it will add support for: - [VS Code Dev Containers] - [GitHub Codespaces] - [Gitpod] Dev Containers use the `idf-rust` container image that was explained in the [Using Container section of the Installing Rust chapter] and provide an environment ready to develop Rust applications for Espressif chips with no installation required. Dev Containers also have integration with [Wokwi simulator], to simulate the project, and allow flashing from the container using [web flash]. For more details about on Dev Containers, see [Dev Container section of the template Readme]. [cargo-generate]: https://github.com/cargo-generate/cargo-generate [esp-idf-template]: https://github.com/esp-rs/esp-idf-template [esp-template]: https://github.com/esp-rs/esp-template [VS Code Dev Containers]: https://code.visualstudio.com/docs/remote/containers##_quick-start-open-an-existing-folder-in-a-container [GitHub Codespaces]: https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace [Gitpod]: https://www.gitpod.io [Using Container section of the Installing Rust chapter]: ../installation/index.md##using-containers [Wokwi simulator]: https://wokwi.com/ [web flash]: https://github.com/bjoernQ/esp-web-flash-server [Dev Container section of the template Readme]: https://github.com/esp-rs/esp-template/tree/main/docs##dev-containers [Understanding esp-template]: ./no-std-applications/understanding-esp-template.md [Understanding esp-idf-template]: ./std-applications/understanding-esp-idf-template.md ## Writing no_std applications The goal of this chapter is to provide a getting-started guide on using the Rust programming language with Espressif SoCs and modules using [esp-hal]. > Note that there are several examples covering the use of specific peripherals under the examples folder of every SoC `esp-hal`. E.g. [`esp32c3-hal/examples`] Examples shown here usually apply to ESP32-C3 using the [ESP32-C3-DevKit-RUST-1] board. You can use any other ESP32, ESP32-C3, ESP32-S2, or ESP32-S3 development board but smaller code changes and configuration changes might be needed. Also, this section of the book will only cover working locally. I.e. we will be using our host machine to develop, not [devcontainers], so make sure you have the [ecosystem properly installed]. [esp-hal]: https://github.com/esp-rs/esp-hal [ESP32-C3-DevKit-RUST-1]: https://github.com/esp-rs/esp-rust-board [`esp32c3-hal/examples`]: https://github.com/esp-rs/esp-hal/tree/main/esp32c3-hal/examples [devcontainers]: ../generate-project-from-template.md##using-dev-containers-in-the-templates [ecosystem properly installed]: ../../installation/index.md ### Understanding esp-template Now that we know how to [generate a no_std project], let's inspect what the generated project contains and try to understand every part of it. #### Inspecting the generated Project When creating a project from [esp-template] using: - MCU: `esp32c3` - Devcontainer support: `false` - `esp-alloc` crate support: `flase` It should generate a file structure like this: ```text ├── .cargo │   └── config.toml ├── src │   └── main.rs ├── .vscode │ └── settings.json ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── rust-toolchain.toml ``` Before going further let's see what these files are for. - [.gitignore] - tells `git` which folders and files to ignore - [Cargo.toml] - the usual Cargo manifest declaring some meta-data and dependencies of the project - LICENSE-APACHE, LICENSE_MIT - those are the most common licenses used in the Rust ecosystem - if you want to apply a different license you can delete these files and change the license in `Cargo.toml` - [rust-toolchain.toml] - defines which Rust toolchain to use - depending on your target this will use `nightly` or `esp` - [.cargo/config.toml] - the Cargo configuration - this defines a few options to correctly build the project - also contains `runner = "espflash --monitor"` - this means you can just use `cargo run` to flash and monitor your code - .vscode/settings.json - settings for Visual Studio Code - if you are not using VSCode you can delete the whole folder - src/main.rs - the main source file of the newly created project - we will examine its content in the next section #### `main.rs` ```rust,ignore #![no_std] #![no_main] use esp32c3_hal::{clock::ClockControl, pac::Peripherals, prelude::*, timer::TimerGroup, Rtc}; use esp_backtrace as _; #[riscv_rt::entry] fn main() -> ! { let peripherals = Peripherals::take().unwrap(); let system = peripherals.SYSTEM.split(); let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); // Disable the RTC and TIMG watchdog timers let mut rtc = Rtc::new(peripherals.RTC_CNTL); let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); let mut wdt0 = timer_group0.wdt; let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); let mut wdt1 = timer_group1.wdt; rtc.swd.disable(); rtc.rwdt.disable(); wdt0.disable(); wdt1.disable(); loop {} } ``` That is quite a lot of code. Let's see what it is good for. - `#![no_std]` - this tells the Rust compiler that this code doesn't use `libstd` - `#![no_main]` - The `no_main` attribute says that this program won't use the standard main interface, which is tailored for command-line applications that receive arguments. Instead of the standard main, we'll use the entry attribute from the `riscv-rt` crate to define a custom entry point. In this program we have named the entry point `main`, but any other name could have been used. The entry point function must be a [diverging function]. I.e. it has the signature `fn foo() -> !`; this type indicates that the function never returns – which means that the program never terminates. - `use esp32c3_hal:{...}` - we need to bring in some types we are going to use - these are from `esp-hal` - `use esp_backtrace as _;` - since we are in a bare-metal environment we need a panic-handler that runs if a panic occurs in code - there are a few different crates you can use (e.g `panic-halt`) but `esp-backtrace` provides an implementation that prints the address of a backtrace - together with `espflash`/`espmonitor` these addresses can get decoded into source code locations - `let peripherals = Peripherals::take().unwrap();` - HAL drivers usually take ownership of peripherals accessed via the PAC - here we take all the peripherals from the PAC to pass them to the HAL drivers later - `let system = peripherals.SYSTEM.split();` - sometimes a peripheral (here the System peripheral) is coarse-grained and doesn't exactly fit the HAL drivers - so here we split the System peripheral into smaller pieces which get passed to the drivers - `let clocks = ClockControl::boot_defaults(system.clock_control).freeze();` - here we configure the system clocks - in this case, we are fine with the defaults - we freeze the clocks which means we cannot change them later - some drivers need a reference to the clocks to know how to calculate rates and durations - the next block of code instantiates some peripherals (namely RTC and the two timer groups) to disable the watchdog which is armed after boot - without that code, the SoC would reboot after some time - there is another way to prevent the reboot: [feeding](https://docs.rs/esp32c3-hal/0.2.0/esp32c3_hal/prelude/trait._embedded_hal_watchdog_Watchdog.html#tymethod.feed) the watchdog - `loop {}` - since our function is supposed to never return we just "do nothing" in a loop #### Running the Code Building and running the code is as easy as ```shell cargo run ``` This builds the code according to the configuration and executes [`espflash`] to flash the code to the board. Since our [`runner` configuration] also passes the `--monitor` argument to [`espflash`] we can see what the code is printing. > Make sure that you have [`espflash`] installed, otherwise this step will fail. To install [`espflash`]: > `cargo install espflash` You should see something similar to this: ```text Connecting... Chip type: ESP32-C3 (revision 3) Crystal frequency: 40MHz Flash size: 4MB Features: WiFi MAC address: 60:55:f9:c0:0e:ec App/part. size: 198752/4128768 bytes, 4.81% [00:00:00] ######################################## 12/12 segment 0x0 [00:00:00] ######################################## 1/1 segment 0x8000 [00:00:01] ######################################## 57/57 segment 0x10000 Flashing has completed! Commands: CTRL+R Reset chip CTRL+C Exit ESP-ROM:esp32c3-api1-20210207 Build:Feb 7 2021 rst:0x15 (USB_UART_CHIP_RESET),boot:0xc (SPI_FAST_FLASH_BOOT) Saved PC:0x4004c72e 0x4004c72e - _stack_start at ??:?? SPIWP:0xee mode:DIO, clock div:1 load:0x3fcd6100,len:0x172c load:0x403ce000,len:0x928 0x403ce000 - _erwtext at ??:?? load:0x403d0000,len:0x2ce0 0x403d0000 - _erwtext at ??:?? entry 0x403ce000 0x403ce000 - _erwtext at ??:?? I (24) boot: ESP-IDF v4.4-dev-2825-gb63ec47238 2nd stage bootloader I (24) boot: compile time 12:10:40 I (25) boot: chip revision: 3 I (28) boot_comm: chip revision: 3, min. bootloader chip revision: 0 I (35) boot.esp32c3: SPI Speed : 80MHz I (39) boot.esp32c3: SPI Mode : DIO I (44) boot.esp32c3: SPI Flash Size : 4MB I (49) boot: Enabling RNG early entropy source... I (54) boot: Partition Table: I (58) boot: ## Label Usage Type ST Offset Length I (65) boot: 0 nvs WiFi data 01 02 00009000 00006000 I (73) boot: 1 phy_init RF data 01 01 0000f000 00001000 I (80) boot: 2 factory factory app 00 00 00010000 003f0000 I (88) boot: End of partition table I (92) boot_comm: chip revision: 3, min. application chip revision: 0 I (99) esp_image: segment 0: paddr=00010020 vaddr=3c030020 size=04a6ch ( 19052) map I (110) esp_image: segment 1: paddr=00014a94 vaddr=40380000 size=00910h ( 2320) load I (116) esp_image: segment 2: paddr=000153ac vaddr=00000000 size=0ac6ch ( 44140) I (131) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=2081ch (133148) map I (152) boot: Loaded app from partition at offset 0x10000 ``` What you see here are messages from the first and second stage bootloader and then ... nothing. And that is exactly what the code is doing. You can reboot with `CTRL+R` or exit with `CTRL+C`. In the next chapter, we will add some more interesting output. [generate a no_std project]: ../generate-project-from-template.md#esp-template [esp-template]: https://github.com/esp-rs/esp-template [.gitignore]: https://git-scm.com/docs/gitignore [Cargo.toml]: https://doc.rust-lang.org/cargo/reference/manifest.html [rust-toolchain.toml]: https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file [.cargo/config.toml]: https://doc.rust-lang.org/cargo/reference/config.html [`espflash`]: https://github.com/esp-rs/espflash/tree/main/espflash [`runner` configuration]: https://doc.rust-lang.org/cargo/reference/config.html#targettriplerunner [diverging function]: https://doc.rust-lang.org/beta/rust-by-example/fn/diverging.html ### Hello World In the last chapter you flashed and run your first piece of code on the SoC - while that is already really exciting we can do better. Traditionally the first thing to run on a microcontroller is _blinky_. However, we will start with _Hello World_ here. #### Add a Dependency You can add a dependency by any of the following methods: - Editing `Cargo.toml` In `Cargo.toml` in the `[dependencies]` section add this line: ```toml esp-println = { version = "0.3.1", features = ["esp32c3"] } ``` - Using [`cargo add`] ```sh cargo add esp-println --features "esp32c3" ``` [`esp-println`] is an additional crate that calls ROM functions to print text that is shown by [`espflash`] (or any other serial monitor). We need to pass the feature `esp32c3` since that crate targets multiple SoCs and needs to know which one it is supposed to run on. > Note that there might be new versions by the time you are reading this, please check [crates.io]. #### Print Something In `main.rs` before the `loop {}` add this line ```rust,ignore esp_println::println!("Hello World"); ``` #### See Results Again run ```shell cargo run ``` You should see the text _Hello World_ printed! [`espflash`]: https://github.com/esp-rs/espflash [`esp-println`]: https://github.com/esp-rs/esp-println [crates.io]: https://crates.io/crates/esp-println [`cargo add`]: https://doc.rust-lang.org/cargo/commands/cargo-add.html ### Panic! When something goes terribly wrong in Rust there might occur a [panic]. Let's see what it looks like for us. In `main.rs` put this line somewhere, e.g. before our `println` ```rust,ignore panic!("This is a panic"); ``` Again run the code. You should see something like this ```text !! A panic occured in 'src\main.rs', at line 25, column 5 PanicInfo { payload: Any { .. }, message: Some( This is a panic, ), location: Location { file: "src\\main.rs", line: 25, col: 5, }, can_unwind: true, } Backtrace: 0x420019aa 0x420019aa - main at C:\tmp\getting-started\src\main.rs:25 0x4200014c 0x4200014c - _start_rust at ...\.cargo\registry\src\github.com-1ecc6299db9ec823\riscv-rt-0.9.0\src\lib.rs:389 ``` We see where the panic occured and we even see a backtrace! While in this example things are obvious, this will come handy in more complex code. Now try running the code compiled with release profile. ```shell cargo run --release ``` Now things are less pretty: ```text !! A panic occured in 'src\main.rs', at line 25, column 5 PanicInfo { payload: Any { .. }, message: Some( This is a panic, ), location: Location { file: "src\\main.rs", line: 25, col: 5, }, can_unwind: true, } Backtrace: 0x42000140 0x42000140 - _start_rust at ??:?? ``` We still see where the panic occured but the backtrace is less helpful now. That is because the compiler omitted debug information and optimized the code. But you might have noticed the difference in the size of the flashed binary. It went from 199056 bytes down to 86896 bytes! Please note that this is still huge for what we get. There are a lot of options to get the binary smaller which is beyond the scope of this book. <!-- (TODO: should we add a section about binary sizes?) --> Before going further remove the line causing the explicit panic. [panic]: https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html ### Blinky Let's see how to create the iconic _Blinky_. Change the code in `main.rs` to this ```rust,ignore #![no_std] #![no_main] use esp32c3_hal::{ clock::ClockControl, pac::Peripherals, prelude::*, timer::TimerGroup, Delay, Rtc, IO, }; use esp_backtrace as _; #[riscv_rt::entry] fn main() -> ! { let peripherals = Peripherals::take().unwrap(); let system = peripherals.SYSTEM.split(); let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); // Disable the RTC and TIMG watchdog timers let mut rtc = Rtc::new(peripherals.RTC_CNTL); let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); let mut wdt0 = timer_group0.wdt; let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); let mut wdt1 = timer_group1.wdt; rtc.swd.disable(); rtc.rwdt.disable(); wdt0.disable(); wdt1.disable(); esp_println::println!("Hello World"); // Set GPIO7 as an output, and set its state high initially. let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); let mut led = io.pins.gpio7.into_push_pull_output(); led.set_high().unwrap(); // Initialize the Delay peripheral, and use it to toggle the LED state in a // loop. let mut delay = Delay::new(&clocks); loop { led.toggle().unwrap(); delay.delay_ms(500u32); } } ``` We need two new types in scope: [`IO`] and [`Delay`] On [ESP32-C3-DevKit-RUST-1] there is a regular [LED connected to GPIO 7]. If you use another board consult the data-sheet. > Note that most of the development boards from Espressif today use an addressable LED which works differently and is beyond the scope of this book. In that case, you can also connect a regular LED to some of the free pins (and don't forget to add a resistor). Here we see that we can drive the pin `high`, `low`, or `toggle` it. We also see that the HAL offers a way to delay execution. [ESP32-C3-DevKit-RUST-1]: https://github.com/esp-rs/esp-rust-board [LED connected to GPIO 7]: https://github.com/esp-rs/esp-rust-board#pin-layout [`IO`]: https://docs.rs/esp32c3-hal/0.2.0/esp32c3_hal/gpio/struct.IO.html [`Delay`]: https://docs.rs/esp32c3-hal/0.2.0/esp32c3_hal/struct.Delay.html ### Detect a button press Most of the dev-boards have a button, in our case, we will use the one labeled [`BOOT` on `GPIO9`]. Let's see how to check the state of the button. ```rust,ignore #![no_std] #![no_main] use esp32c3_hal::{ clock::ClockControl, pac::Peripherals, prelude::*, timer::TimerGroup, Rtc, IO, }; use esp_backtrace as _; #[riscv_rt::entry] fn main() -> ! { let peripherals = Peripherals::take().unwrap(); let system = peripherals.SYSTEM.split(); let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); // Disable the RTC and TIMG watchdog timers let mut rtc = Rtc::new(peripherals.RTC_CNTL); let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); let mut wdt0 = timer_group0.wdt; let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); let mut wdt1 = timer_group1.wdt; rtc.swd.disable(); rtc.rwdt.disable(); wdt0.disable(); wdt1.disable(); // Set GPIO7 as an output, GPIO9 as input let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); let mut led = io.pins.gpio7.into_push_pull_output(); let button = io.pins.gpio9.into_pull_up_input(); loop { if button.is_high().unwrap() { led.set_high().unwrap(); } else { led.set_low().unwrap(); } } } ``` Now if the button is not pressed the LED is lit. If the button is pressed the LED is off. Similarly to turning a `GPIO` into an `output` we can turn it into an `input`. Then we can get the current state of the `input` pin with `is_high` and similar functions. [`BOOT` on `GPIO9`]: https://github.com/esp-rs/esp-rust-board#ios ### Detect a button press with interrupt [Interrupts] offer a mechanism by which the processor handles asynchronous events and fatal errors. Let's add the [`critical-section`] crate [(see instructions on how to add a dependency)], and change `main.rs` to look like this: ```rust,ignore #![no_std] #![no_main] use core::cell::RefCell; use critical_section::Mutex; use esp32c3_hal::{ clock::ClockControl, gpio::Gpio9, gpio_types::{Event, Input, Pin, PullUp}, interrupt, pac::{self, Peripherals}, prelude::*, timer::TimerGroup, Rtc, IO, }; use esp_backtrace as _; static BUTTON: Mutex<RefCell<Option<Gpio9<Input<PullUp>>>>> = Mutex::new(RefCell::new(None)); #[riscv_rt::entry] fn main() -> ! { let peripherals = Peripherals::take().unwrap(); let system = peripherals.SYSTEM.split(); let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); // Disable the RTC and TIMG watchdog timers let mut rtc = Rtc::new(peripherals.RTC_CNTL); let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); let mut wdt0 = timer_group0.wdt; let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); let mut wdt1 = timer_group1.wdt; rtc.swd.disable(); rtc.rwdt.disable(); wdt0.disable(); wdt1.disable(); // Set GPIO9 as input let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); let mut button = io.pins.gpio9.into_pull_up_input(); button.listen(Event::FallingEdge); // raise interrupt on falling edge critical_section::with(|cs| BUTTON.borrow_ref_mut(cs).replace(button)); interrupt::enable(pac::Interrupt::GPIO, interrupt::Priority::Priority3).unwrap(); loop {} } #[interrupt] fn GPIO() { critical_section::with(|cs| { esp_println::println!("GPIO interrupt"); BUTTON .borrow_ref_mut(cs) .as_mut() .unwrap() .clear_interrupt(); }); } ``` There are quite a lot of new things here. First thing is the `static BUTTON`. We need it since in the interrupt handler we have to clear the pending interrupt on the button and we somehow need to pass the button from main to the interrupt handler. Since an interrupt handler can't have arguments we need a static to get the button into the interrupt handler. We need the `Mutex` to make access to the button safe. > Please note that this is not the Mutex you might know from `libstd` but it's the Mutex from [`critical-section`] (and that's why we need to add it as a dependency). Then we need to call `listen` on the `output` pin to configure the peripheral to raise interrupts. We can raise interrupts for different events - here we want to raise the interrupt on the falling edge. In the next line we move our button into the `static BUTTON` for the interrupt handler to get hold of it. Last thing we need to do is actually enable the interrupt. First parameter here is the kind of interrupt we want. There are several [possible interrupts]. Second parameter is the priority of the interrupt. The interrupt handler is defined via the `#[interrupt]` macro. Here the name of the function must match the interrupt. [Interrupts]: https://docs.rust-embedded.org/book/start/interrupts.html [`critical-section`]: https://crates.io/crates/critical-section [(see instructions on how to add a dependency)]: ./hello-world.md#add-a-dependency [possible interrupts]: https://docs.rs/esp32c3/0.5.1/esp32c3/enum.Interrupt.html ## Writing std applications If you want to learn how to develop `std` application, there is a training developed alongside [Ferrous Systems]: - [Book of training] - [Repository of the training] The training is based on [ESP32-C3-DevKit-RUST-1]. You can use any other ESP32, ESP32-C3, ESP32-S2, or ESP32-S3 development board but code changes and configuration changes might be needed. The training is split into two parts: * [Introductory level examples]: * A basic hardware-check ([Source](https://github.com/ferrous-systems/espressif-trainings/tree/main/intro/hardware-check)) * An HTTP Client ([Source](https://github.com/ferrous-systems/espressif-trainings/tree/main/intro/http-client)) * An HTTP Server ([Source](https://github.com/ferrous-systems/espressif-trainings/tree/main/intro/http-server)) * An MQTT Client ([Source](https://github.com/ferrous-systems/espressif-trainings/tree/main/intro/mqtt)) * [Advanced level examples]: * Low-level GPIO * Interrupts in General * I2C Driver ([Source](https://github.com/ferrous-systems/espressif-trainings/tree/main/advanced/i2c-driver)) * I2C Sensor Reading ([Source](https://github.com/ferrous-systems/espressif-trainings/tree/main/advanced/i2c-sensor-reading)) * GPIO/Button Interrupts ([Source](https://github.com/ferrous-systems/espressif-trainings/tree/main/advanced/button-interrupt)) * Driving an RGB LED > Note that there are several examples covering the use of specific peripherals under the examples folder of `esp-idf-hal`. I.e. [`esp32-idf-hal/examples`]. [Ferrous Systems]: https://ferrous-systems.com/ [Book of training]: https://espressif-trainings.ferrous-systems.com/ [Repository of the training]: https://github.com/ferrous-systems/espressif-trainings [ESP32-C3-DevKit-RUST-1]: https://github.com/esp-rs/esp-rust-board [Introductory level examples]: https://github.com/ferrous-systems/espressif-trainings/tree/main/intro [Advanced level examples]: https://github.com/ferrous-systems/espressif-trainings/tree/main/advanced [`esp32-idf-hal/examples`]: https://github.com/esp-rs/esp-idf-hal/tree/master/examples ### Understanding esp-idf-template Now that we know how to [generate a std project], let's inspect what the generated project contains and try to understand every part of it. #### Inspecting the generated Project When creating a project from [esp-idf-template] using: - MCU: `esp32c3` - ESP-IDF version: `v4.4` - STD support: `true` - Devcontainer support: `false` It should generate a file structure like this: ```text ├── build.rs ├── .cargo │   └── config.toml ├── Cargo.toml ├── .gitignore ├── rust-toolchain.toml ├── sdkconfig.defaults └── src    └── main.rs ``` Before going further let's see what these files are for. - [.gitignore] - tells `git` which folders and files to ignore - [Cargo.toml] - the usual Cargo manifest declaring some meta-data and dependencies of the project - LICENSE-APACHE, LICENSE_MIT - those are the most common licenses used in the Rust ecosystem - if you want to apply a different license you can delete these files and change the license in `Cargo.toml` - [rust-toolchain.toml] - defines which Rust toolchain to use - depending on your target this will use `nightly` or `esp` - [.cargo/config.toml] - the Cargo configuration - contains our target - contains `runner = "espflash --monitor"` - this means you can just use `cargo run` to flash and monitor your code - contains the linker to use, in our case, [`ldproxy`] - contains the unstable `build-std` cargo feature enabled. - contains the `ESP-IDF-VERSION` envrionment variable that tells [`esp-idf-sys`] which ESP-IDF version the project will use. - src/main.rs - the main source file of the newly created project - we will examine its content in the next section - [build.rs] - propagates linker arguments for `ldproxy`. - [sdkconfig.defaults] - contains the overriden values from the ESP-IDF defaults. #### `main.rs` ```rust,ignore use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported fn main() { esp_idf_sys::link_patches(); println!("Hello, world!"); } ``` The first line its an import that defines the esp-idf entry-point when the root crate is a binary crate that defines a main function. Then, we have an usual main function with two lines on it: - A call to `esp_idf_sys::link_patches` function that makes sure that a few patches to the ESP-IDF which are implemented in Rust are linked to the final executable. - We print in our console the famous "Hello World!". #### Running the Code Building and running the code is as easy as ```shell cargo run ``` This builds the code according to the configuration and executes [`espflash`] to flash the code to the board. Since our [`runner` configuration] also passes the `--monitor` argument to [`espflash`] we can see what the code is printing. > Make sure that you have [`espflash`] installed, otherwise this step will fail. To install [`espflash`]: > `cargo install espflash` You should see something similar to this: ```text Connecting... Chip type: ESP32-C3 (revision 3) Crystal frequency: 40MHz Flash size: 4MB Features: WiFi MAC address: 60:55:f9:c0:39:7c App/part. size: 409728/4128768 bytes, 9.92% [00:00:00] ######################################## 12/12 segment 0x0 [00:00:00] ######################################## 1/1 segment 0x8000 [00:00:04] ######################################## 210/210 segment 0x10000 Flashing has completed! Commands: CTRL+R Reset chip CTRL+C Exit ESP-ROM:esp32c3-api1-20210207 Build:Feb 7 2021 rst:0x15 (USB_UART_CHIP_RESET),boot:0xc (SPI_FAST_FLASH_BOOT) Saved PC:0x4004c97e 0x4004c97e - chip726_phyrom_version_num at ??:?? SPIWP:0xee mode:DIO, clock div:1 load:0x3fcd6100,len:0x172c load:0x403ce000,len:0x928 0x403ce000 - _iram_text_end at ??:?? load:0x403d0000,len:0x2ce0 0x403d0000 - _iram_text_end at ??:?? entry 0x403ce000 0x403ce000 - _iram_text_end at ??:?? I (24) boot: ESP-IDF v4.4-dev-2825-gb63ec47238 2nd stage bootloader I (24) boot: compile time 12:10:40 I (24) boot: chip revision: 3 I (28) boot_comm: chip revision: 3, min. bootloader chip revision: 0 I (35) boot.esp32c3: SPI Speed : 80MHz I (39) boot.esp32c3: SPI Mode : DIO I (44) boot.esp32c3: SPI Flash Size : 4MB I (49) boot: Enabling RNG early entropy source... I (54) boot: Partition Table: I (58) boot: ## Label Usage Type ST Offset Length I (65) boot: 0 nvs WiFi data 01 02 00009000 00006000 I (73) boot: 1 phy_init RF data 01 01 0000f000 00001000 I (80) boot: 2 factory factory app 00 00 00010000 003f0000 I (88) boot: End of partition table I (92) boot_comm: chip revision: 3, min. application chip revision: 0 I (99) esp_image: segment 0: paddr=00010020 vaddr=3c050020 size=17640h ( 95808) map I (122) esp_image: segment 1: paddr=00027668 vaddr=3fc89c00 size=0146ch ( 5228) load I (123) esp_image: segment 2: paddr=00028adc vaddr=40380000 size=0753ch ( 30012) load I (133) esp_image: segment 3: paddr=00030020 vaddr=42000020 size=419d8h (268760) map I (176) esp_image: segment 4: paddr=00071a00 vaddr=4038753c size=02644h ( 9796) load I (178) esp_image: segment 5: paddr=0007404c vaddr=50000010 size=00010h ( 16) load I (185) boot: Loaded app from partition at offset 0x10000 I (188) boot: Disabling RNG early entropy source... I (205) cpu_start: Pro cpu up. I (213) cpu_start: Pro cpu start user code I (213) cpu_start: cpu freq: 160000000 I (213) cpu_start: Application information: I (216) cpu_start: Project name: libespidf I (221) cpu_start: App version: 1 I (226) cpu_start: Compile time: Nov 3 2022 13:16:23 I (232) cpu_start: ELF file SHA256: 0000000000000000... I (238) cpu_start: ESP-IDF: 755ce10-dirty I (243) heap_init: Initializing. RAM available for dynamic allocation: I (250) heap_init: At 3FC8BF90 len 00050780 (321 KiB): DRAM I (257) heap_init: At 3FCDC710 len 00002950 (10 KiB): STACK/DRAM I (263) heap_init: At 50000020 len 00001FE0 (7 KiB): RTCRAM I (270) spi_flash: detected chip: generic I (274) spi_flash: flash io: dio I (279) sleep: Configure to isolate all GPIO pins in sleep state I (285) sleep: Enable automatic switching of GPIO sleep configuration I (292) cpu_start: Starting scheduler. Hello, world! ``` As you can see, there are messages from the first and second stage bootloader and then, our "Hello, world!" its printed. You can reboot with `CTRL+R` or exit with `CTRL+C`. [.gitignore]: https://git-scm.com/docs/gitignore [Cargo.toml]: https://doc.rust-lang.org/cargo/reference/manifest.html [rust-toolchain.toml]: https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file [.cargo/config.toml]: https://doc.rust-lang.org/cargo/reference/config.html [generate a std project]: ../generate-project-from-template.md#esp-idf-template [esp-idf-template]: https://github.com/esp-rs/esp-template [`esp-idf-sys`]: https://github.com/esp-rs/esp-idf-sys [`ldproxy`]: https://github.com/esp-rs/embuild/tree/master/ldproxy [build.rs]: https://doc.rust-lang.org/cargo/reference/build-scripts.html [sdkconfig.defaults]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html#custom-sdkconfig-defaults [`espflash`]: https://github.com/esp-rs/espflash/tree/main/espflash [`runner` configuration]: https://doc.rust-lang.org/cargo/reference/config.html#targettriplerunner # Troubleshooting Here, we will present a list of common errors that may appear when building a project alongside the reason and a solution to them. ## Environment variable LIBCLANG_PATH not set ```text thread 'main' panicked at 'Unable to find libclang: "couldn't find any valid shared libraries matching: ['libclang.so', 'libclang-*.so', 'libclang.so.*', 'libclang-*.so.*'], set the `LIBCLANG_PATH` environment variable to a path where one of these files can be found (invalid: [])"', /home/esp/.cargo/registry/src/github.com-1ecc6299db9ec823/bindgen-0.60.1/src/lib.rs:2172:31 ``` We need `libclang` for [`bindgen`] to generate the Rust bindings to the ESP-IDF C headers. Make sure the environment variable `LIBCLANG_PATH` is set and pointing to our custom fork of LLVM: - Unix: ```sh export $HOME/.espressif/tools/xtensa-esp32-elf-clang/<clang_version>-<host_triple>/esp-clang/lib ``` - Windows: ```powershell $Env:LIBCLANG_PATH="%USERPROFILE%/.espressif/tools/xtensa-esp32-elf-clang/<clang_version>-<host_triple>/esp-clang/bin/libclang.dll" $Env:PATH+=";%USERPROFILE%/.espressif/tools/xtensa-esp32-elf-clang/<clang_version>-<host_triple>/esp-clang/bin/" ``` [`bindgen`]: https://github.com/rust-lang/rust-bindgen ## Missing `ldproxy` ```sh error: linker `ldproxy` not found | = note: No such file or directory (os error 2) ``` If you are trying to build a `std` application [`ldproxy`] must be installed. ```sh cargo install ldproxy ``` [`ldproxy`]: https://github.com/esp-rs/embuild/tree/master/ldproxy ## Using a wrong Rust toolchain ```text $ cargo build error: failed to run `rustc` to learn about target-specific information Caused by: process didn't exit successfully: `rustc - --crate-name ___ --print=file-names --target xtensa-esp32-espidf --crate-type bin --crate-type rlib --crate-type dylib --crate-type cdylib --crate-type staticlib --crate-type proc-macro --print=sysroot --print=cfg` (exit status: 1) --- stderr error: Error loading target specification: Could not find specification for target "xtensa-esp32-espidf". Run `rustc --print target-list` for a list of built-in targets ``` If you are encountering the previous error or a similar one, you are probably not using the proper Rust toolchain, remember that for Xtensa targets, you need to use Espressif Rust fork toolchain, there are several ways to do it: - A [toolchain override] shorthand used on the command-line: `cargo +esp`. - Set `RUSTUP_TOOLCHAIN` environment variable to `esp`. - Set a [directory override]: `rustup override set esp` - Add a [rust-toolchain.toml] file to you project: ```toml [toolchain] channel = "esp" ``` - Set `esp` as [default toolchain]. For more information on toolchain overriding, see the [Overrides chapter of The rustup book]. [toolchain override]: https://rust-lang.github.io/rustup/overrides.html#toolchain-override-shorthand [directory override]: https://rust-lang.github.io/rustup/overrides.html#directory-overrides [rust-toolchain.toml]: https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file [default toolchain]: https://rust-lang.github.io/rustup/overrides.html#default-toolchain [Overrides chapter of The rustup book]: https://rust-lang.github.io/rustup/overrides.html#overrides ## Windows ### Long path names When using Windows, you may encounter issues building a new project if using long path names. Follow these steps to substitute the path of your project: ```powershell subst r:\ <pathToYourProject> cd r:\ ``` ### Missing ABI ```powershell Compiling cc v1.0.69 error: linker `link.exe` not found | = note: The system cannot find the file specified. (os error 2) note: the msvc targets depend on the msvc linker but `link.exe` was not found note: please ensure that VS 2013, VS 2015, VS 2017 or VS 2019 was installed with the Visual C++ option error: could not compile `compiler_builtins` due to previous error warning: build failed, waiting for other jobs to finish... error: build failed ``` The reason for this error is that we are missing the MSVC C++, hence we are not meeting the [Compile-time Requirements], please install [Visual Studio 2013 (or later) or the Visual C++ Build Tools 2019]. For Visual Studio, make sure to check the "C++ tools" and "Windows 10 SDK" options. If using GNU ABI, install [MinGW/MSYS2 toolchain]. [Compile-time Requirements]: https://github.com/rust-lang/cc-rs#compile-time-requirements [Visual Studio 2013 (or later) or the Visual C++ Build Tools 2019]: https://rust-lang.github.io/rustup/installation/windows.html [MinGW/MSYS2 toolchain]: https://www.msys2.org/ ## Missing `libtinfo.so.5` ```text thread 'main' panicked at 'Unable to find libclang: "the `libclang` shared library at /home/user/.espressif/tools/xtensa-esp32-elf-clang/esp-15.0.0-20221014-x86_64-unknown-linux-gnu/esp-clang/lib/libclang.so.15.0.0 could not be o pened: libtinfo.so.5: cannot open shared object file: No such file or directory"', /home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/bindgen-0.60.1/src/lib.rs:2172:31 ``` Some of the LLVM 15 releases, `esp-15.0.0-20220922` and `esp-15.0.0-20221014`, require `libtinfo.so.5`. This dependency was removed in `esp-15.0.0-20221201` LLVM releases. If you are using any of the versions that require it, make sure `libtinf5` is installed: - Ubuntu/Debian: `sudo apt-get install libtinfo5` - Fedora: `sudo dnf install ncurses-compat-libs` - openSUSE: `sudo dnf install libncurses5` - Arch Linux: `sudo pacman -S ncurses5-compat-libs`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully