Rust on ESP32 "Hello, World" app

A demo binary crate for the ESP32 and ESP-IDF, which connects to WiFi, drives a small HTTP server and draws on a LED screen

Rust on ESP32 STD demo app

A demo STD binary crate for the ESP32[XX] and ESP-IDF, which connects to WiFi, Ethernet, drives a small HTTP server and draws on a LED screen.

Highlights:

  • Pure Rust and pure Cargo build! No CMake, no PlatformIO, no C helpers
    • ... via esp-idf-sys and embuild
  • Support for Rust STD (threads, console, TCP/IP) safe APIs
    • ... upstreamed and part of the Rust STD library
  • New, experimental! Support for asynchronous networking using smol
  • New, experimental! Support for running in the Espressif fork of QEMU
  • Rust Safe APIs for various ESP-IDF services like WiFi, Ping, Httpd and logging
    • ... via esp-idf-svc (embedded-svc abstractions implemented on top of ESP-IDF)
  • NAPT support (Router from the SoftAP to the STA interface). NOTE: In production, do NOT leave the SoftAP interface open (without password)!
  • Driving a LED screen with the embedded-graphics Rust crate
    • ... via esp-idf-hal (embedded-hal drivers implemented on top of ESP-IDF)
  • (ESP32-S2 only) Blink a LED by loading a pure Rust program onto the RiscV Ultra Low Power CPU

Build

  • Install the Rust Espressif compiler toolchain and the Espressif LLVM Clang toolchain
    • This is necessary, because support for the Xtensa architecture (ESP32 / ESP32-S2 / ESP32-S3) is not upstreamed in LLVM yet
  • Switch to the esp toolchain from the pre-built binaries: rustup default esp
    • (You can also skip this step and switch to the esp toolchain for the demo crate only by executing rustup override set esp inside the rust-esp32-std-demo directory once you have cloned the demo as per below)
    • NOTE For ESP32-C3 - which runs a RiscV32 chip - you can just use the stock nightly Rust compiler, and a recent, stock Clang (as in Clang 11+)
    • (You can do this by issuing rustup install nightly and then rustup default nightly instead of installing/building the Rust & Clang ESP forks and switching to their esp toolchain as advised above)
  • If using the custom Espressif Clang, make sure that you DON'T have a system Clang installed as well, because even if you have the Espressif one first on your $PATH, Bindgen will still pick the system one
    • A workaround that does not require uninstalling the system Clang is to do export LIBCLANG_PATH=<path to the Espressif Clang lib directory> prior to continuing the build process
  • cargo install ldproxy
  • Clone this repo: git clone https://github.com/ivmarkov/rust-esp32-std-demo
  • Enter it: cd rust-esp32-std-demo
  • Export two environment variables that would contain the SSID & password of your wireless network:
    • export RUST_ESP32_STD_DEMO_WIFI_SSID=<ssid>
    • export RUST_ESP32_STD_DEMO_WIFI_PASS=<ssid>
  • To configure the demo for your particular board, please uncomment the relevant Rust target for your board and comment the others. Alternatively, just append the --target <target> flag to all cargo build lines below.
  • Build: cargo build or cargo build --release
    • (Only if you happen to have a TTGO T-Display board): Add ttgo to the --features build flags above (as in cargo build --features ttgo) to be greeted with a Hello Rust! message on the board's LED screen
    • (Only if you happen to have a Waveshare board and a waveshare 4.2" e-paper screen): Add waveshare_epd to the --features build flags above (as in cargo build --features waveshare_epd) to be greeted with a Hello Rust! message on the e-paper screen
    • (Only if you happen to have an ESP32-S2-Kaluga-1 board): Add kaluga to the --features build flags above (as in cargo build --features kaluga) to be greeted with a Hello Rust! message on the board's LED screen
    • (Only if you happen to have a Heltec LoRa 32 board): Add heltec to the --features build flags above (as in cargo build --features heltec) to be greeted with a Hello Rust! message on the board's LED screen
    • (Only if you happen to have an ESP32-S3-USB-OTG): Add esp32s3_usb_otg to the --features build flags above (as in cargo build --features esp32s3_usb_otg) to be greeted with a Hello Rust! message on the board's LED screen
    • (Only if you happen to have an Ethernet-to-SPI board based on the W5500 chip): Add w5500 to the --features build flags above (as in cargo build --features w5500) to have Ethernet connectivity as part of the demo
      • Note that other Ethernet-to-SPI boards might work just fine as well, but you'll have to change the chip from SpiEthDriver::W5500 to whatever chip your SPI board is using, in the demo code itself.
    • (Only if you happen to have an ESP32 board with an onboard IP101 LAN chip and/or a stock ESP32 board connected to an IP101 Ethernet board via RMII): Add ip101 to the --features build flags above (as in cargo build --features ip101) to have Ethernet connectivity as part of the demo
      • Note that other RMII Ethernet boards might work just fine as well, but you'll have to change the chip from RmiiEthDriver::IP101 to whatever chip your board is using, in the demo code itself.
  • (Only if you happen to have an ESP32-S2 board and can connect a LED to GPIO Pin 04 and GND): Try accessing http://<dhcp-ip-of-the-board>>/ulp once build is flashed on the MCU

QEMU (WIP, experimental)

  • Rather than flashing on the chip, you can now run the demo in QEMU:
    • Clone and then build the Espressif fork of QEMU by following the build instructions
    • Install the esptool.py utility
    • Uncomment CONFIG_ETH_USE_OPENETH=y, CONFIG_MBEDTLS_HARDWARE_AES=n, and CONFIG_MBEDTLS_HARDWARE_SHA=n in sdkconfig.defaults.esp32 (it is not enabled by default because this somehow causes issues when compiling for the ESP32S2)
    • Build the app with cargo build --features qemu
    • NOTE: Only ESP32 is supported for the moment, so make sure that the xtensa-esp32-espidf target (the default one) is active in your .cargo/config.toml file (or override with cargo build --features qemu --target xtensa-esp32-espidf)
    • Run it in QEMU by typing ./qemu.sh. NOTE: You might have to change the ESP_QEMU_PATH in that script to point to the build subdirectory of your QEMU Espressif clone

Flash

  • cargo install espflash
  • espflash /dev/ttyUSB0 target/[xtensa-esp32-espidf|xtensa-esp32s2-espidf|riscv32imc-esp-espidf]/debug/rust-esp32-std-demo
  • Replace dev/ttyUSB0 above with the USB port where you've connected the board

NOTE: The above commands do use espflash and NOT cargo espflash, even though both can be installed via Cargo. cargo espflash is essentially espflash but it has some extra superpowers, like the capability to build the project before flashing, or to generate an ESP32 .BIN file from the built .ELF image.

Alternative flashing

  • You can also flash with the esptool.py utility which is part of the Espressif toolset
  • Use the instructions below only if you have flashed successfully with espflash at least once, or else you might not have a valid bootloader and partition table!
  • The instructions below only (re)flash the application image, as the (one and only) factory image starting from 0x10000 in the partition table!
  • Install esptool using Python: pip install esptool
  • (After each cargo build) Convert the elf image to binary: esptool.py --chip [esp32|esp32s2|esp32c3] elf2image target/xtensa-esp32-espidf/debug/rust-esp32-std-demo
  • (After each cargo build) Flash the resulting binary: esptool.py --chip [esp32|esp32s2|esp32c3] -p /dev/ttyUSB0 -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 4MB 0x10000 target/xtensa-esp32-espidf/debug/rust-esp32-std-demo.bin

Monitor

  • Once flashed, the board can be connected with any suitable serial monitor, e.g.:

    • ESPMonitor: espmonitor /dev/ttyUSB0 (you need to cargo install espmonitor first)
    • Cargo PIO (this one decodes stack traces!): cargo pio espidf monitor /dev/ttyUSB0 (you need to cargo install cargo-pio first)
      • Please run it from within the rust-esp32-std-demo project directory, or else the built ELF file will not be detected, and the stack traces will not be decoded!
    • Built-in Linux/MacOS screen: screen /dev/ttyUSB0 115200 (use Ctrl+A and then type :quit to stop it)
    • Miniterm: miniterm --raw /dev/ttyUSB0 115200
  • If the app starts successfully, it should be listening on the printed IP address from the WiFi connection logs, port 80.

  • Open a browser, and navigate to one of these:

    • http://<printed-ip-address>
    • http://<printed-ip-address>/foo?key=value
    • http://<printed-ip-address>/bar
    • http://<printed-ip-address>/ulp (ESP32-S2 only)
  • The monitor should output more or less the following:

Issues

Collection of the latest Issues

gyscos

gyscos

1

The current demo demonstrates how to use wifi, but not bluetooth.

Including that in the example (probably using apache nimble?) could be beneficial.

svenstaro

svenstaro

2

Currently you maintain a bunch of downstream forks (which is great!) but I think it'd be even better if async with esp32 could just work out of the box with upstream libraries. Do you think there's a chance to get your work integrated upstream?

callmephilip

callmephilip

12

I am trying to figure out how to add OTA updates support to demo using this as the reference. Here are the steps:

Updated partitions.csv with 2 ota slots:

Updated sdkconfig.defaults to include as seen here

I intend to use this API once I have firmware update downloaded, however I am confused as to what exactly to download. Output found in target/xtensa-esp32-espidf/debug/rust-esp32-std-demo is a 34.6MB binary. This seems to exceed the size of the flash on the chip, as far as I can tell. Does this output need to processed further before being downloaded used for the update?

JakobLachermeier

JakobLachermeier

10

Hi, i'm getting a bootloop after building for the c3 and flashing with espflash.

I have installed rust-nightly and clang version 13.

Here are the steps i used to build and flash.

Any tips on how to troubleshoot are appreciated. This is the espmonitor output.

ESP-ROM:esp32c3-api1-20210207 Build:Feb 7 2021 rst:0xc (RTC_SW_CPU_RST),boot:0xc (SPI_FAST_FLASH_BOOT) Saved PC:0x40384396 SPIWP:0xee mode:DIO, clock div:1 load:0x3fcd6100,len:0x172c load:0x403ce000,len:0x928 load:0x403d0000,len:0x2ce0 entry 0x403ce000 I (35) boot: ESP-IDF v4.4-dev-2825-gb63ec47238 2nd stage bootloader I (35) boot: compile time 12:10:40 I (35) boot: chip revision: 3 I (38) boot_comm: chip revision: 3, min. bootloader chip revision: 0 I (45) boot.esp32c3: SPI Speed : 80MHz I (50) boot.esp32c3: SPI Mode : DIO I (55) boot.esp32c3: SPI Flash Size : 4MB I (60) boot: Enabling RNG early entropy source... I (65) boot: Partition Table: I (69) boot: ## Label Usage Type ST Offset Length I (76) boot: 0 nvs WiFi data 01 02 00009000 00006000 I (83) boot: 1 phy_init RF data 01 01 0000f000 00001000 I (91) boot: 2 factory factory app 00 00 00010000 003f0000 I (98) boot: End of partition table I (102) boot_comm: chip revision: 3, min. application chip revision: 0 I (110) esp_image: segment 0: paddr=00010020 vaddr=3c0b0020 size=239a0h (145824) map I (140) esp_image: segment 1: paddr=000339c8 vaddr=3fc8e800 size=03378h ( 13176) load I (143) esp_image: segment 2: paddr=00036d48 vaddr=40380000 size=092d0h ( 37584) load I (153) esp_image: segment 3: paddr=00040020 vaddr=42000020 size=ab978h (702840) map I (260) esp_image: segment 4: paddr=000eb9a0 vaddr=403892d0 size=05334h ( 21300) load I (264) esp_image: segment 5: paddr=000f0cdc vaddr=50000000 size=00010h ( 16) load I (270) boot: Loaded app from partition at offset 0x10000 I (272) boot: Disabling RNG early entropy source... I (288) cpu_start: Pro cpu up. I (301) cpu_start: Pro cpu start user code I (301) cpu_start: cpu freq: 160000000 I (301) cpu_start: Application information: I (304) cpu_start: Project name: esp-idf I (308) cpu_start: App version: 0d085d7-dirty I (314) cpu_start: Compile time: Dec 10 2021 18:08:35 I (320) cpu_start: ELF file SHA256: 0000000000000000... I (326) cpu_start: ESP-IDF: 4.3.1 I (331) heap_init: Initializing. RAM available for dynamic allocation: I (338) heap_init: At 3FC95CA0 len 0002A360 (168 KiB): DRAM I (344) heap_init: At 3FCC0000 len 0001F060 (124 KiB): STACK/DRAM I (351) heap_init: At 50000010 len 00001FF0 (7 KiB): RTCRAM I (358) spi_flash: detected chip: generic I (362) spi_flash: flash io: dio E (366) spi_flash: Detected size(2048k) smaller than the size in the binary image header(4096k). Probe failed. assertion "flash_ret == ESP_OK" failed: file "/home/jakob/temp/rust-esp32-std-demo/.embuild/platformio/packages/framework-espidf/components/esp_system/startup.c", line 329, function: do_core_init abort() was called at PC 0x42090f5d on core 0 Core 0 register dump: MEPC : 0x403858a2 RA : 0x40386018 SP : 0x3fcde230 GP : 0x3fc8f000
TP : 0x00000000 T0 : 0x37363534 T1 : 0x7271706f T2 : 0x33323130
S0/FP : 0x00000004 S1 : 0x3fcde294 A0 : 0x3fcde25c A1 : 0x3fcde292
A2 : 0x00000000 A3 : 0x3fcde289 A4 : 0x00000001 A5 : 0x3fc96000
A6 : 0x7a797877 A7 : 0x76757473 S2 : 0x3c0c9000 S3 : 0x3fc93038
S4 : 0x42000020 S5 : 0x00040020 S6 : 0x00000000 S7 : 0x3fcd7000
S8 : 0x3c0b0020 S9 : 0x00010020 S10 : 0x00000006 S11 : 0x3fcde3cc
T3 : 0x6e6d6c6b T4 : 0x6a696867 T5 : 0x66656463 T6 : 0x62613938
MSTATUS : 0x00001881 MTVEC : 0x40380001 MCAUSE : 0x00000007 MTVAL : 0x00000000
MHARTID : 0x00000000
Stack memory: 3fcde230: 0x00000006 0x00010020 0x3fcde290 0x4038cd96 0x00000000 0x00040020 0x42000020 0x3fc9113c 3fcde250: 0x3fcde294 0x3fc91158 0x3fcde290 0x726f6261 0x20292874 0x20736177 0x6c6c6163 0x61206465 3fcde270: 0x43502074 0x34783020 0x30393032 0x20643566 0x63206e6f 0x2065726f 0x00000030 0x42090000 3fcde290: 0x00000030 0x39303234 0x64356630 0x3c0bcd00 0x00000149 0x3c0bd000 0x3c0bcdd4 0x42090f60 3fcde2b0: 0x00000000 0x3c0bce48 0x3c0bcd84 0x00000149 0x3c0c9000 0x3c0bd000 0x3fc93038 0x420293ea 3fcde2d0: 0xffff0000 0x00000146 0x3c0bcbc4 0x403802ec 0xffff0000 0x3c0b0020 0x3c0bd000 0x4202951e 3fcde2f0: 0xffff0000 0x30303030 0x30303030 0x30303030 0x30303030 0x000bb900 0x0000000c 0x420a36c6 3fcde310: 0xffff0000 0x000bb977 0x0000000c 0x40380434 0x00000000 0x000ab978 0x000bb997 0x403d0f1a 3fcde330: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x3fcdf000 3fcde350: 0x00004d64 0x3ff1c14c 0x0000ffff 0xffffffff 0xffffffff 0x3fcde4b0 0xffffffff 0x403d15aa 3fcde370: 0x00000000 0x0000000a 0x00010000 0x003f0000 0x00010000 0x100206e9 0x403802ec 0x000000ee 3fcde390: 0x00000005 0x00000000 0x01000000 0x3c0b0020 0x000239a0 0x3fc8e800 0x00003378 0x40380000 3fcde3b0: 0x000092d0 0x42000020 0x000ab978 0x403892d0 0x00005334 0x50000000 0x00000010 0x00000000 3fcde3d0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 3fcde3f0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 3fcde410: 0x00000000 0x00000000 0x00000000 0x00010020 0x000339c8 0x00036d48 0x00040020 0x000eb9a0 3fcde430: 0x000f0cdc 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 3fcde450: 0x00000000 0x00000000 0x00000000 0x000e0d10 0x6aea34f5 0xe1e1cd0b 0x72e40372 0x33cdf4c7 3fcde470: 0x938e4d9d 0xe6fe6ced 0x1ef0ccf1 0x83292d06 0x00004d64 0x3ff1c14c 0x0000ffff 0x3fce0000 3fcde490: 0x00000000 0x000000e4 0x00004d70 0x403ce07c 0x4cbb5b54 0x712931aa 0x00000000 0xffffffff 3fcde4b0: 0x00000000 0x00000000 0x00010000 0x003f0000 0x00000000 0x00000000 0x00000000 0x00000000 3fcde4d0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 3fcde4f0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 3fcde510: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 3fcde530: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 3fcde550: 0x600c5200 0x600c5200 0x8082dbb8 0x4004a296 0xba1481c2 0xec8c6203 0x3fcde668 0x00000000 3fcde570: 0x34333936 0x00000000 0x00010000 0x2f0203e9 0x403ce000 0x403d0000 0x00002ce0 0x000000ee 3fcde590: 0x00000005 0x00000000 0x01000000 0x3c004d70 0x7533885e 0xcedd6070 0x3b6231b0 0x144aa432 3fcde5b0: 0xc32e18bd 0xf4abe6ff 0xbbaffc5d 0xf3047dd7 0x4c97a429 0x00000000 0x00000000 0x00000000 3fcde5d0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x3fcde700 0x00000101 0x00000002 3fcde5f0: 0xcedd6070 0x3b6231b0 0x144aa432 0xc32e18bd 0xf4abe6ff 0xbbaffc5d 0xf3047dd7 0x4c97a429 3fcde610: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 ELF file SHA256: 0000000000000000 Rebooting...

MabezDev

MabezDev

1

Building 876a42e9a70843e88ec2fc4deb8f1582ed8c091a for c3 (both pio & native build) produces a IllegalInstruction exception, I captured the PC and fed it into addr2line and it reported the invalid instruction to be in super::intrinsics::abort() inside panic_fmt. Looking at git log it seems immediate abort was removed, but perhaps its still around somewhere?

Regardless of all that, there seems to be a panic when running the demo on a C3.

Here is the log snippet, which may be of interest:

Information - Updated May 12, 2022

Stars: 355
Forks: 44
Issues: 7

Cargo-xbuild is a replacement for xargo which is now no longer supported

Cross compling sysroot features can be specified in the command line parameters or via cargo config

Cargo-xbuild is a replacement for xargo which is now no longer supported

Cargo Advent of Code Helper

cargo-aoc is a simple CLI tool that aims to be a helper for the Criterion

Cargo Advent of Code Helper

cargo-tree has been integrated directly into Cargo as of the 1

This repository is archived as a result

cargo-tree has been integrated directly into Cargo as of the 1

cargo-ndk - Build Rust code for Android

This cargo extension handles all the environment configuration needed for successfully building libraries

cargo-ndk - Build Rust code for Android

cargo-all-features

Cargo subcommands that build and test all feature flag combinations for a crate

cargo-all-features

cargo-geiger ☢️

A program that lists statistics related to the usage of unsafe Rust code in a Rust

cargo-geiger ☢️

cargo-io-lib-template

This is tweaked cargo init --lib for FOSS

cargo-io-lib-template

cargo-spellcheck

Grammar check using on how to define a custom dictionary file

cargo-spellcheck

cargo-cov: Source coverage for Rust

cargo-cov is a cargo subcommand which performs source coverage collection and reporting for Rust crates

cargo-cov: Source coverage for Rust

hello_world is initial rust app

hello_cargo is working with build system and package manager

hello_world is initial rust app
Facebook Instagram Twitter GitHub Dribbble
Privacy