use core::fmt::Arguments; use log::LevelFilter; 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 } }; 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( 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() { esp_println::logger::init_logger(LOG_LEVEL_FILTER); log::info!("Logger initialized!"); } } /// Alternative logger via UART. #[cfg(feature = "alt-log")] #[macro_use] 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}, uart::{Uart, UartTx}, }; use log::{Log, info}; 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) }) } #[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(format_args!("{}\n", args)).unwrap(); uart.flush().unwrap(); }) } struct UartLogger; impl Log for UartLogger { #[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| { with_formatted_log_record(record, |args| uart.write_fmt(args)).unwrap(); uart.flush().unwrap(); }) } fn flush(&self) {} } #[panic_handler] fn panic_handler(info: &core::panic::PanicInfo) -> ! { use super::{RED, RESET}; 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 {} } pub fn setup_logging(uart_tx: UartTx<'static, Blocking>) { critical_section::with(|cs| { *ALT_LOGGER_UART.borrow(cs).borrow_mut() = Some(uart_tx); }); unsafe { log::set_logger_racy(&UartLogger).unwrap(); log::set_max_level_racy(LOG_LEVEL_FILTER); } info!("Logger initialized!"); } } /// Logging via RTT for probe-rs. #[cfg(feature = "rtt-log")] pub mod rtt { use super::*; #[allow(unused)] pub 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() { rtt_target::rtt_init_log!(LOG_LEVEL_FILTER, ChannelMode::BlockIfFull); log::info!("Logger initialized!"); } } // #[macro_export] // macro_rules! dbg { // () => { // $crate::logging::implementation::println!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!()) // }; // ($val:expr $(,)?) => { // match $val { // tmp => { // $crate::logging::uart::println!("[{}:{}:{}] {} = {:#?}", // file!(), // line!(), // column!(), // stringify!($val), // // The `&T: Debug` check happens here (not in the format literal desugaring) // // to avoid format literal related messages and suggestions. // &&tmp as &dyn ::core::fmt::Debug, // ); // tmp // } // } // }; // ($($val:expr),+ $(,)?) => { // ($($crate::dbg!($val)),+,) // }; // } // #[cfg(feature = "alt-log")] // pub use uart as implementation; // #[cfg(feature = "rtt-log")] // pub use rtt as implementation;