mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 00:02:28 +00:00
Merge pull request #3486 from BjornTheProgrammer/main
Add ReceiverHandler to logger
This commit is contained in:
commit
763de8a37e
@ -3,6 +3,7 @@
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use core::fmt::Write as _;
|
||||
use core::future::Future;
|
||||
|
||||
use embassy_futures::join::join;
|
||||
use embassy_sync::pipe::Pipe;
|
||||
@ -13,6 +14,25 @@ use log::{Metadata, Record};
|
||||
|
||||
type CS = embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
|
||||
/// A trait that can be implemented and then passed to the
|
||||
pub trait ReceiverHandler {
|
||||
/// Data comes in from the serial port with each command and runs this function
|
||||
fn handle_data(&self, data: &[u8]) -> impl Future<Output = ()> + Send;
|
||||
|
||||
/// Create a new instance of the Handler
|
||||
fn new() -> Self;
|
||||
}
|
||||
|
||||
/// Use this Handler if you don't wish to use any handler
|
||||
pub struct DummyHandler;
|
||||
|
||||
impl ReceiverHandler for DummyHandler {
|
||||
async fn handle_data(&self, _data: &[u8]) {}
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
/// The logger state containing buffers that must live as long as the USB peripheral.
|
||||
pub struct LoggerState<'d> {
|
||||
state: State<'d>,
|
||||
@ -39,17 +59,19 @@ impl<'d> LoggerState<'d> {
|
||||
pub const MAX_PACKET_SIZE: u8 = 64;
|
||||
|
||||
/// The logger handle, which contains a pipe with configurable size for buffering log messages.
|
||||
pub struct UsbLogger<const N: usize> {
|
||||
pub struct UsbLogger<const N: usize, T: ReceiverHandler + Send + Sync> {
|
||||
buffer: Pipe<CS, N>,
|
||||
custom_style: Option<fn(&Record, &mut Writer<'_, N>) -> ()>,
|
||||
recieve_handler: Option<T>,
|
||||
}
|
||||
|
||||
impl<const N: usize> UsbLogger<N> {
|
||||
impl<const N: usize, T: ReceiverHandler + Send + Sync> UsbLogger<N, T> {
|
||||
/// Create a new logger instance.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
buffer: Pipe::new(),
|
||||
custom_style: None,
|
||||
recieve_handler: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,9 +80,15 @@ impl<const N: usize> UsbLogger<N> {
|
||||
Self {
|
||||
buffer: Pipe::new(),
|
||||
custom_style: Some(custom_style),
|
||||
recieve_handler: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a command handler to the logger
|
||||
pub fn with_handler(&mut self, handler: T) {
|
||||
self.recieve_handler = Some(handler);
|
||||
}
|
||||
|
||||
/// Run the USB logger using the state and USB driver. Never returns.
|
||||
pub async fn run<'d, D>(&'d self, state: &'d mut LoggerState<'d>, driver: D) -> !
|
||||
where
|
||||
@ -118,15 +146,22 @@ impl<const N: usize> UsbLogger<N> {
|
||||
}
|
||||
}
|
||||
};
|
||||
let discard_fut = async {
|
||||
let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
|
||||
let reciever_fut = async {
|
||||
let mut reciever_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
|
||||
receiver.wait_connection().await;
|
||||
loop {
|
||||
let _ = receiver.read_packet(&mut discard_buf).await;
|
||||
let n = receiver.read_packet(&mut reciever_buf).await.unwrap();
|
||||
match &self.recieve_handler {
|
||||
Some(handler) => {
|
||||
let data = &reciever_buf[..n];
|
||||
handler.handle_data(data).await;
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
join(log_fut, discard_fut).await;
|
||||
join(log_fut, reciever_fut).await;
|
||||
}
|
||||
|
||||
/// Creates the futures needed for the logger from a given class
|
||||
@ -142,7 +177,7 @@ impl<const N: usize> UsbLogger<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> log::Log for UsbLogger<N> {
|
||||
impl<const N: usize, T: ReceiverHandler + Send + Sync> log::Log for UsbLogger<N, T> {
|
||||
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
@ -182,7 +217,7 @@ impl<'d, const N: usize> core::fmt::Write for Writer<'d, N> {
|
||||
|
||||
/// Initialize and run the USB serial logger, never returns.
|
||||
///
|
||||
/// Arguments specify the buffer size, log level and the USB driver, respectively.
|
||||
/// Arguments specify the buffer size, log level and the USB driver, respectively. You can optionally add a RecieverHandler.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
@ -196,17 +231,27 @@ impl<'d, const N: usize> core::fmt::Write for Writer<'d, N> {
|
||||
#[macro_export]
|
||||
macro_rules! run {
|
||||
( $x:expr, $l:expr, $p:ident ) => {
|
||||
static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new();
|
||||
static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> =
|
||||
::embassy_usb_logger::UsbLogger::new();
|
||||
unsafe {
|
||||
let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
|
||||
}
|
||||
let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await;
|
||||
};
|
||||
|
||||
( $x:expr, $l:expr, $p:ident, $h:ty ) => {
|
||||
unsafe {
|
||||
static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> = ::embassy_usb_logger::UsbLogger::new();
|
||||
LOGGER.with_handler(<$h>::new());
|
||||
let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
|
||||
let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Initialize the USB serial logger from a serial class and return the future to run it.
|
||||
///
|
||||
/// Arguments specify the buffer size, log level and the serial class, respectively.
|
||||
/// Arguments specify the buffer size, log level and the serial class, respectively. You can optionally add a RecieverHandler.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
@ -220,19 +265,29 @@ macro_rules! run {
|
||||
#[macro_export]
|
||||
macro_rules! with_class {
|
||||
( $x:expr, $l:expr, $p:ident ) => {{
|
||||
static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new();
|
||||
static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> =
|
||||
::embassy_usb_logger::UsbLogger::new();
|
||||
unsafe {
|
||||
let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
|
||||
}
|
||||
LOGGER.create_future_from_class($p)
|
||||
}};
|
||||
|
||||
( $x:expr, $l:expr, $p:ident, $h:ty ) => {{
|
||||
unsafe {
|
||||
static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> = ::embassy_usb_logger::UsbLogger::new();
|
||||
LOGGER.with_handler(<$h>::new());
|
||||
let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
|
||||
LOGGER.create_future_from_class($p)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Initialize the USB serial logger from a serial class and return the future to run it.
|
||||
/// This version of the macro allows for a custom style function to be passed in.
|
||||
/// The custom style function will be called for each log record and is responsible for writing the log message to the buffer.
|
||||
///
|
||||
/// Arguments specify the buffer size, log level, the serial class and the custom style function, respectively.
|
||||
/// Arguments specify the buffer size, log level, the serial class and the custom style function, respectively. You can optionally add a RecieverHandler.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
@ -250,10 +305,21 @@ macro_rules! with_class {
|
||||
#[macro_export]
|
||||
macro_rules! with_custom_style {
|
||||
( $x:expr, $l:expr, $p:ident, $s:expr ) => {{
|
||||
static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::with_custom_style($s);
|
||||
static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> =
|
||||
::embassy_usb_logger::UsbLogger::with_custom_style($s);
|
||||
unsafe {
|
||||
let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
|
||||
}
|
||||
LOGGER.create_future_from_class($p)
|
||||
}};
|
||||
|
||||
( $x:expr, $l:expr, $p:ident, $s:expr, $h:ty ) => {{
|
||||
unsafe {
|
||||
static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> =
|
||||
::embassy_usb_logger::UsbLogger::with_custom_style($s);
|
||||
LOGGER.with_handler(<$h>::new());
|
||||
let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
|
||||
LOGGER.create_future_from_class($p)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
64
examples/rp/src/bin/usb_serial_with_handler.rs
Normal file
64
examples/rp/src/bin/usb_serial_with_handler.rs
Normal file
@ -0,0 +1,64 @@
|
||||
//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.
|
||||
//!
|
||||
//! This creates the possibility to send log::info/warn/error/debug! to USB serial port.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::str;
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::peripherals::USB;
|
||||
use embassy_rp::rom_data::reset_to_usb_boot;
|
||||
use embassy_rp::usb::{Driver, InterruptHandler};
|
||||
use embassy_time::Timer;
|
||||
use embassy_usb_logger::ReceiverHandler;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
USBCTRL_IRQ => InterruptHandler<USB>;
|
||||
});
|
||||
|
||||
struct Handler;
|
||||
|
||||
impl ReceiverHandler for Handler {
|
||||
async fn handle_data(&self, data: &[u8]) {
|
||||
if let Ok(data) = str::from_utf8(data) {
|
||||
let data = data.trim();
|
||||
|
||||
// If you are using elf2uf2-term with the '-t' flag, then when closing the serial monitor,
|
||||
// this will automatically put the pico into boot mode
|
||||
if data == "q" || data == "elf2uf2-term" {
|
||||
reset_to_usb_boot(0, 0); // Restart the chip
|
||||
} else if data.eq_ignore_ascii_case("hello") {
|
||||
log::info!("World!");
|
||||
} else {
|
||||
log::info!("Recieved: {:?}", data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn logger_task(driver: Driver<'static, USB>) {
|
||||
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver, Handler);
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let driver = Driver::new(p.USB, Irqs);
|
||||
spawner.spawn(logger_task(driver)).unwrap();
|
||||
|
||||
let mut counter = 0;
|
||||
loop {
|
||||
counter += 1;
|
||||
log::info!("Tick {}", counter);
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user