Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rust_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
workflow_dispatch:

env:
rust_toolchain: nightly
rust_toolchain: stable
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jobs:
Expand Down
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Two IEEE 802.15.4 radios are supported out of the box:

## Build

For certain MCUs / Rust targets, the OpenThread libraries are pre-compiled for convenience.
For certain MCUs / Rust targets, the OpenThread libraries are pre-compiled with a commonly used set of defaults for convenience. The defaults allow running a minimal thread device (MTD) that registers services using SRP.
Current list (might be extended upon request):
- `riscv32imac-unknown-none-elf` (ESP32C6 and ESP32H2)
- `thumbv7em-none-eabi` (NRF52)
Expand All @@ -30,7 +30,7 @@ Small caveat: since `openthread` does a few calls into the C standard library (p

### Build for other targets / custom build

For targets where pre-compiled libs are not available (including for the Host itself), a standard `build.rs` build is also supported.
For targets where pre-compiled libs are not available (including for the Host itself), or for when additional OpenThread features beyond the defaults are desired, a standard `build.rs` build is also supported.
For the on-the-fly OpenThread CMake build to work, you'll need to install and set in your `$PATH`:
- The GCC toolchain correspponding to your Rust target, with **working** `foo-bar-gcc -print-sysroot` command
- Recent Clang (for Espressif `xtensa`, [it must be the Espressif fork](https://crates.io/crates/espup), but for all other chips, the stock Clang would work)
Expand All @@ -41,20 +41,33 @@ As per above, since `openthread` does a few calls into the C standard library (p
Examples of GCC toolchains that are known to work fine:
- For ARM (Cortex M CPUs and others) - the [ARM GNU toolchain](https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain);
- Note that the Ubuntu `arm-none-eabi-gcc` system package does **NOT** work, as it does not print a sysroot, i.e. `arm-none-eabi-gcc -print-sysroot` returns an empty response, and furthermore, the `newlib` headers are installed in a separate location from the arch headers;
- For RISCV - the [Espressif RISCV toolchain](https://github.com/espressif/crosstool-NG/releases). The ["official" RISCV GNU toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain) should also work;
- For RISCV - the [Espressif RISCV toolchain](https://github.com/espressif/crosstool-NG/releases). The ["official" RISCV GNU toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain) should also work (see below for guidance on building this);
- For xtensa (Espressif ESP32 and ESP32SXX MCUs) - the [Espressif xtensa toolchain](https://github.com/espressif/crosstool-NG/releases);
- For the Host machine (non-embedded) - your pre-installed toolchain would work just fine.

### Compiling the RISCV GNU Toolchain
As above, the requirements are for the toolchain to have `newlib`, so `--disable-linux` must be specified. For the `riscv32imac-unknown-none-elf` target (ESP32-C6 and ESP32-H2), the following options will produce a usable build:
```
./configure --prefix=/opt/riscv --with-arch=rv32imac --with-abi=ilp32 --disable-linux
make
```

Don't forget to include the resulting binaries in your path:
```
export PATH=$PATH:/opt/riscv/bin
```


## Features

- MTD (Minimal Thread Device) functionality
- Optional FTD (Full Thread Device) functionality
- Optional integration with [`embassy-net`]() and [`edge-nal`]()
- Out of the box support for the IEEE 802.15.4 radio in [Espressif](openthread/src/esp.rs) and [Nordic Semiconductor](openthread/src/nrf.rs) chips

## Next

- Sleepy end-device
- FTD (Full Thread Device) functionality

## Non-Goals

Expand Down
2 changes: 0 additions & 2 deletions examples/esp/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
[toolchain]
channel = "nightly"
components = ["rust-src"]
targets = ["riscv32imac-unknown-none-elf"]

2 changes: 2 additions & 0 deletions openthread-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ force-generate-bindings = []
# If this feature is not enabled, the build will assume and use the "official" RiscV GCC toolchain:
# https://github.com/riscv-collab/riscv-gnu-toolchain
force-esp-riscv-toolchain = []
# Build the OpenThread library in Full Thread Device (FTD) mode
full-thread-device = []

[build-dependencies]
anyhow = "1"
Expand Down
35 changes: 25 additions & 10 deletions openthread-sys/build.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
#![deny(unexpected_cfgs)]

use std::{env, path::PathBuf};

use anyhow::Result;

use crate::builder::OpenThreadConfig;
use crate::paths::PreGenerationPaths;

#[path = "gen/builder.rs"]
mod builder;
#[path = "gen/pregen_paths.rs"]
mod paths;

fn main() -> Result<()> {
let crate_root_path = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());

builder::OpenThreadBuilder::track(&crate_root_path.join("gen"));
builder::OpenThreadBuilder::track(&crate_root_path.join("openthread"));
builder::OpenThreadBuilder::track(&crate_root_path.join("CMakeLists.txt"));

// If `custom` is enabled, we need to re-build the bindings on-the-fly even if there are
// pre-generated bindings for the target triple

let host = env::var("HOST").unwrap();
let target = env::var("TARGET").unwrap();

let force_esp_riscv_toolchain = env::var("CARGO_FEATURE_FORCE_ESP_RISCV_TOOLCHAIN").is_ok();
let force_esp_riscv_toolchain = cfg!(feature = "force-esp-riscv-toolchain");

let mut openthread_config = OpenThreadConfig::default();
set_config_from_features(&mut openthread_config);

let force_generate_bindings = cfg!(feature = "force-generate-bindings");
let paths = PreGenerationPaths::derive(&crate_root_path, &target, &openthread_config);

let pregen_bindings = env::var("CARGO_FEATURE_FORCE_GENERATE_BINDINGS").is_err();
let pregen_bindings_rs_file = crate_root_path
.join("src")
.join("include")
.join(format!("{target}.rs"));
let pregen_libs_dir = crate_root_path.join("libs").join(&target);
let use_pregen_bindings = !force_generate_bindings && paths.bindings_rs_file.exists();

let dirs = if pregen_bindings && pregen_bindings_rs_file.exists() {
// Use the pre-generated bindings
Some((pregen_bindings_rs_file, pregen_libs_dir))
let dirs = if use_pregen_bindings {
Some((paths.bindings_rs_file, paths.libs_dir))
} else if target.ends_with("-espidf") {
// Nothing to do for ESP-IDF, `esp-idf-sys` will do everything for us
None
Expand All @@ -44,6 +52,7 @@ fn main() -> Result<()> {
None,
None,
force_esp_riscv_toolchain,
openthread_config,
);

let libs_dir = builder.compile(&out, None)?;
Expand Down Expand Up @@ -79,3 +88,9 @@ fn main() -> Result<()> {

Ok(())
}

fn set_config_from_features(config: &mut OpenThreadConfig) {
if cfg!(feature = "full-thread-device") {
config.ftd(true);
}
}
29 changes: 11 additions & 18 deletions openthread-sys/gen/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ use std::{
use anyhow::{anyhow, Result};
use bindgen::Builder;
use cmake::Config;
pub use openthread_config::*;

#[path = "./openthread_config.rs"]
mod openthread_config;

pub struct OpenThreadBuilder {
crate_root_path: PathBuf,
cmake_configurer: CMakeConfigurer,
clang_path: Option<PathBuf>,
clang_sysroot_path: Option<PathBuf>,
clang_target: Option<String>,
openthread_config: OpenThreadConfig,
}

impl OpenThreadBuilder {
Expand All @@ -37,6 +42,7 @@ impl OpenThreadBuilder {
clang_sysroot_path: Option<PathBuf>,
clang_target: Option<String>,
force_esp_riscv_toolchain: bool,
openthread_config: OpenThreadConfig,
) -> Self {
Self {
cmake_configurer: CMakeConfigurer::new(
Expand All @@ -50,6 +56,7 @@ impl OpenThreadBuilder {
clang_path,
clang_sysroot_path,
clang_target,
openthread_config,
}
}

Expand Down Expand Up @@ -175,25 +182,11 @@ impl OpenThreadBuilder {

let mut config = self.cmake_configurer.configure(Some(lib_dir));

for (key, value) in self.openthread_config.iter() {
config.define(key, value);
}

config
.define("OT_LOG_LEVEL", "NOTE")
.define("OT_FTD", "OFF")
.define("OT_MTD", "ON")
.define("OT_RCP", "OFF")
.define("OT_TCP", "OFF")
.define("OT_APP_CLI", "OFF")
.define("OT_APP_NCP", "OFF")
.define("OT_APP_RCP", "OFF")
.define("OT_BORDER_ROUTER", "OFF")
.define("OT_BORDER_ROUTING", "OFF")
.define("OT_SRP_CLIENT", "ON")
.define("OT_SLAAC", "ON")
.define("OT_ECDSA", "ON")
.define("OT_PING_SENDER", "ON")
// Do not change from here below
.define("OT_LOG_OUTPUT", "PLATFORM_DEFINED")
.define("OT_PLATFORM", "external")
.define("OT_SETTINGS_RAM", "OFF")
//.define("OT_COMPILE_WARNING_AS_ERROR", "ON "$@" "${OT_SRCDIR}"")
// ... or else the build would fail with `arm-none-eabi-gcc` during the linking phase
// with "undefined symbol `__exit`" error
Expand Down
Loading