Skip to content

When the ESP32-S3 sends messages on the CAN bus and there is no receiver, the software reset may fail #4683

@chenjiefang

Description

@chenjiefang

Bug description

It was observed on the ESP32-S3 that a software reset will not succeed if initiated while the CAN peripheral is actively attempting to transmit data without a connected receiver. The reset process is blocked at the transmit_async function call.

To Reproduce

  1. To create the project, run the following command:
esp-generate --chip esp32s3 writereset
  1. To use the master branch from GitHub, update the esp crate dependency in your Cargo.toml as follows. The current Cargo.toml content is:
[package]
edition = "2024"
name = "writereset"
rust-version = "1.88"
version = "0.1.0"

[[bin]]
name = "writereset"
path = "./src/bin/main.rs"

[dependencies]
esp-hal = { git = "https://github.com/esp-rs/esp-hal.git", features = [
  "esp32s3",
  "log-04",
  "unstable",
] }

esp-rtos = { git = "https://github.com/esp-rs/esp-hal.git", features = [
  "embassy",
  "esp-alloc",
  "esp32s3",
  "log-04",
] }

esp-bootloader-esp-idf = { git = "https://github.com/esp-rs/esp-hal.git", features = [
  "esp32s3",
  "log-04",
] }
log = "0.4.27"

embassy-executor = { version = "0.9.1", features = ["log"] }
embassy-time = { version = "0.5.0", features = ["log"] }
embassy-futures = { version = "0.1.2", features = ["log"] }
embassy-sync = { version = "0.7.2", features = ["log"] }
esp-alloc = { git = "https://github.com/esp-rs/esp-hal.git" }
esp-println = { git = "https://github.com/esp-rs/esp-hal.git", features = [
  "esp32s3",
  "log-04",
] }

critical-section = "1.2.0"
static_cell = "2.1.1"
embedded-can = "0.4.1"


[profile.dev]
# Rust debug is too slow.
# For debug builds always builds with some optimization
opt-level = "s"

[profile.release]
codegen-units = 1        # LLVM can perform better optimizations using a single thread
debug = 2
debug-assertions = false
incremental = false
lto = 'fat'
opt-level = 's'
overflow-checks = false

  1. Shown below is the final structure of the project:
Image
  1. The main.rs file is shown below:
#![no_std]
#![no_main]
#![deny(
    clippy::mem_forget,
    reason = "mem::forget is generally not safe to do with esp_hal types, especially those \
    holding buffers for the duration of a data transfer."
)]
#![deny(clippy::large_stack_frames)]

use embassy_executor::Spawner;
use embassy_time::Timer;
use esp_hal::clock::CpuClock;
use esp_hal::interrupt::software::SoftwareInterruptControl;
use esp_hal::ram;
use esp_hal::timer::timg::TimerGroup;
use log::info;

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

extern crate alloc;

// This creates a default app-descriptor required by the esp-idf bootloader.
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
esp_bootloader_esp_idf::esp_app_desc!();

#[allow(
    clippy::large_stack_frames,
    reason = "it's not unusual to allocate larger buffers etc. in main"
)]
#[esp_rtos::main]
async fn main(spawner: Spawner) -> ! {
    // generator version: 1.1.0

    esp_println::logger::init_logger_from_env();

    let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
    let peripherals = esp_hal::init(config);

    esp_alloc::heap_allocator!(#[ram(reclaimed)] size: 64 * 1024);
    esp_alloc::heap_allocator!(size: 36 * 1024);

    let timg0 = TimerGroup::new(peripherals.TIMG0);
    let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
    esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);

    info!("Embassy initialized!");

    let twai_config = esp_hal::twai::TwaiConfiguration::new(
        peripherals.TWAI0,
        peripherals.GPIO10,
        peripherals.GPIO12,
        esp_hal::twai::BaudRate::B250K,
        esp_hal::twai::TwaiMode::Normal,
    );
    let _ = spawner.spawn(writereset::can::twai_can(twai_config));

    let mut i = 0;
    loop {
        Timer::after_millis(1000).await;
        writereset::can::send_message(2, [1, 2, 1, 1, 0, 0, 0, 0]).await;
        i += 1;
        info!("i = {}", i);
        if i == 10 {
            writereset::can::software_reset();
        }
    }

    // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v~1.0/examples
}

  1. The lib.rs file is shown below:
#![no_std]
pub mod can;

  1. The can.rs file is shown below:
use embassy_futures::select::Either3;
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
use log::{error, info};

static MESSAGE_CHANNEL: embassy_sync::channel::Channel<CriticalSectionRawMutex, (u32, [u8; 8]), 8> =
    embassy_sync::channel::Channel::new();

/// reset signal
static SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new();

#[embassy_executor::task]
pub async fn twai_can(twai_config: esp_hal::twai::TwaiConfiguration<'static, esp_hal::Blocking>) {
    info!("init can bus");
    let id = b"xxxxxxxxxxxxxxxxxxxxxxxxxxxx1";
    let filter = esp_hal::twai::filter::SingleExtendedFilter::new(&id, b"x");

    let mut twai_config = twai_config.into_async();
    twai_config.set_filter(filter);
    twai_config.set_error_warning_limit(1);
    let twai = twai_config.start();
    let (rx, tx) = twai.split();

    match embassy_futures::select::select3(SIGNAL.wait(), twai_reader(rx), twai_writer(tx)).await {
        Either3::First(_) => {}
        Either3::Second(_) => {}
        Either3::Third(_) => {}
    }

    info!("software reset");
    embassy_time::Timer::after_secs(1).await;
    esp_hal::system::software_reset();
}

async fn twai_reader(mut rx: esp_hal::twai::TwaiRx<'_, esp_hal::Async>) {
    loop {
        match rx.receive_async().await {
            Ok(frame) => {
                use embedded_can::Frame;
                if !frame.is_data_frame() {
                    continue;
                }
                match frame.id() {
                    embedded_can::Id::Standard(id) => {
                        error!("standard id = {:?}", id);
                    }
                    embedded_can::Id::Extended(id) => {
                        info!("ext id = {:?}", id);
                    }
                }
                let data = frame.data();
                let mut v = [0u8; 8];
                let copy_len = core::cmp::min(data.len(), v.len());
                v[..copy_len].copy_from_slice(&data[..copy_len]);
                info!("msg = {:?}", v);
            }
            Err(err) => {
                error!("err = {:?}", err);
            }
        }
    }
}

async fn twai_writer(mut tx: esp_hal::twai::TwaiTx<'_, esp_hal::Async>) {
    loop {
        let (id, msg) = MESSAGE_CHANNEL.receive().await;
        let Some(id) = esp_hal::twai::ExtendedId::new(id) else {
            error!("ExtendedId::new fail");
            continue;
        };
        let Some(frame) = esp_hal::twai::EspTwaiFrame::new(id, &msg) else {
            error!("new frame fail");
            continue;
        };
        info!("start transmit_async");
        if let Err(err) = tx.transmit_async(&frame).await {
            error!("transmit_async err = {:?}", err);
        }
        info!("end transmit_async");
    }
}

pub async fn send_message(id: u32, msg: [u8; 8]) {
    MESSAGE_CHANNEL.send((id, msg)).await;
}

pub fn software_reset() {
    SIGNAL.signal(());
}

  1. After commenting out the following section in can.rs, the software_reset() function can be executed successfully.
if let Err(err) = tx.transmit_async(&frame).await {
    error!("transmit_async err = {:?}", err);
}
  1. The log messages indicative of the issue are shown below:
[2025-12-21T00:50:23Z INFO ] Connecting...
[2025-12-21T00:50:24Z INFO ] Using flash stub
Chip type:         esp32s3 (revision v0.2)
Crystal frequency: 40 MHz
Flash size:        4MB
Features:          WiFi, BLE, Embedded Flash
MAC address:       f0:9e:9e:43:99:3c
App/part. size:    145,072/4,128,768 bytes, 3.51%
[00:00:00] [========================================]      14/14      0x0      Skipped! (checksum matches)                                                                                                                 [00:00:00] [========================================]       1/1       0x8000   Skipped! (checksum matches)                                                                                                                 
[00:00:01] [========================================]      62/62      0x10000  Verifying... OK!                                                                                                                            [2025-12-21T00:50:25Z INFO ] Flashing has completed!
Commands:
    CTRL+R    Reset chip
    CTRL+C    Exit

ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x15 (USB_UART_CHIP_RESET),boot:0x29 (SPI_FAST_FLASH_BOOT)
Saved PC:0x40378f0e
SPIWP:0xee
mode:DIO, clock div:2
load:0x3fce2820,len:0x158c
load:0x403c8700,len:0xd24
load:0x403cb700,len:0x2f34
entry 0x403c8924
I (27) boot: ESP-IDF v5.5.1-838-gd66ebb86d2e 2nd stage bootloader
I (27) boot: compile time Nov 26 2025 12:27:56
I (27) boot: Multicore bootloader
I (29) boot: chip revision: v0.2
I (32) boot: efuse block revision: v1.3
I (35) boot.esp32s3: Boot SPI Speed : 40MHz
I (39) boot.esp32s3: SPI Mode       : DIO
I (43) boot.esp32s3: SPI Flash Size : 4MB
I (47) boot: Enabling RNG early entropy source...
I (51) boot: Partition Table:
I (54) boot: ## Label            Usage          Type ST Offset   Length
I (60) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (66) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (73) boot:  2 factory          factory app      00 00 00010000 003f0000
I (80) boot: End of partition table
I (83) esp_image: segment 0: paddr=00010020 vaddr=3c000020 size=0c5d8h ( 50648) map
I (103) esp_image: segment 1: paddr=0001c600 vaddr=3fc89d10 size=022e8h (  8936) load
I (106) esp_image: segment 2: paddr=0001e8f0 vaddr=40378000 size=01728h (  5928) load
I (108) esp_image: segment 3: paddr=00020020 vaddr=42010020 size=1307ch ( 77948) map
I (133) esp_image: segment 4: paddr=000330a4 vaddr=40379728 size=005e8h (  1512) load
I (135) boot: Loaded app from partition at offset 0x10000
I (136) boot: Disabling RNG early entropy source...
INFO - Embassy initialized!
INFO - init can bus
INFO - i = 1
INFO - start transmit_async
INFO - i = 2
INFO - i = 3
INFO - i = 4
INFO - i = 5
INFO - i = 6
INFO - i = 7
INFO - i = 8
INFO - i = 9
  1. After commenting out the transmission code, the program executed successfully. The log output is as follows:
   Compiling writereset v0.1.0 (/home/chenjf/projects/writereset)
warning: unused variable: `tx`
  --> src/can.rs:63:26
   |
63 | async fn twai_writer(mut tx: esp_hal::twai::TwaiTx<'_, esp_hal::Async>) {
   |                          ^^ help: if this is intentional, prefix it with an underscore: `_tx`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `frame`
  --> src/can.rs:70:18
   |
70 |         let Some(frame) = esp_hal::twai::EspTwaiFrame::new(id, &msg) else {
   |                  ^^^^^ help: if this is intentional, prefix it with an underscore: `_frame`

warning: variable does not need to be mutable
  --> src/can.rs:63:22
   |
63 | async fn twai_writer(mut tx: esp_hal::twai::TwaiTx<'_, esp_hal::Async>) {
   |                      ----^^
   |                      |
   |                      help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

warning: `writereset` (lib) generated 3 warnings (run `cargo fix --lib -p writereset` to apply 1 suggestion)
    Finished `dev` profile [optimized + debuginfo] target(s) in 0.45s
     Running `espflash flash --monitor --chip esp32s3 target/xtensa-esp32s3-none-elf/debug/writereset`
[2025-12-21T00:54:27Z INFO ] Serial port: '/dev/ttyACM0'
[2025-12-21T00:54:27Z INFO ] Connecting...
[2025-12-21T00:54:28Z INFO ] Using flash stub
Chip type:         esp32s3 (revision v0.2)
Crystal frequency: 40 MHz
Flash size:        4MB
Features:          WiFi, BLE, Embedded Flash
MAC address:       f0:9e:9e:43:99:3c
App/part. size:    144,320/4,128,768 bytes, 3.50%
[00:00:00] [========================================]      14/14      0x0      Skipped! (checksum matches)                                                                                                                 [00:00:00] [========================================]       1/1       0x8000   Skipped! (checksum matches)                                                                                                                 
[00:00:01] [========================================]      62/62      0x10000  Verifying... OK!                                                                                                                            [2025-12-21T00:54:29Z INFO ] Flashing has completed!
Commands:
    CTRL+R    Reset chip
    CTRL+C    Exit

ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x15 (USB_UART_CHIP_RESET),boot:0x29 (SPI_FAST_FLASH_BOOT)
Saved PC:0x40378eb7
SPIWP:0xee
mode:DIO, clock div:2
load:0x3fce2820,len:0x158c
load:0x403c8700,len:0xd24
load:0x403cb700,len:0x2f34
entry 0x403c8924
I (27) boot: ESP-IDF v5.5.1-838-gd66ebb86d2e 2nd stage bootloader
I (27) boot: compile time Nov 26 2025 12:27:56
I (27) boot: Multicore bootloader
I (29) boot: chip revision: v0.2
I (32) boot: efuse block revision: v1.3
I (35) boot.esp32s3: Boot SPI Speed : 40MHz
I (39) boot.esp32s3: SPI Mode       : DIO
I (43) boot.esp32s3: SPI Flash Size : 4MB
I (47) boot: Enabling RNG early entropy source...
I (51) boot: Partition Table:
I (54) boot: ## Label            Usage          Type ST Offset   Length
I (60) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (66) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (73) boot:  2 factory          factory app      00 00 00010000 003f0000
I (80) boot: End of partition table
I (83) esp_image: segment 0: paddr=00010020 vaddr=3c000020 size=0c550h ( 50512) map
I (103) esp_image: segment 1: paddr=0001c578 vaddr=3fc89d10 size=022f0h (  8944) load
I (106) esp_image: segment 2: paddr=0001e870 vaddr=40378000 size=017a8h (  6056) load
I (108) esp_image: segment 3: paddr=00020020 vaddr=42010020 size=12e0ch ( 77324) map
I (133) esp_image: segment 4: paddr=00032e34 vaddr=403797a8 size=00568h (  1384) load
I (135) boot: Loaded app from partition at offset 0x10000
I (135) boot: Disabling RNG early entropy source...
INFO - Embassy initialized!
INFO - init can bus
INFO - i = 1
INFO - start transmit_async
INFO - end transmit_async
INFO - i = 2
INFO - start transmit_async
INFO - end transmit_async
INFO - i = 3
INFO - start transmit_async
INFO - end transmit_async
INFO - i = 4
INFO - start transmit_async
INFO - end transmit_async
INFO - i = 5
INFO - start transmit_async
INFO - end transmit_async
INFO - i = 6
INFO - start transmit_async
INFO - end transmit_async
INFO - i = 7
INFO - start transmit_async
INFO - end transmit_async
INFO - i = 8
INFO - start transmit_async
INFO - end transmit_async
INFO - i = 9
INFO - start transmit_async
INFO - end transmit_async
INFO - i = 10
INFO - software reset
INFO - i = 11
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x3 (RTC_SW_SYS_RST),boot:0x29 (SPI_FAST_FLASH_BOOT)
Saved PC:0x403795cb
SPIWP:0xee
mode:DIO, clock div:2
load:0x3fce2820,len:0x158c
load:0x403c8700,len:0xd24
load:0x403cb700,len:0x2f34
entry 0x403c8924
I (26) boot: ESP-IDF v5.5.1-838-gd66ebb86d2e 2nd stage bootloader
I (27) boot: compile time Nov 26 2025 12:27:56
I (27) boot: Multicore bootloader
I (28) boot: chip revision: v0.2
I (31) boot: efuse block revision: v1.3
I (35) boot.esp32s3: Boot SPI Speed : 40MHz
I (38) boot.esp32s3: SPI Mode       : DIO
I (42) boot.esp32s3: SPI Flash Size : 4MB
I (46) boot: Enabling RNG early entropy source...
I (50) boot: Partition Table:
I (53) boot: ## Label            Usage          Type ST Offset   Length
I (59) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (66) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (72) boot:  2 factory          factory app      00 00 00010000 003f0000
I (79) boot: End of partition table
I (82) esp_image: segment 0: paddr=00010020 vaddr=3c000020 size=0c550h ( 50512) map
I (102) esp_image: segment 1: paddr=0001c578 vaddr=3fc89d10 size=022f0h (  8944) load
I (105) esp_image: segment 2: paddr=0001e870 vaddr=40378000 size=017a8h (  6056) load
I (108) esp_image: segment 3: paddr=00020020 vaddr=42010020 size=12e0ch ( 77324) map
I (132) esp_image: segment 4: paddr=00032e34 vaddr=403797a8 size=00568h (  1384) load
I (135) boot: Loaded app from partition at offset 0x10000
I (135) boot: Disabling RNG early entropy source...
INFO - Embassy initialized!

Expected behavior

Regardless of the CAN bus configuration (transmitter-only with no receiver), a call to esp_hal::system::software_reset() is expected to succeed, not block at the transmit_async function.

Environment

  • Target device: [e.g. ESP32-S3]
  • Crate name and version: [e.g. esp-hal 1.0.0]

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingperipheral:twaiTWAI peripheral

    Type

    No type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions