ESP32 Embedded Rust at the HAL: Random Number Generator
Tags: rust, tutorial, beginners, esp32
Published: 2023-06-16 19:36:37

> ***This blog post is the ninth of a multi-part series of posts where I explore various peripherals in the ESP32C3 using embedded Rust at the HAL level. Please be aware that certain concepts in newer posts could depend on concepts in prior posts.***   

#### If you find this post useful, and if Embedded Rust interests you, stay in the know and skyrocket your learning curve by subscribing to The Embedded Rustacean newsletter:

{% cta  http://www.theembeddedrustacean.com %} Subscribe Now to The Embedded Rustacean{% endcta%} 

## Introduction

The random number generator (RNG) in the ESP32C3 offers a significant advantage over pseudo-random number generators (PRNGs) due to its true randomness. Unlike PRNGs, which generate sequences of numbers based on a predetermined algorithm, the ESP32C3's RNG utilizes physical processes to produce genuinely unpredictable and statistically random numbers. This inherent randomness makes the ESP32C3 RNG ideal for applications requiring high levels of security, such as cryptographic key generation and secure communication protocols. Additionally, the ESP32C3 RNG ensures fair outcomes in simulations, statistical analysis, and gaming scenarios, where unbiased randomness is crucial.

Using the RNG in the ESP32C3 does not take a lot of code. In this post I'm going to build a simple application that reads in a file statically and prints out lines randomly. Which line will be printed will be determined by the number generated by the RNG.

### 📚 Knowledge Pre-requisites

To understand the content of this post, you need the following:

* Basic knowledge of coding in Rust.
    
* Familiarity with the basic template for creating embedded applications in Rust.
    

### 💾 Software Setup

All the code presented in this post is available on the [**apollolabs ESP32C3**](https://github.com/apollolabsdev/ESP32C3) git repo. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.

Additionally, the full project (code and simulation) is available on Wokwi [**here**](https://wokwi.com/projects/367671402643650561).

### 🛠 Hardware Setup

#### **Materials**

* [**ESP32-C3-DevKitM**](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html)
    
![ESP32C3 Devkit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cpdvlnnr68y03933nz12.jpg)
    

#### 🔌 Connections

No connections are necessary for this application.

## 👨‍🎨 Software Design

In the code introduced in this post, the application will grab a random number from the RNG every 2 seconds. The number will be used to print out an NMEA sentence from a file that is read statically.

## 👨‍💻 Code Implementation

### 📥 Crate Imports

In this implementation the crates required are as follows:

* The `esp32c3_hal` crate to import the ESP32C3 device hardware abstractions.
    
* The `esp_backtrace` crate to define the panicking behavior.
    
* The `esp_println` crate to provide `println!` implementation.
    

```rust
use esp32c3_hal::{
    clock::ClockControl, 
    esp_riscv_rt::entry, 
    peripherals::Peripherals, 
    prelude::*,
    timer::TimerGroup, 
    Delay, Rng, Rtc,
};
use esp_backtrace as _;
use esp_println::{print, println};
```

### 🌍 Global Static Variables

For this application, we're going to need a file with a list of mock sentences. In Rust, there is the [`include_str` macro](https://doc.rust-lang.org/std/macro.include_str.html) that would allow us to read a file statically at compile time as an `str`. The macro will yield a `&'static str` type that includes the contents of the file:

```rust
static MOCK_SENTENCES: &'static str = include_str!("nmea.rs");
```

***📝 Note***

> The `nmea.rs` file had a different extension of `.log` , however I had to change the extension for Wokwi so that it would recognize the file. The extension doesn't really matter for the `include_str` macro however as long as the contents of the file are UTF-8 encoded.

### 🎛 Peripheral Configuration Code

1️⃣ **Obtain a handle for the device peripherals**: In embedded Rust, as part of the singleton design pattern, we first have to take the PAC-level device peripherals. This is done using the `take()` method. Here I create a device peripheral handler named `dp` as follows:

```rust
let peripherals = Peripherals::take();
```

**2️⃣ Disable the Watchdogs:** The ESP32C3 has watchdogs enabled by default and they need to be disabled. If they are not disabled then the device would keep on resetting. I'm not going to go into much detail, however, watchdogs require the application software to periodically "kick" them to avoid resets. This is out of the scope of this example, though to avoid this issue, the following code needs to be included:

```rust
let mut system = peripherals.SYSTEM.split();
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

let mut rtc = Rtc::new(peripherals.RTC_CNTL);
let timer_group0 = TimerGroup::new(
        peripherals.TIMG0,
        &clocks,
        &mut system.peripheral_clock_control,
);
let mut wdt0 = timer_group0.wdt;
let timer_group1 = TimerGroup::new(
        peripherals.TIMG1,
        &clocks,
        &mut system.peripheral_clock_control,
);
let mut wdt1 = timer_group1.wdt;

rtc.swd.disable();
rtc.rwdt.disable();
wdt0.disable();
wdt1.disable();
```

3️⃣ **Instantiate and Create Handle for the RNG**: To create an instance of the RNG, all we need to do is call the `new()` instance method on the `RNG` struct as follows:

```rust
let mut rng = Rng::new(peripherals.RNG);
```

Note how the `new` method requires passing the `RNG` peripheral.

4️⃣ **Obtain a handle for the delay**: I'll be using the hal `Delay` type to create a delay handle as follows:

```rust
let mut delay = Delay::new(&clocks);
```

### 📱 Application Code

#### 🔁 Application Loop

The code below represents the full application:

```rust
loop {
    let num = rng.random() as u8;
    let sentence = MOCK_SENTENCES.lines().nth(num as usize).unwrap();
    println!("{}", sentence);
    delay.delay_ms(2000_u32);
}
```

In the first line of the code, a random number is obtained using the `rng` handle and the `random` method. As it turns out in the time being, the `random` interface returns a `u32`. Additionally, there aren't any interfaces that allow the control of the value range. Since my file does not have a number of lines that requires a `u32` I need to scale the number down. As a result, I cast the number as a `u8`.

In the second line, I use the `lines` method on the file read in statically. `lines` in Rust returns an iterator over the lines of a string, this would allow us to perform various operations on each line. Additionally, the `nth` method is used allowing us to read back a particular line number.

Finally, the third and fourth lines print the read sentence to the console and delay for 2 secs, respectively.

## 📱 Full Application Code

Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the [**apollolabs ESP32C3**](https://github.com/apollolabsdev/ESP32C3) git repo. Also the Wokwi project can be accessed [**here**](https://wokwi.com/projects/367671402643650561).

```rust
#![no_std]
#![no_main]

use esp32c3_hal::{
    clock::ClockControl, esp_riscv_rt::entry, peripherals::Peripherals, prelude::*,
    timer::TimerGroup, Delay, Rng, Rtc,
};
use esp_backtrace as _;
use esp_println::{print, println};

static MOCK_SENTENCES: &'static str = include_str!("nmea.rs");

#[entry]
fn main() -> ! {
    // Take Peripherals, Initialize Clocks, and Create a Handle for Each
    let peripherals = Peripherals::take();
    let mut system = peripherals.SYSTEM.split();
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    // Instantiate and Create Handles for the RTC and TIMG watchdog timers
    let mut rtc = Rtc::new(peripherals.RTC_CNTL);
    let timer_group0 = TimerGroup::new(
        peripherals.TIMG0,
        &clocks,
        &mut system.peripheral_clock_control,
    );
    let mut wdt0 = timer_group0.wdt;
    let timer_group1 = TimerGroup::new(
        peripherals.TIMG1,
        &clocks,
        &mut system.peripheral_clock_control,
    );
    let mut wdt1 = timer_group1.wdt;

    // Disable the RTC and TIMG watchdog timers
    rtc.swd.disable();
    rtc.rwdt.disable();
    wdt0.disable();
    wdt1.disable();

    let mut delay = Delay::new(&clocks);

    // Configure terminal: enable newline conversion
    print!("\x1b[20h");

    let mut rng = Rng::new(peripherals.RNG);

    // Application Loop
    loop {
        let num = rng.random() as u8;
        let sentence = MOCK_SENTENCES.lines().nth(num as usize).unwrap();
        println!("{}", sentence);
        delay.delay_ms(2000_u32);
    }
}
```

## Conclusion

In this post, an application was created leveraging the random number generator (RNG) peripheral for the ESP32C3 to read random lines from a file. The RNG code was created at the HAL level using the Rust esp32c3-hal. Have any questions/comments? Share your thoughts in the comments below 👇.

#### If you found this post useful, and if Embedded Rust interests you, stay in the know and skyrocket your learning curve by subscribing to The Embedded Rustacean newsletter:

{% cta  http://www.theembeddedrustacean.com %} Subscribe Now to The Embedded Rustacean{% endcta%}