use core::fmt::Write; use core::{cell::RefCell, fmt::Arguments}; use critical_section::{CriticalSection, Mutex}; use esp_hal::Blocking; use esp_hal::clock::CpuClock; use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock}; 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 } }; 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() -> impl Future { 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>>> = 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 { // TODO: I don't think this is necessary. Consider removing. // () => {{ // 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: impl esp_hal::uart::Instance + 'static, tx: impl PeripheralOutput<'static>, rx: impl PeripheralInput<'static>, ) -> impl Future { let (uart_rx, uart_tx) = Uart::new(uart, Default::default()) .unwrap() .with_tx(tx) .with_rx(rx) .split(); 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!"); 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 { rtt_target::rtt_init_log!(LOG_LEVEL_FILTER, ChannelMode::BlockIfFull); log::info!("Logger initialized!"); async {} } }