Skip to main content Link Menu Expand (external link) Document Search Copy Copied

使用Rust语言为ESP编程系列一 – 准备开发环境

前言

之前尝试写了一篇文章尝试使用Rust为esp系列控制器开发软件介绍了如何用Rust实现一个运行在ESP32S3上的webdav服务,发现还挺受欢迎,所有打算展开来讲一讲,希望能够对有兴趣在ESP上使用Rust语言的朋友有所帮助。

本文预设读者对使用Linux命令行、Rust开发和ESP系列控制器有一些基本认识,所以对如何切换目录和执行命令等操作细节,不做特别解释。

原理浅析

使用Rust语言为ESP芯片开发程序是在PC或服务器上利用交叉编译的方法,生成能够运行在ESP芯片上的程序的一种方法。Rust on ESP交叉编译环境通过扩展通用Rust开发环境而实现。通过增加必要的扩展,使Rust代码可以被编译为可运行在ESP上的机器码;并提供针对ESP的代码库,使得可以在Rust代码中访问ESP系统的设备。甚至我们可以直接使用系统库std的功能,就像在PC上开发代码一样使用内存分配、创建用户线程、文件接口等标准库接口。我们也可以不选择使用系统库std,只使用Rust的核心库core,可以生成更小镜像,但也不能使用内存分配器、线程模型和文件系统等抽象接口,代码直接调用硬件驱动提供的接口。

Linux环境下安装开发环境

如果你熟悉docker的使用,有一个更加简便的方法就是使用docker镜像idf-rust,这个镜像已经包含一个安装好了的Rust开发环境。下面是安装Rust on ESP的详细过程。

  1. 安装Rust on ESP开发环境

    开发环境基于通用的Rust的开发环境,第一步和安装普通的Rust开发环境一样。如果是Linux环境,可以在命令行中运行rustup网站的安装命令。这个命令会自动下载Rust开发环境并安装。

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    

    安装好Rust开发环境之后,下面就是安装ESP需要的Rust扩展部分,因为ESP系列分别有Xtensa核心和RISC-V核心的芯片。老款的ESP32、ESP32S2、ESP32S3是基于Xtensa核心的芯片,而新款的ESP32-C系列、ESP32-H系列和ESP32-P系列都是基于RISC-V核心。仅安装支持RISC-V核心芯片的工具链和安装支持两种核心芯片的工具链步骤有所不同。因为我主要使用ESP32,所以我选择第二个安装步骤。

    安装的命令其实很简单,主要的工作由espup工具完成。先安装espup,然后执行 espup install

    cargo install espup
    espup install
    

    espup会帮我们安装下列组件:

    • 支持ESP系列芯片的Rust

    • 支持RISC-V的工具链

    • 支持Xtensa核心的LLVM

    • GCC工具链用于链接过程,并负责生成二进制文件

      安装过程可能需要花不少时间,一方面因为需要下载不少软件组件,另外还受到下载速度影响。如果网速不稳,经常遇到下载软件包失败的问题,可能需要通过代理或镜像来解决。网络问题是当前安装过程中的最大麻烦,其他过程还比较简单。

  2. 安装系统库的依赖

    成功完成上面的步骤,我们已经安装好了开发环境。但要使用Rust系统库std,我们还要安装std依赖的组件。面向ESP的系统库std有两个依赖,ESP-IDF开发框架和ldproxy库。

    先安装ESP-IDF开发框架,第一步安装ESP-IDF依赖的第三方工具。

    • Ubuntu和Debian环境

      sudo apt-get install git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
      
    • CentOS 7 & 8

      sudo yum -y update && sudo yum install git wget flex bison gperf python3 cmake ninja-build ccache dfu-util libusbx
      
    • Arch

      sudo pacman -S --needed gcc git make flex bison gperf python cmake ninja ccache dfu-util libusb
      

      下面安装ESP-IDF框架本身,先下载ESP-IDF的源代码。当前的稳定版本是5.3,所以这里选择代码分支release/v5.3,并更新所有子模块。

      mkdir -p ~/esp
      cd ~/esp
      git clone --recursive https://github.com/espressif/esp-idf.git
      git checkout release/v5.3
      git submodule update --init --recursive
      

      下载好代码后开始ESP-IDF安装过程,进入esp-idf目录,执行下面命令。第二条命令用于将下载源导向乐鑫科技为国内网络提供的资源,这样下载速度会更快。

      cd ~/esp/esp-idf/
      export IDF_GITHUB_ASSETS="dl.espressif.cn/github_assets"
      ./install.sh esp32,esp32s2,esp32s3
      

      安装好ESP-IDF框架后,再安装ldproxy库,这个比较简单。

      cargo install ldproxy
      

      到这里就完成了系统库std依赖的安装,系统库已经包含在espup安装的组件内了,所以系统库本身已经安装好了。

开发环境测试

我们创建Rust on ESP项目需要用到乐鑫提供的一个工具cargo-generate。先安装cargo-generate工具。

cargo install cargo-generate  

再使用 cargo generate 命令生成一个使用std的空项目。如不使用系统库,使用 esp-generate --chip=<esp32xx> <your-project> 命令。

cargo generate esp-rs/esp-idf-template cargo

这是一个向导式工具,填入项目名称、目标芯片、选择不配置高级选项。工具输出如下。

user1@blackbox:~/code$ cargo generate esp-rs/esp-idf-template cargo
⚠️   Favorite `esp-rs/esp-idf-template` not found in config, using it as a git repository: https://github.com/esp-rs/esp-idf-template.git
🤷   Project Name: hello-world-rust
🔧   Destination: /home/user1/code/hello-world-rust ...
🔧   project-name: hello-world-rust ...
🔧   Generating template ...
✔ 🤷   Which MCU to target? · esp32
✔ 🤷   Configure advanced template options? · false
🔧   Moving generated files into: `/home/user1/code/hello-world-rust`...
🔧   Initializing a fresh Git repository
✨   Done! New project created /home/user1/code/hello-world-rust

打开Cargo.toml,在最后面增加下面的一段。告诉编译模块不要再下载ESP-IDF,使用环境变量中指定的ESP-IDF。

[package.metadata.esp-idf-sys]
esp_idf_tools_install_dir = "fromenv"
esp_idf_sdkconfig = "sdkconfig"
esp_idf_sdkconfig_defaults = ["sdkconfig.defaults", "sdkconfig.defaults.ble"]

在src/main.rs中添加代码,这里测试了动态内存分配。

fn main() {
    // It is necessary to call this function once. Otherwise some patches to the runtime
    // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
    esp_idf_svc::sys::link_patches();

    // Bind the log crate to the ESP Logging facilities
    esp_idf_svc::log::EspLogger::initialize_default();

    let five = Box::new(5);

    log::info!("Hello, world! Give me {}", *five);
}

下面就可以编译代码了,在编译前一定要执行下面命令,用于定义Rust on ESP和ESP-IDF的一些环境变量。

. $HOME/export-esp.sh
. $HOME/esp/esp-idf/export.sh

第一个是Rust on ESP开发环境的环境变量,第二个是ESP-IDF的一些环境变量。 顺序 一定不能反,我定位了好久才发现这个问题。由于espup安装的交叉编译工具和ESP-IDF自带的交叉编译工具版本有所不同,所以要让ESP-IDF环境变量覆盖前一个工具的环境变量,不然编译过程会报告版本不对。然后运行 cargo build 命令。

cargo build

如果安装过程没有问题,会生成固件文件hello-world-rust, 位置在 target/xtensa-esp32-espidf/debug/hello-world-rust 。用usb线连接ESP32开发板,运行 cargo espflash flash 将固件刷到板子上。如果没有espflash,先安装 cargo install espflash 。再运行 cargo espflash monitor 查看输出,如果没有问题,这时你会看到下面的输出。

...
I (423) main_task: Started on CPU0
I (433) main_task: Calling app_main()
I (433) hello_world_rust: Hello, world! Give me 5
I (433) main_task: Returned from app_main()

总结

恭喜你完成了Rust on ESP的安装,现在你就可以用Rust给ESP系列开发板写代码了。下面我们会进一步探索使用Rust语言我们可以做出哪些有趣的尝试。本文主要参考的互联网上的资料,其中关于Rust on ESP开发环境的安装主要参考的The Rust on ESP Book,ESP-IDF的安装参考的乐鑫提供的安装指导。安装过程繁琐,难免有所疏漏。如果大家有发现什么问题,欢迎指正。