# Rust bindings for ESP-IDF
(Espressif's IoT Development Framework)
[![CI](https://github.com/esp-rs/esp-idf-sys/actions/workflows/ci.yml/badge.svg)](https://github.com/esp-rs/esp-idf-sys/actions/workflows/ci.yml)
[![Documentation](https://img.shields.io/badge/docs-esp--rs-brightgreen)](https://esp-rs.github.io/esp-idf-sys/esp_idf_sys/index.html)
The ESP-IDF API in Rust, with support for each ESP chip (ESP32, ESP32S2, ESP32S3, ESP32C3, etc.) based on the Rust target.
For more information, check out:
* The [Rust on ESP Book](https://esp-rs.github.io/book/)
* The [esp-idf-template](https://github.com/esp-rs/esp-idf-template) project
* The [esp-idf-svc](https://github.com/esp-rs/esp-idf-svc) project
* The [esp-idf-hal](https://github.com/esp-rs/esp-idf-hal) project
* The [Rust for Xtensa toolchain](https://github.com/esp-rs/rust-build)
* The [Rust-with-STD demo](https://github.com/ivmarkov/rust-esp32-std-demo) project
**Table of contents**
- [Build](#build)
- [Features](#features)
- [sdkconfig](#sdkconfig)
- [Build configuration](#build-configuration)
- [Extra esp-idf components](#extra-esp-idf-components)
- [Conditional compilation](#conditional-compilation)
- [More info](#more-info)
## Build
- To build this crate, please follow all the build requirements specified in the [ESP-IDF Rust Hello World template crate](https://github.com/esp-rs/esp-idf-template)
- The relevant Espressif toolchain, as well as the ESP-IDF framework itself, are all automatically
downloaded during the build:
- With feature `native` (default): utilizing native ESP-IDF tooling via the [embuild](https://github.com/ivmarkov/embuild) crate or
- With feature `pio` (backup): utilizing [PlatformIO](https://platformio.org/) (also via the [embuild](https://github.com/ivmarkov/embuild) crate).
- Check the [ESP-IDF Rust Hello World template crate](https://github.com/esp-rs/esp-idf-template) for a "Hello, world!" Rust template demonstrating how to use and build this crate.
- Check the [demo](https://github.com/ivmarkov/rust-esp32-std-demo) crate for a more comprehensive example in terms of capabilities.
## Features
- ### `native`
This is the default feature for downloading all tools and building the ESP-IDF framework using the framework's "native" (own) tooling.
It relies on build and installation utilities available in the [embuild](https://github.com/ivmarkov/embuild) crate.
The `native` builder installs all needed tools to compile this crate as well as the ESP-IDF framework itself.
- ### `pio`
This is a backup feature for installing all build tools and building the ESP-IDF framework. It uses [PlatformIO](https://platformio.org/) via the
[embuild](https://github.com/ivmarkov/embuild) crate.
Similarly to the `native` builder, the `pio` builder also automatically installs all needed tools (PlatformIO packages and frameworks in this case) to compile this crate as well as the ESP-IDF framework itself.
> ⚠️ The `pio` builder is less flexible than the default `native` builder in that it can work with only **one, specific** version of ESP-IDF. At the time of writing, this is V4.3.2.
- ### `binstart`
Defines the esp-idf entry-point for when the root crate is a [binary
crate](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries) that
defines a
[`main`](https://doc.rust-lang.org/reference/crates-and-source-files.html?highlight=main#main-functions)
function.
- ### `libstart`
Defines the esp-idf entry-point for when the root crate is a [library
crate](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#library). the root
crate is expected to provide a
```rust
#[no_mangle]
fn main() {}
```
function.
## sdkconfig
The esp-idf makes use of an [sdkconfig](#espidfsdkconfig-espidfsdkconfig) file for its
compile-time component configuration (see the [esp-idf
docs](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#project-configuration)
for more information). This config is separate from the [build configuration](#build-configuration).
### (*native* builder only) Using cargo-idf to interactively modify ESP-IDF's `sdkconfig` file
TBD: Upcoming
### (*pio* builder only) Using cargo-pio to interactively modify ESP-IDF's `sdkconfig` file
To enable Bluetooth, or do other configurations to the ESP-IDF sdkconfig you might take advantage of the cargo-pio Cargo subcommand:
* To install it, issue `cargo install cargo-pio --git https://github.com/ivmarkov/cargo-pio`
* To open the ESP-IDF interactive menuconfig system, issue `cargo pio espidf menuconfig` in the root of your **binary crate** project
* To use the generated/updated `sdkconfig` file, follow the steps described in the "Bluetooth Support" section
## Build configuration
There are two ways to configure how the ESP-IDF framework is compiled:
1. Environment variables, denoted by `$VARIABLE`;
> The environment variables can be passed on the command line, or put into the `[env]`
> section of a `.cargo/config.toml` file (see [cargo reference](https://doc.rust-lang.org/cargo/reference/config.html#env)).
2. The `[package.metadata.esp-idf-sys]` section of the `Cargo.toml`, denoted by *`field`*.
> **Note**
> Configuration can only come from the **root crate's** `Cargo.toml`. The root crate
> is the package in the *workspace directory*. If there is not root crate in case of a
> [virtual
> workspace](https://doc.rust-lang.org/cargo/reference/workspaces.html#virtual-manifest),
> its *name* can be specified with the `ESP_IDF_SYS_ROOT_CRATE` environment variable.
> ⚠️ Environment variables always take precedence over `Cargo.toml` metadata.
> **Note**: *workspace directory*
> The workspace directory mentioned here is always the directory containing the
> `Cargo.lock` file and the `target` directory (where the build artifacts are stored). It
> can be overridden with the `CARGO_WORKSPACE_DIR` environment variable, should this not
> be the right directory.
> (See
> [`embuild::cargo::workspace_dir`](https://docs.rs/embuild/latest/embuild/cargo/fn.workspace_dir.html)
> for more information).
>
> There is no need to explicitly add a
> [`[workspace]`](https://doc.rust-lang.org/cargo/reference/workspaces.html#the-workspace-section)
> section to the `Cargo.toml` of the workspace directory.
The following configuration options are available:
- ### *`esp_idf_sdkconfig_defaults`*, `$ESP_IDF_SDKCONFIG_DEFAULTS`
A single path or a list of paths to `sdkconfig.defaults` files to be used as base
values for the [`sdkconfig`](#espidfsdkconfig-espidfsdkconfig). If such a path is
relative, it will be relative to the *workspace directory*.
Defaults to `sdkconfig.defaults`.
In case of the environment variable, multiple elements should be `;`-separated.
> **Note**
> For each defaults file in this list, a more specific file will also be searched and
> used. This happens with the following patterns and order (least to most specific):
>
> 1. `<path>`
> 2. `<path>.<profile>`
> 3. `<path>.<mcu>`
> 4. `<path>.<profile>.<mcu>`
>
> where `<profile>` is the current cargo profile used (`debug`/`release`) and `<mcu>`
> specifies the mcu for which this is currently compiled for (see the [*`mcu`*](#mcu-mcu)
> configuration option below).
> ⚠️
> A setting contained in a more specific defaults file will override the
> same setting specified in a less specific one.
- ### *`esp_idf_sdkconfig`*, `$ESP_IDF_SDKCONFIG`
The `sdkconfig` file used to [configure the
`esp-idf`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html).
If this is a relative path, it is relative to the *workspace directory*.
Defaults to `sdkconfig`.
> **Note**
> Similar to the `sdkconfig.defaults`-file a more specific `sdkconfig`-file will be
> selected if available. This happens with the following patterns and precedence:
>
> 1. `<path>.<profile>.<mcu>`
> 2. `<path>.<mcu>`
> 3. `<path>.<profile>`
> 4. `<path>`
>
>
> **Note**: *native* builder only
> The cargo optimization options (`debug` and `opt-level`) are used by default to
> determine the compiler optimizations of the `esp-idf`, **however** if the compiler
> optimization options are already set in the `sdkconfig` **they will be used instead.**
- ### *`esp_idf_tools_install_dir`*, `$ESP_IDF_TOOLS_INSTALL_DIR`
The install location for the ESP-IDF framework tooling.
> **Note**
> The framework tooling is either [PlatformIO](https://platformio.org/) when the `pio` builder is used, or the ESP-IDF
> native toolset when the `native` builder is used (default).
This option can take one of the following values:
- `workspace` (default) - the tooling will be installed or used in
`<crate-workspace-dir>/.embuild/platformio` for `pio`, and
`<crate-workspace-dir>/.embuild/espressif` for the `native` builder;
- `out` - the tooling will be installed or used inside *esp-idf-sys*'s [build output
directory](https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script),
and will be deleted when `cargo clean` is invoked;
- `global` - the tooling will be installed or used in its standard directory
(`~/.platformio` for PlatformIO, and `~/.espressif` for the native ESP-IDF toolset);
- `custom:<dir>` - the tooling will be installed or used in the directory specified by
`<dir>`. If this directory is a relative location, it is assumed to be relative to
the *workspace directory*;
- `fromenv` - use the build framework from the environment
- *native* builder: use activated esp-idf environment (see esp-idf docs
[unix](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/linux-macos-setup.html#step-4-set-up-the-environment-variables)
/
[windows](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/windows-setup.html#using-the-command-prompt))
- *pio* builder: use `platformio` from the environment (i.e. `$PATH`)
and error if this is not possible.
> ⚠️ Please be extra careful with the `custom:<dir>` setting when switching from `pio` to `native` and the other way around, because
> the builder will install the tooling in `<dir>` without using any additional `platformio` or `espressif` subdirectories, so if you are not careful, you might end up with
> both PlatformIO, as well as the ESP-IDF native tooling intermingled together in a single folder.
> **Note**
> The [ESP-IDF git repository](https://github.com/espressif/esp-idf) will be cloned
> *inside* the tooling directory. The *native* builder will use the esp-idf at
> [*`idf_path`*](#idfpath-idfpath-native-builder-only) of available.
- ### *`idf_path`*, `$IDF_PATH` (*native* builder only)
A path to a user-provided local clone of the [esp-idf](https://github.com/espressif/esp-idf),
that will be used instead of the one downloaded by the build script.
- ### *`esp_idf_version`*, `$ESP_IDF_VERSION` (*native* builder only)
The version used for the `esp-idf`, can be one of the following:
- `commit:<hash>`: Uses the commit `<hash>` of the `esp-idf` repository.
Note that this will clone the whole `esp-idf` not just one commit.
- `tag:<tag>`: Uses the tag `<tag>` of the `esp-idf` repository.
- `branch:<branch>`: Uses the branch `<branch>` of the `esp-idf` repository.
- `v<major>.<minor>` or `<major>.<minor>`: Uses the tag `v<major>.<minor>` of the `esp-idf` repository.
- `<branch>`: Uses the branch `<branch>` of the `esp-idf` repository.
Defaults to `v4.4.1`.
- ### *`esp_idf_repository`*, `$ESP_IDF_REPOSITORY` (*native* builder only)
The URL to the git repository of the `esp-idf`, defaults to <https://github.com/espressif/esp-idf.git>.
> **Note**
> When the `pio` builder is used, it is possible to achieve something similar to
> `ESP_IDF_VERSION` and `ESP_IDF_REPOSITORY` by using the
> [`platform_packages`](https://docs.platformio.org/en/latest/projectconf/section_env_platform.html#platform-packages)
> PlatformIO option as follows:
>
> `ESP_IDF_PIO_CONF="platform_packages = framework-espidf @ <git-url> [@ <git-branch>]"`
>
> The above approach however has the restriction that PlatformIO will always use the ESP-IDF build tooling from
> its own ESP-IDF distribution, so the user-provided ESP-IDF branch may or may not compile. The current
> PlatformIO tooling is suitable for compiling ESP-IDF branches derived from versions 4.3.X and 4.4.X.
- ### `$ESP_IDF_GLOB[_XXX]_BASE` and `$ESP_IDF_GLOB[_XXX]_YYY`
A pair of environment variable prefixes that enable copying files and directory trees that match a certain glob mask into the native C project used for building the ESP-IDF framework:
- `ESP_IDF_GLOB[_XXX]_BASE` specifies the base directory which will be glob-ed for resources to be copied
- `ESP_IDF_GLOB[_XXX]_BASE_YYY` specifies one or more environment variables that represent the glob masks of resources to be searched for and copied, using the directory designated by the `ESP_IDF_GLOB[_XXX]_BASE` environment variable as the root. For example, if the following variables are specified:
- `ESP_IDF_GLOB_HOMEDIR_BASE=/home/someuser`
- `ESP_IDF_GLOB_HOMEDIR_FOO=foo*`
- `ESP_IDF_GLOB_HOMEDIR_BAR=bar*`
... then all files and directories matching 'foo*' or 'bar*' from the home directory of the user will be copied into the ESP-IDF C project.
Note also that `_HOMEDIR` in the above example is optional, and is just a mechanism allowing the user to specify more than one base directory and its glob patterns.
- ### `$ESP_IDF_PIO_CONF_XXX` (*pio* builder only)
A PlatformIO setting (or multiple settings separated by a newline) that will be passed
as-is to the `platformio.ini` file of the C project that compiles the ESP-IDF.
Check [the PlatformIO
documentation](https://docs.platformio.org/en/latest/projectconf/index.html) for more
information as to what settings you can pass via this variable.
> **Note**
> This is not one variable, but rather a family of variables all
> starting with `ESP_IDF_PIO_CONF_`. For example, passing `ESP_IDF_PIO_CONF_1` as well as
> `ESP_IDF_PIO_CONF_FOO` is valid and all such variables will be honored.
- ### *`esp_idf_cmake_generator`*, `$ESP_IDF_CMAKE_GENERATOR` (*native* builder only)
The CMake generator to be used when building the ESP-IDF.
If not specified or set to `default`, Ninja will be used on all platforms except
Linux/aarch64, where (for now) the Unix Makefiles generator will be used, as there are
no Ninja builds for that platform provided by Espressif yet.
Possible values for this environment variable are [the names of all command-line
generators that CMake
supports](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#cmake-generators)
with **spaces and hyphens removed**.
- ### *`mcu`*, `$MCU`
The MCU name (i.e. `esp32`, `esp32s2`, `esp32s3` `esp32c3` and `esp32h2`).
If not set this will be automatically detected from the cargo target.
> ⚠️
> [Older ESP-IDF versions might not support all MCUs from above.](https://github.com/espressif/esp-idf#esp-idf-release-and-soc-compatibility)
- ### *`esp_idf_components`*, `$ESP_IDF_COMPONENTS` (*native* builder only)
The (`;`-separated for the environment variable) list of esp-idf component names that
should be built. This list is used to trim the esp-idf build. Any component that is a
dependency of a component in this list will also automatically be built.
Defaults to all components being built.
> **Note**
> Some components must be explicitly enabled in the sdkconfig.
> [Extra components](#extra-esp-idf-components) must also be added to this list if
> they are to be built.
### Example
An example of the `[package.metadata.esp-idf-sys]` section of the `Cargo.toml`.
```toml
[package.metadata.esp-idf-sys]
esp_idf_tools_install_dir = "global"
esp_idf_sdkconfig = "sdkconfig"
esp_idf_sdkconfig_defaults = ["sdkconfig.defaults", "sdkconfig.defaults.ble"]
# native builder only
esp_idf_version = "branch:release/v4.4"
esp_idf_components = ["pthread"]
```
## Extra esp-idf components
It is possible to let *esp-idf-sys* compile extra [esp-idf
components](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html#concepts)
and generate bindings for them.
This is possible by adding an object to the
`package.metadata.esp-idf-sys.extra_components` array of the `Cargo.toml`. *esp-idf-sys*
will honor all such extra components in the *root crate*'s and all **direct**
dependencies' `Cargo.toml`.
> **Note**
> By only specifying the `bindings_header` field, one can extend the set of *esp-idf*
> bindings that were generated from
> [src/include/esp-idf/bindings.h](src/include/esp-idf/bindings.h).
An extra component can be specified like this:
```toml
[[package.metadata.esp-idf-sys.extra_components]]
# A single path or a list of paths to a component directory or directory
# containing components.
#
# Each path can be absolute or relative. Relative paths will be relative to the
# folder containing the defining `Cargo.toml`.
#
# **This field is optional.** No component will be built if this field is absent, though
# the bindings of the `[Self::bindings_header]` will still be generated.
component_dirs = ["dir1", "dir2"] # or "dir"
# The path to the C header to generate the bindings with. If this option is absent,
# **no** bindings will be generated.
#
# The path can be absolute or relative. A relative path will be relative to the
# folder containing the defining `Cargo.toml`.
#
# This field is optional.
bindings_header = "bindings.h"
# If this field is present, the component bindings will be generated separately from
# the `esp-idf` bindings and put into their own module inside the `esp-idf-sys` crate.
# Otherwise, if absent, the component bindings will be added to the existing
# `esp-idf` bindings (which are available in the crate root).
#
# To put the bindings into its own module, a separate bindgen instance will generate
# the bindings. Note that this will result in duplicate `esp-idf` bindings if the
# same `esp-idf` headers that were already processed for the `esp-idf` bindings are
# included by the component(s).
#
# This field is optional.
bindings_module = "name"
```
and is equivalent to
```toml
[package.metadata.esp-idf-sys]
extra_components = [
{ component_dirs = [ "dir1", "dir2" ], bindings_header = "bindings.h", bindings_module = "name" }
]
```
## Conditional compilation
The *esp-idf-sys* build script will set [rustc *cfg*s](https://doc.rust-lang.org/reference/conditional-compilation.html)
available for its sources.
> ⚠️ If an upstream crate also wants to have access to the *cfg*s it must:
> - have `esp-idf-sys` as a dependency, and
> - propagate the *cfg*s in its [build
> script](https://doc.rust-lang.org/cargo/reference/build-scripts.html) with
>
> ```rust
> embuild::build::CfgArgs::output_propagated("ESP_IDF").expect("no esp-idf-sys cfgs");
> ```
> using the [embuild](https://crates.io/crates/embuild) crate.
The list of available *cfg*s:
- `esp_idf_comp_{component}_enabled` for each [component](#espidfcomponents-espidfcomponents-native-builder-only)
- `esp_idf_version="{major}.{minor}"`
- `esp_idf_version_full="{major}.{minor}.{patch}"`
- `esp_idf_version_major="{major}"`
- `esp_idf_version_minor="{minor}"`
- `esp_idf_version_patch="{patch}"`
- `esp_idf_{sdkconfig_option}`
Each [sdkconfig
setting](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#configuration-options-reference)
where `{sdkconfig_option}` corresponds to the option set in the sdkconfig **lowercased**
and **without** the `CONFIG_` prefix. Only options set to `y` will get a *cfg*.
- `{mcu}`
Corresponds to the [mcu](#mcu-mcu) for which the esp-idf is compiled for.
## More info
If you are interested in how it all works under the hood, check the [build.rs](build/build.rs)
build script of this crate.