Skip to content

Bug: Flashing failure on inclusion of terminal.draw(draw).unwrap(); on esp32 c6 #175

@JaminMartin

Description

@JaminMartin

Describe the bug

When I include terminal.draw(draw).unwrap(); even to reproduce the example code, probe-rs gets the following error:

❯ cargo run
   Compiling plague_rat v0.1.0 (/Users/jamin/Documents/Programming/Rust/plague_rat)
    Finished `dev` profile [optimized + debuginfo] target(s) in 2.43s
     Running `probe-rs run --chip=esp32c6 --preverify --always-print-stacktrace --no-location target/riscv32imac-unknown-none-elf/debug/plague_rat`
      Erasing ✔ 100% [####################] 320.00 KiB @ 316.61 KiB/s (took 1s)                                       Finished in 3.55s
ERROR nusb::platform::macos_iokit::device: Failed to submit Out transfer 0xbdf938d80 of len 48 on endpoint 02: e000404f
 WARN probe_rs::session: Could not clear all hardware breakpoints: An error with the usage of the probe occurred

Caused by:
    0: USB Communication Error
    1: endpoint stalled
ERROR nusb::platform::macos_iokit::device: Failed to submit Out transfer 0xbdf938d80 of len 64 on endpoint 02: e000404f
 WARN probe_rs::session: Failed to deconfigure device during shutdown: Probe(Usb(Custom { kind: ConnectionReset, error: Stall }))
Error: An error with the usage of the probe occurred

Caused by:
    0: USB Communication Error
    1: endpoint stalled

When it removed, the chip is flashed and I get the expect output.

    Finished `release` profile [optimized + debuginfo] target(s) in 2.10s
     Running `probe-rs run --chip=esp32c6 --preverify --always-print-stacktrace --no-location target/riscv32imac-unknown-none-elf/release/plague_rat`
      Erasing ✔ 100% [####################] 128.00 KiB @ 271.13 KiB/s (took 0s)
  Programming ✔ 100% [####################]  44.82 KiB @  57.41 KiB/s (took 1s)                                       Finished in 2.29s
[INFO ] Booting...
[INFO ] Heap OK
[INFO ] GPIO OK - busy: false
[INFO ] SPI OK
[INFO ] Pre-init delay...
[INFO ] Driver init...
[INFO ] Driver init OK
[INFO ] Display buffer OK
[INFO ] Creating backend...
[INFO ] Backend OK
[INFO ] Terminal OK
[INFO ] Calling draw...

I have checked the display is initialising correctly by 1. Rendering a raw image to it through embeded graphics directly. 2. Recreating my desired terminal display on a pipico.
To Reproduce
Steps to reproduce the behavior:

  • setup project using esp-generate
  • add mousefood, ratatui
  • use mousefood example code

Fingers crossed its some weird copy pasta error from converting from the pipico version, but given I cant even get the mousefood demo working, it might be more serious.
Expected behavior
The example ratatui terminal display should be rendered to the display.

Additional context
I have migrated this from a pipico, where I was able to get my own draw logic to render on this display.
I have attached my cargo.toml, the src file itself here:
cargo.toml

[package]
edition      = "2024"
name         = "plague_rat"
rust-version = "1.88"
version      = "0.1.0"

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

[dependencies]
esp-hal = { version = "~1.0", features = ["defmt", "esp32c6", "unstable"] }

esp-rtos = { version = "0.2.0", features = [
  "defmt",
  "embassy",
  "esp-alloc",
  "esp-radio",
  "esp32c6",
] }

defmt                  = "1.0.1"
esp-bootloader-esp-idf = { version = "0.4.0", features = ["defmt", "esp32c6"] }

bt-hci = "0.6.0"
embassy-executor = { version = "0.9.1", features = ["defmt"] }
embassy-time = { version = "0.5.0", features = ["defmt"] }
embedded-io = { version = "0.7.1", features = ["defmt"] }
embedded-io-async = { version = "0.7.0", features = ["defmt"] }
embedded-hal-bus = "0.2"
esp-alloc = { version = "0.9.0", features = ["defmt"] }
esp-radio = { version = "0.17.0", features = [
  "ble",
  "defmt",
  "esp-alloc",
  "esp32c6",
  "unstable",
] }
rtt-target = { version = "0.6.2", features = ["defmt"] }
trouble-host = { version = "0.5.0", features = ["gatt"] }

critical-section = "1.2.0"
static_cell      = "2.1.1"


weact-studio-epd    = { version = "0.1.2", features = ["blocking"] }
display-interface-spi = "0.5"
embedded-graphics   = "0.8.2"


mousefood = { git = "https://github.com/ratatui/mousefood", default-features = false, features = ["epd-weact", "fonts"] }
ratatui             = { version = "0.30.0", default-features = false }
kasuari             = { version = "0.4", default-features = false, features = ["portable-atomic"] }
heapless            = "0.9.2"
[profile.dev]

opt-level = "s"

[profile.release]
codegen-units    = 1
debug            = 2
debug-assertions = false
incremental      = false
lto              = 'fat'
opt-level        = 's'
overflow-checks  = false

main.rs

#![no_std]
#![no_main]

extern crate alloc;
use alloc::boxed::Box;
use defmt::info;
use esp_hal::clock::CpuClock;
use esp_hal::delay::Delay;
use esp_hal::gpio::{Input, InputConfig, Level, Output, OutputConfig, Pull};
use esp_hal::main;
use esp_hal::spi::Mode;
use esp_hal::spi::master::{Config as SpiConfig, Spi};
use esp_hal::time::Rate;
use embedded_hal_bus::spi::ExclusiveDevice;
use weact_studio_epd::graphics::{Display213BlackWhite, DisplayRotation};
use weact_studio_epd::WeActStudio213BlackWhiteDriver;
use mousefood::prelude::*;
use ratatui::style::Stylize;
use ratatui::widgets::{Block, Paragraph, Wrap};
use ratatui::{Frame, Terminal};
esp_bootloader_esp_idf::esp_app_desc!();

#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    defmt::error!("PANIC: {:?}", defmt::Debug2Format(info));
    loop {}
}

#[allow(clippy::large_stack_frames)]
#[main]
fn main() -> ! {
    rtt_target::rtt_init_defmt!();
    info!("Booting...");

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

    esp_alloc::heap_allocator!(size: 128 * 1024);
    info!("Heap OK");

    let cs   = Output::new(peripherals.GPIO4,  Level::High, OutputConfig::default());
    let dc   = Output::new(peripherals.GPIO5,  Level::High, OutputConfig::default());
    let rst  = Output::new(peripherals.GPIO10, Level::High, OutputConfig::default());
    let busy = Input::new(peripherals.GPIO11,  InputConfig::default().with_pull(Pull::Up));
    info!("GPIO OK - busy: {}", busy.is_high());

    let spi = Spi::new(
        peripherals.SPI2,
        SpiConfig::default()
            .with_frequency(Rate::from_mhz(4))
            .with_mode(Mode::_0),
    )
    .unwrap()
    .with_sck(peripherals.GPIO2)
    .with_mosi(peripherals.GPIO3);

    let spi_device = ExclusiveDevice::new_no_delay(spi, cs).unwrap();
    let interface = display_interface_spi::SPIInterface::new(spi_device, dc);
    info!("SPI OK");

    let mut driver = WeActStudio213BlackWhiteDriver::new(interface, busy, rst, Delay::new());

    info!("Pre-init delay...");
    for _ in 0..1_000_000u32 {
        core::hint::spin_loop();
    }

    info!("Driver init...");
    driver.init().unwrap();
    info!("Driver init OK");

    let mut display = Display213BlackWhite::new();
    display.set_rotation(DisplayRotation::Rotate270);
    info!("Display buffer OK");

    info!("Creating backend...");
    let config = EmbeddedBackendConfig {
        flush_callback: Box::new(move |d: &mut Display213BlackWhite| {
            info!("Flushing to e-paper...");
            driver.full_update(d).expect("epd update failed");
            info!("Flush done!");
        }),
        ..Default::default()
    };
    let backend = EmbeddedBackend::new(&mut display, config);
    info!("Backend OK");

    let mut terminal = Terminal::new(backend).unwrap();
    info!("Terminal OK");

    info!("Calling draw...");
    terminal.draw(draw).unwrap();

    loop {}
}
fn draw(frame: &mut Frame) {
    let text = "Ratatui on embedded devices!";
    let paragraph = Paragraph::new(text.white()).wrap(Wrap { trim: true });
    let bordered_block = Block::bordered().title("Mousefood");
    frame.render_widget(paragraph.block(bordered_block), frame.area());
}

build.rs

fn main() {
    linker_be_nice();
    println!("cargo:rustc-link-arg=-Tdefmt.x");
    // make sure linkall.x is the last linker script (otherwise might cause problems with flip-link)
    println!("cargo:rustc-link-arg=-Tlinkall.x");
}

fn linker_be_nice() {
    let args: Vec<String> = std::env::args().collect();
    if args.len() > 1 {
        let kind = &args[1];
        let what = &args[2];

        match kind.as_str() {
            "undefined-symbol" => match what.as_str() {
                what if what.starts_with("_defmt_") => {
                    eprintln!();
                    eprintln!(
                        "💡 `defmt` not found - make sure `defmt.x` is added as a linker script and you have included `use defmt_rtt as _;`"
                    );
                    eprintln!();
                }
                "_stack_start" => {
                    eprintln!();
                    eprintln!("💡 Is the linker script `linkall.x` missing?");
                    eprintln!();
                }
                what if what.starts_with("esp_rtos_") => {
                    eprintln!();
                    eprintln!(
                        "💡 `esp-radio` has no scheduler enabled. Make sure you have initialized `esp-rtos` or provided an external scheduler."
                    );
                    eprintln!();
                }
                "embedded_test_linker_file_not_added_to_rustflags" => {
                    eprintln!();
                    eprintln!(
                        "💡 `embedded-test` not found - make sure `embedded-test.x` is added as a linker script for tests"
                    );
                    eprintln!();
                }
                "free"
                | "malloc"
                | "calloc"
                | "get_free_internal_heap_size"
                | "malloc_internal"
                | "realloc_internal"
                | "calloc_internal"
                | "free_internal" => {
                    eprintln!();
                    eprintln!(
                        "💡 Did you forget the `esp-alloc` dependency or didn't enable the `compat` feature on it?"
                    );
                    eprintln!();
                }
                _ => (),
            },
            // we don't have anything helpful for "missing-lib" yet
            _ => {
                std::process::exit(1);
            }
        }

        std::process::exit(0);
    }

    println!(
        "cargo:rustc-link-arg=--error-handling-script={}",
        std::env::current_exe().unwrap().display()
    );
}

config.toml

[target.riscv32imac-unknown-none-elf]
runner = "probe-rs run --chip=esp32c6 --preverify --always-print-stacktrace --no-location"

[env]
DEFMT_LOG="info"

[build]
rustflags = [
  # Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.)
  # NOTE: May negatively impact performance of produced code
  "-C", "force-frame-pointers",
]

target = "riscv32imac-unknown-none-elf"

[unstable]
build-std = ["alloc", "core"]

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions