Embedded Rust on the ESP-32 : Hello World
Vaishnav-sabari-girish

Vaishnav-sabari-girish @vaishnav_sabari_girish

About: I am a final year ECE student in India.

Joined:
Feb 20, 2024

Embedded Rust on the ESP-32 : Hello World

Publish Date: Jul 14
0 0

In my previous blog link, I wrote about getting started with Embedded Rust on the ESP-32.
In this blog we will now create our first Embedded Rust application.
We will be creating the famous "Hello World" application.

Installing the necessary tools - Part 2

Install esp-generate. This generates the template for an Embedded Rust application for ESP32.

cargo install esp-generate
Enter fullscreen mode Exit fullscreen mode

Setting Up

Assuming that you have installed all the required tools as told in the previous blog and above, we can start with using a template to create a project (This make things really easy). You can build from scratch too, but , only if you have enough technical knowledge and debugging expertise.

  • Navigate to the directory where you want to save you project (Say Desktop) using the command cd Desktop.
  • Create the project using the esp-generate function as follows :
esp-generate --chip=esp32 <project_name>
Enter fullscreen mode Exit fullscreen mode
  • The --chip parameter depends on the ESP32 module being used. I personally am using the ESP32-WROOM module so it's esp32 for me. Refer the documentation for more chips.

  • Make sure you do not forget to follow the steps here

  • After creating the project, you will now get a directory of the below structure :

.
├── .cargo
│   └── config.toml
├── .gitignore
├── build.rs
├── Cargo.lock
├── Cargo.toml
├── rust-toolchain.toml
├── src
│   ├── bin
│   │   └── main.rs
│   └── lib.rs
└── tests
    └── hello_test.rs
Enter fullscreen mode Exit fullscreen mode

Before going further, let us see what these files are for

  • build.rs

Sets the linker script arguments based on the template options.

  • .cargo/config.toml

The Cargo configuration. This defines a few options to correctly build the project. Contains the custom runner command for espflash or probe-rs. For example, runner = "espflash flash --monitor" - this means you can just use cargo run to flash and monitor your code

  • Cargo.toml

The usual Cargo manifest declares some meta-data and dependencies of the project

  • .gitignore

Tells git which folders and files to ignore

  • rust-toolchain.toml

Defines which Rust toolchain to use. The toolchain will be nightly or esp depending on your target

  • src/bin/main.rs

The main source file of the newly created project.

  • src/lib.rs

This tells the Rust compiler that this code doesn't use libstd.


The Hello World Embedded Rust Program

Creating the project

When you run the command

esp-generate --chip=esp32 hello_world
Enter fullscreen mode Exit fullscreen mode

You will get a TUI interface for more settings.
To select a feature, press . As shown in the below GIF.

esp-generate

Make sure you choose the ones selected on the GIF only i.e only the Enable Unstable HAL features and Use esp-backtrace as panic handler which is inside Flashing, Logging and Debugging.

Writing the code

Now, in the src/bin/main.rs file inside the project directory, write the below code :

#![no_std]
#![no_main]

use esp_backtrace as _;
use esp_hal::{
    clock::CpuClock,
    delay::Delay, 
    main
};
use esp_println::println;
esp_bootloader_esp_idf::esp_app_desc!();


#[main]
fn main() -> ! {
    let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
    let _peripherals = esp_hal::init(config);
    let delay = Delay::new();
    loop {
        println!("Hello World");
        delay.delay_millis(500);
    }
}

Enter fullscreen mode Exit fullscreen mode

This code prints Hello World after every 500 milliseconds or 1/2 a second.

To run and flash this code , type

cargo run --release
Enter fullscreen mode Exit fullscreen mode

This will compile and flash the code to the ESP32.

Here's the output

Hello World

Into the Code : A line-by-line explanation

Line-by-Line Explanation: ESP32 "Hello World" in Embedded Rust

#![no_std]
Enter fullscreen mode Exit fullscreen mode
  • This disables the Rust standard library. In embedded systems, the standard library is often unavailable or unnecessary due to hardware constraints.
#![no_main]
Enter fullscreen mode Exit fullscreen mode
  • This tells the Rust compiler not to use the default entry point (main from the std lib). Instead, we define our own entry point suitable for embedded devices.
use esp_backtrace as _;
Enter fullscreen mode Exit fullscreen mode
  • This pulls in the esp_backtrace crate to provide panic handlers and backtraces (when supported), which are essential for debugging embedded systems. The as _ suppresses the unused import warning.
use esp_hal::{
    clock::CpuClock,
    delay::Delay, 
    main
};
Enter fullscreen mode Exit fullscreen mode
  • Imports:
    • CpuClock: lets you configure the CPU clock speed.
    • Delay: for inserting delays into your program.
    • main: an attribute macro that marks the main function as the program's entry point.
use esp_println::println;
Enter fullscreen mode Exit fullscreen mode
  • This brings in println! macro functionality, which outputs to UART so we can see debug output from the ESP32.
esp_bootloader_esp_idf::esp_app_desc!();
Enter fullscreen mode Exit fullscreen mode
  • This macro embeds metadata from the esp-idf build into the binary (like project name, version, etc.). It’s optional but helpful for diagnostics and logging.
#[main]
Enter fullscreen mode Exit fullscreen mode
  • The #[main] macro from esp_hal designates the following function as the program’s entry point.
fn main() -> ! {
Enter fullscreen mode Exit fullscreen mode
  • Defines the entry point of the application. The -> ! return type indicates it never returns (infinite loop).
    let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
Enter fullscreen mode Exit fullscreen mode
  • Creates a default configuration and sets the CPU clock to the maximum supported frequency.
    let _peripherals = esp_hal::init(config);
Enter fullscreen mode Exit fullscreen mode
  • Initializes the ESP32 HAL with the provided configuration and takes ownership of the device’s peripherals.
    let delay = Delay::new();
Enter fullscreen mode Exit fullscreen mode
  • Instantiates a delay object used to insert blocking delays into the code.
    loop {
Enter fullscreen mode Exit fullscreen mode
  • Infinite loop to run your logic continuously (typical in embedded firmware).
        println!("Hello World");
Enter fullscreen mode Exit fullscreen mode
  • Sends the string "Hello World" to the serial output using UART.
        delay.delay_millis(500);
Enter fullscreen mode Exit fullscreen mode
  • Inserts a 500 ms delay between messages.
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Closes the loop and main function.

Important Links

  1. Getting Started blog
  2. Blinking an LED
  3. GitHub Repository
  4. Official Espressif Documentation

Thank You

Comments 0 total

    Add comment