Logging cleanup and fix for RTT

This commit is contained in:
Jakub Hlusička 2026-01-31 20:21:40 +01:00
parent f8ef06ee0c
commit c2e3f1bec3
9 changed files with 211 additions and 162 deletions

View file

@ -9,9 +9,9 @@
"chip": "esp32s3", "chip": "esp32s3",
"coreConfigs": [ "coreConfigs": [
{ {
"programBinary": "target/xtensa-esp32s3-none-elf/debug/acid-firmware" "programBinary": "target/xtensa-esp32s3-none-elf/debug/acid-firmware",
} },
] ],
}, },
{ {
"name": "probe-rs release restart", "name": "probe-rs release restart",
@ -21,9 +21,9 @@
"chip": "esp32s3", "chip": "esp32s3",
"coreConfigs": [ "coreConfigs": [
{ {
"programBinary": "target/xtensa-esp32s3-none-elf/release/acid-firmware" "programBinary": "target/xtensa-esp32s3-none-elf/release/acid-firmware",
} },
] ],
}, },
{ {
"preLaunchTask": "rust: cargo build", "preLaunchTask": "rust: cargo build",
@ -37,9 +37,9 @@
"chip": "esp32s3", "chip": "esp32s3",
"coreConfigs": [ "coreConfigs": [
{ {
"programBinary": "target/xtensa-esp32s3-none-elf/debug/acid-firmware" "programBinary": "target/xtensa-esp32s3-none-elf/debug/acid-firmware",
} },
] ],
}, },
{ {
"preLaunchTask": "rust: cargo build --release", "preLaunchTask": "rust: cargo build --release",
@ -53,9 +53,9 @@
"chip": "esp32s3", "chip": "esp32s3",
"coreConfigs": [ "coreConfigs": [
{ {
"programBinary": "target/xtensa-esp32s3-none-elf/release/acid-firmware" "programBinary": "target/xtensa-esp32s3-none-elf/release/acid-firmware",
} },
] ],
} },
] ],
} }

4
firmware/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,4 @@
{
"rust-analyzer.cargo.noDefaultFeatures": true,
"rust-analyzer.cargo.features": ["probe"],
}

View file

@ -4,9 +4,12 @@
{ {
"label": "rust: cargo build", "label": "rust: cargo build",
"type": "cargo", "type": "cargo",
"options": {
"cwd": "${workspaceFolder}/acid-firmware"
},
"command": "build", "command": "build",
"args": [ "args": [
"--no-default-features", "--features=probe,info" "--no-default-features", "--features=probe"
], ],
"problemMatcher": [ "problemMatcher": [
"$rustc" "$rustc"
@ -19,9 +22,12 @@
{ {
"label": "rust: cargo build --release", "label": "rust: cargo build --release",
"type": "cargo", "type": "cargo",
"options": {
"cwd": "${workspaceFolder}/acid-firmware"
},
"command": "build", "command": "build",
"args": [ "args": [
"--release", "--no-default-features", "--features=probe,info" "--release", "--no-default-features", "--features=probe"
], ],
"problemMatcher": [ "problemMatcher": [
"$rustc" "$rustc"

2
firmware/Cargo.lock generated
View file

@ -22,7 +22,7 @@ dependencies = [
"embassy-sync 0.7.2", "embassy-sync 0.7.2",
"embassy-time", "embassy-time",
"embedded-cli", "embedded-cli",
"embedded-io 0.7.1", "embedded-io 0.6.1",
"embedded-storage-async", "embedded-storage-async",
"embuild", "embuild",
"enumset", "enumset",

View file

@ -1,6 +1,6 @@
[target.'cfg(all(any(target_arch = "riscv32", target_arch = "xtensa"), target_os = "none"))'] [target.'cfg(all(any(target_arch = "riscv32", target_arch = "xtensa"), target_os = "none"))']
runner = "espflash flash --monitor" # runner = "espflash flash --monitor"
# runner = "probe-rs run --chip esp32s3 --preverify" runner = "probe-rs run --chip esp32s3 --preverify"
[build] [build]
target = "xtensa-esp32s3-none-elf" target = "xtensa-esp32s3-none-elf"

View file

@ -95,7 +95,7 @@ i-slint-core = { version = "1.14.1", git = "https://github.com/Limeth/slint", re
# Crates for serial UART CLI # Crates for serial UART CLI
embedded-cli = { version = "0.2.1", default-features = false, features = ["help", "macros"] } embedded-cli = { version = "0.2.1", default-features = false, features = ["help", "macros"] }
embedded-io = "0.7" embedded-io = "0.6"
mutually_exclusive_features = "0.1.0" mutually_exclusive_features = "0.1.0"
[dependencies.ekv] [dependencies.ekv]

View file

@ -1,12 +1,14 @@
use core::fmt::Write; use core::fmt::Write;
use embedded_cli::cli::CliBuilder;
use embedded_cli::Command; use embedded_cli::Command;
use esp_hal::{Async, uart::{TxError, UartRx}}; use embedded_cli::cli::CliBuilder;
use log::{info, error}; use esp_hal::{
Async,
uart::{TxError, UartRx},
};
use log::{error, info};
use crate::logging::with_uart_tx; use crate::logging::uart::with_uart_tx;
struct Writer; struct Writer;
@ -16,26 +18,21 @@ impl embedded_io::ErrorType for Writer {
impl embedded_io::Write for Writer { impl embedded_io::Write for Writer {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
with_uart_tx(|_, uart| { with_uart_tx(|_, uart| uart.write(buf))
uart.write(buf)
})
} }
fn flush(&mut self) -> Result<(), Self::Error> { fn flush(&mut self) -> Result<(), Self::Error> {
with_uart_tx(|_, uart| { with_uart_tx(|_, uart| uart.flush())
uart.flush()
})
} }
} }
#[derive(Command)] #[derive(Command)]
enum Base/*<'a>*/ { enum Base /*<'a>*/ {
// /// Say hello to World or someone else // /// Say hello to World or someone else
// Hello { // Hello {
// /// To whom to say hello (World by default) // /// To whom to say hello (World by default)
// name: Option<&'a str>, // name: Option<&'a str>,
// }, // },
/// Display the version of the firmware. /// Display the version of the firmware.
Version, Version,
@ -73,16 +70,26 @@ pub async fn run_console(mut uart_rx: UartRx<'_, Async>) {
// write!(cli.writer(), "Hello, {}", name.unwrap_or("World"))?; // write!(cli.writer(), "Hello, {}", name.unwrap_or("World"))?;
// } // }
Base::Version => { Base::Version => {
cli.writer().write_fmt(format_args!("{} - {} - {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), env!("GIT_COMMIT"))).unwrap(); cli.writer()
.write_fmt(format_args!(
"{} - {} - {}",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"),
env!("GIT_COMMIT")
))
.unwrap();
} }
Base::Reset => { Base::Reset => {
cli.writer().write_str("Performing software reset.").unwrap(); cli.writer()
.write_str("Performing software reset.")
.unwrap();
esp_hal::system::software_reset(); esp_hal::system::software_reset();
} }
} }
Ok(()) Ok(())
}), }),
).unwrap(); )
.unwrap();
} }
} }
} }

View file

@ -1,8 +1,10 @@
use core::cell::RefCell;
use core::fmt::Write; use core::fmt::Write;
use core::{cell::RefCell, fmt::Arguments};
use critical_section::{CriticalSection, Mutex}; use critical_section::{CriticalSection, Mutex};
use esp_hal::Blocking; use esp_hal::Blocking;
use esp_hal::clock::CpuClock;
use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
use esp_hal::uart::UartTx; use esp_hal::uart::UartTx;
use log::{LevelFilter, Log}; use log::{LevelFilter, Log};
@ -19,30 +21,107 @@ pub const LOG_LEVEL_FILTER: LevelFilter = {
} else if string.eq_ignore_ascii_case("TRACE") { } else if string.eq_ignore_ascii_case("TRACE") {
LevelFilter::Trace LevelFilter::Trace
} else { } else {
panic!("Unknown `ESP_LOG` value. Only `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`, or `OFF` may be used."); panic!(
"Unknown `ESP_LOG` value. Only `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`, or `OFF` may be used."
);
} }
} else { } else {
LevelFilter::Off LevelFilter::Off
} }
}; };
static ALT_LOGGER_UART: Mutex<RefCell<Option<UartTx<'static, Blocking>>>> = const RESET: &str = "\u{001B}[0m";
const RED: &str = "\u{001B}[31m";
const GREEN: &str = "\u{001B}[32m";
const YELLOW: &str = "\u{001B}[33m";
const BLUE: &str = "\u{001B}[34m";
const CYAN: &str = "\u{001B}[35m";
fn with_formatted_log_record<R>(
record: &log::Record,
callback: impl FnOnce(Arguments<'_>) -> R,
) -> R {
let color = match record.level() {
log::Level::Error => RED,
log::Level::Warn => YELLOW,
log::Level::Info => GREEN,
log::Level::Debug => BLUE,
log::Level::Trace => CYAN,
};
let args = format_args!(
"{}{:>5} - {}{}\n",
color,
record.level(),
record.args(),
RESET
);
(callback)(args)
}
/// The default USB logger.
#[cfg(feature = "usb-log")]
pub mod usb {
use super::*;
pub fn setup_logging() -> impl Future<Output = ()> {
esp_println::logger::init_logger(LOG_LEVEL_FILTER);
log::info!("Logger initialized!");
async {}
}
}
/// Alternative logger via UART.
#[cfg(feature = "alt-log")]
pub mod uart {
use super::*;
use crate::console;
use core::{cell::RefCell, fmt::Write};
use critical_section::{CriticalSection, Mutex};
use esp_hal::{
Blocking,
gpio::interconnect::{PeripheralInput, PeripheralOutput},
peripherals::UART2,
uart::{Uart, UartTx},
};
use log::{Log, info};
static ALT_LOGGER_UART: Mutex<RefCell<Option<UartTx<'static, Blocking>>>> =
Mutex::new(RefCell::new(None)); Mutex::new(RefCell::new(None));
pub fn with_uart_tx<R>( pub fn with_uart_tx<R>(
f: impl FnOnce(CriticalSection<'_>, &'_ mut UartTx<'static, Blocking>) -> R, f: impl FnOnce(CriticalSection<'_>, &'_ mut UartTx<'static, Blocking>) -> R,
) -> R { ) -> R {
critical_section::with(|cs| { critical_section::with(|cs| {
let mut uart = ALT_LOGGER_UART.borrow(cs).borrow_mut(); let mut uart = ALT_LOGGER_UART.borrow(cs).borrow_mut();
let uart = uart.as_mut().unwrap(); let uart = uart.as_mut().unwrap();
(f)(cs, uart) (f)(cs, uart)
}) })
} }
struct AlternativeLogger; #[allow(unused)]
macro_rules! println {
// TODO: I don't think this is necessary. Consider removing.
// () => {{
// do_print(Default::default());
// }};
impl Log for AlternativeLogger { ($($arg:tt)*) => {{
do_print(::core::format_args!($($arg)*));
}};
}
#[allow(unused)]
fn do_print(args: core::fmt::Arguments<'_>) {
with_uart_tx(|_, uart| {
uart.write_fmt(format_args!("{}\n", args)).unwrap();
uart.flush().unwrap();
})
}
struct UartLogger;
impl Log for UartLogger {
#[allow(unused)] #[allow(unused)]
fn enabled(&self, _: &log::Metadata) -> bool { fn enabled(&self, _: &log::Metadata) -> bool {
// Filtered by `log` already // Filtered by `log` already
@ -52,71 +131,17 @@ impl Log for AlternativeLogger {
#[allow(unused)] #[allow(unused)]
fn log(&self, record: &log::Record) { fn log(&self, record: &log::Record) {
with_uart_tx(|cs, uart| { with_uart_tx(|cs, uart| {
print_log_record(uart, record); with_formatted_log_record(record, |args| uart.write_fmt(args)).unwrap();
uart.flush().unwrap();
}) })
} }
fn flush(&self) {} fn flush(&self) {}
} }
const RESET: &str = "\u{001B}[0m"; #[panic_handler]
const RED: &str = "\u{001B}[31m"; fn panic_handler(info: &core::panic::PanicInfo) -> ! {
const GREEN: &str = "\u{001B}[32m"; use super::{RED, RESET};
const YELLOW: &str = "\u{001B}[33m";
const BLUE: &str = "\u{001B}[34m";
const CYAN: &str = "\u{001B}[35m";
#[cfg(feature = "rtt-log")]
#[allow(unused)]
use ::rtt_target::{rprint as print, rprintln as println};
#[cfg(feature = "alt-log")]
#[allow(unused)]
macro_rules! println {
() => {{
do_print(Default::default());
}};
($($arg:tt)*) => {{
do_print(::core::format_args!($($arg)*));
}};
}
#[allow(unused)]
fn do_print(args: core::fmt::Arguments<'_>) {
with_uart_tx(|_, uart| {
uart.write_fmt(args).unwrap();
uart.write_str("\n").unwrap();
uart.flush().unwrap();
})
}
fn print_log_record(uart: &mut UartTx<'_, Blocking>, record: &log::Record) {
let color = match record.level() {
log::Level::Error => RED,
log::Level::Warn => YELLOW,
log::Level::Info => GREEN,
log::Level::Debug => BLUE,
log::Level::Trace => CYAN,
};
let reset = RESET;
let args = format_args!(
"{}{:>5} - {}{}\n",
color,
record.level(),
record.args(),
reset
);
uart.write_fmt(args).unwrap();
uart.flush().unwrap();
}
#[cfg(feature = "rtt-log")]
use panic_rtt_target as _;
#[cfg(feature = "alt-log")]
#[panic_handler]
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
use esp_backtrace::Backtrace; use esp_backtrace::Backtrace;
println!("{RED}"); println!("{RED}");
@ -133,19 +158,45 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! {
} }
loop {} loop {}
} }
pub fn setup_logging(
uart: impl esp_hal::uart::Instance + 'static,
tx: impl PeripheralOutput<'static>,
rx: impl PeripheralInput<'static>,
) -> impl Future<Output = ()> {
let (uart_rx, uart_tx) = Uart::new(uart, Default::default())
.unwrap()
.with_tx(tx)
.with_rx(rx)
.split();
#[cfg(feature = "alt-log")]
pub fn setup_alternative_logging(
alt_uart: UartTx<'static, Blocking>,
level_filter: log::LevelFilter,
) {
critical_section::with(|cs| { critical_section::with(|cs| {
*ALT_LOGGER_UART.borrow(cs).borrow_mut() = Some(alt_uart); *ALT_LOGGER_UART.borrow(cs).borrow_mut() = Some(uart_tx);
}); });
unsafe { unsafe {
log::set_logger_racy(&AlternativeLogger).unwrap(); log::set_logger_racy(&UartLogger).unwrap();
log::set_max_level_racy(level_filter); log::set_max_level_racy(LOG_LEVEL_FILTER);
}
info!("Logger initialized!");
console::run_console(uart_rx.into_async())
}
}
/// Logging via RTT for probe-rs.
#[cfg(feature = "rtt-log")]
pub mod rtt {
use super::*;
#[allow(unused)]
use ::rtt_target::{rprint as print, rprintln as println};
use panic_rtt_target as _; // Use the RTT panic handler.
use rtt_target::ChannelMode;
pub fn setup_logging() -> impl Future<Output = ()> {
rtt_target::rtt_init_log!(LOG_LEVEL_FILTER, ChannelMode::BlockIfFull);
log::info!("Logger initialized!");
async {}
} }
} }

View file

@ -117,15 +117,6 @@ static SIGNAL_UI_RENDER: Signal<CriticalSectionRawMutex, ()> = Signal::new();
#[esp_rtos::main] #[esp_rtos::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
#[cfg(feature = "usb-log")]
{
esp_println::logger::init_logger(LOG_LEVEL_FILTER);
info!("Logger initialized!");
}
#[cfg(feature = "rtt-log")]
rtt_target::rtt_init_log!(LOG_LEVEL_FILTER);
let config = esp_hal::Config::default() let config = esp_hal::Config::default()
.with_cpu_clock(CpuClock::max()) .with_cpu_clock(CpuClock::max())
.with_psram(PsramConfig { .with_psram(PsramConfig {
@ -135,24 +126,14 @@ async fn main(_spawner: Spawner) {
ram_frequency: SpiRamFreq::Freq80m, ram_frequency: SpiRamFreq::Freq80m,
}); });
let peripherals: esp_hal::peripherals::Peripherals = esp_hal::init(config); let peripherals: esp_hal::peripherals::Peripherals = esp_hal::init(config);
info!("System initialized!");
#[cfg(feature = "usb-log")]
let console_task = logging::usb::setup_logging();
#[cfg(feature = "alt-log")] #[cfg(feature = "alt-log")]
let alt_uart_rx_task = { let console_task =
use esp_hal::uart::Uart; logging::uart::setup_logging(peripherals.UART2, peripherals.GPIO12, peripherals.GPIO5);
#[cfg(feature = "rtt-log")]
let (uart_rx, uart_tx) = Uart::new(peripherals.UART2, Default::default()) let console_task = logging::rtt::setup_logging();
.unwrap()
.with_tx(peripherals.GPIO12)
.with_rx(peripherals.GPIO5)
.split();
logging::setup_alternative_logging(uart_tx, LOG_LEVEL_FILTER);
info!("Logger initialized!");
console::run_console(uart_rx.into_async())
};
#[cfg(not(feature = "alt-log"))]
let alt_uart_rx_task = async {};
// Use the internal DRAM as the heap. // Use the internal DRAM as the heap.
// Memory reclaimed from the esp-idf bootloader. // Memory reclaimed from the esp-idf bootloader.
@ -444,7 +425,7 @@ async fn main(_spawner: Spawner) {
), ),
create_hid_report_interceptor(), create_hid_report_interceptor(),
user_controller.event_loop(), user_controller.event_loop(),
alt_uart_rx_task console_task
] ]
.await; .await;
} }