use core::cell::RefCell; use core::fmt::Write; use critical_section::{CriticalSection, Mutex}; use esp_hal::Blocking; use esp_hal::uart::UartTx; use log::{LevelFilter, Log}; pub const LOG_LEVEL_FILTER: LevelFilter = { if let Some(string) = option_env!("ESP_LOG") { if string.eq_ignore_ascii_case("ERROR") { LevelFilter::Error } else if string.eq_ignore_ascii_case("WARN") { LevelFilter::Warn } else if string.eq_ignore_ascii_case("INFO") { LevelFilter::Info } else if string.eq_ignore_ascii_case("DEBUG") { LevelFilter::Debug } else if string.eq_ignore_ascii_case("TRACE") { LevelFilter::Trace } else { panic!("Unknown `ESP_LOG` value. Only `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`, or `OFF` may be used."); } } else { LevelFilter::Off } }; static ALT_LOGGER_UART: Mutex>>> = Mutex::new(RefCell::new(None)); pub fn with_uart_tx( f: impl FnOnce(CriticalSection<'_>, &'_ mut UartTx<'static, Blocking>) -> R, ) -> R { critical_section::with(|cs| { let mut uart = ALT_LOGGER_UART.borrow(cs).borrow_mut(); let uart = uart.as_mut().unwrap(); (f)(cs, uart) }) } struct AlternativeLogger; impl Log for AlternativeLogger { #[allow(unused)] fn enabled(&self, _: &log::Metadata) -> bool { // Filtered by `log` already true } #[allow(unused)] fn log(&self, record: &log::Record) { with_uart_tx(|cs, uart| { print_log_record(uart, record); }) } fn flush(&self) {} } 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"; #[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; println!("{RED}"); println!("=============== CUSTOM PANIC HANDLER =============="); println!("{info}{RESET}"); println!(""); println!("Backtrace:"); println!(""); let backtrace = Backtrace::capture(); for frame in backtrace.frames() { println!("0x{:x}", frame.program_counter()); } loop {} } #[cfg(feature = "alt-log")] pub fn setup_alternative_logging( alt_uart: UartTx<'static, Blocking>, level_filter: log::LevelFilter, ) { critical_section::with(|cs| { *ALT_LOGGER_UART.borrow(cs).borrow_mut() = Some(alt_uart); }); unsafe { log::set_logger_racy(&AlternativeLogger).unwrap(); log::set_max_level_racy(level_filter); } }