aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjorn <[email protected]>2024-10-31 22:51:03 -0700
committerBjorn <[email protected]>2024-10-31 22:51:03 -0700
commit333d8584812c0ea3e1f9262922befbd3fe709775 (patch)
tree4b9079f70f4283185ac7a7b94247ee7752e42967
parentf319f1bc1b36cd6c7860391e49083ee64c079547 (diff)
Added ReceiverHandler to logger
-rw-r--r--embassy-usb-logger/src/lib.rs92
-rw-r--r--examples/rp/src/bin/usb_serial_with_handler.rs64
2 files changed, 143 insertions, 13 deletions
diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs
index 11188b4ef..29c102f10 100644
--- a/embassy-usb-logger/src/lib.rs
+++ b/embassy-usb-logger/src/lib.rs
@@ -3,6 +3,7 @@
3#![warn(missing_docs)] 3#![warn(missing_docs)]
4 4
5use core::fmt::Write as _; 5use core::fmt::Write as _;
6use core::future::Future;
6 7
7use embassy_futures::join::join; 8use embassy_futures::join::join;
8use embassy_sync::pipe::Pipe; 9use embassy_sync::pipe::Pipe;
@@ -13,6 +14,25 @@ use log::{Metadata, Record};
13 14
14type CS = embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 15type CS = embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
15 16
17/// A trait that can be implemented and then passed to the
18pub trait ReceiverHandler {
19 /// Data comes in from the serial port with each command and runs this function
20 fn handle_data(&self, data: &[u8]) -> impl Future<Output = ()> + Send;
21
22 /// Create a new instance of the Handler
23 fn new() -> Self;
24}
25
26/// Use this Handler if you don't wish to use any handler
27pub struct DummyHandler;
28
29impl ReceiverHandler for DummyHandler {
30 async fn handle_data(&self, _data: &[u8]) {}
31 fn new() -> Self {
32 Self {}
33 }
34}
35
16/// The logger state containing buffers that must live as long as the USB peripheral. 36/// The logger state containing buffers that must live as long as the USB peripheral.
17pub struct LoggerState<'d> { 37pub struct LoggerState<'d> {
18 state: State<'d>, 38 state: State<'d>,
@@ -39,17 +59,19 @@ impl<'d> LoggerState<'d> {
39pub const MAX_PACKET_SIZE: u8 = 64; 59pub const MAX_PACKET_SIZE: u8 = 64;
40 60
41/// The logger handle, which contains a pipe with configurable size for buffering log messages. 61/// The logger handle, which contains a pipe with configurable size for buffering log messages.
42pub struct UsbLogger<const N: usize> { 62pub struct UsbLogger<const N: usize, T: ReceiverHandler + Send + Sync> {
43 buffer: Pipe<CS, N>, 63 buffer: Pipe<CS, N>,
44 custom_style: Option<fn(&Record, &mut Writer<'_, N>) -> ()>, 64 custom_style: Option<fn(&Record, &mut Writer<'_, N>) -> ()>,
65 recieve_handler: Option<T>,
45} 66}
46 67
47impl<const N: usize> UsbLogger<N> { 68impl<const N: usize, T: ReceiverHandler + Send + Sync> UsbLogger<N, T> {
48 /// Create a new logger instance. 69 /// Create a new logger instance.
49 pub const fn new() -> Self { 70 pub const fn new() -> Self {
50 Self { 71 Self {
51 buffer: Pipe::new(), 72 buffer: Pipe::new(),
52 custom_style: None, 73 custom_style: None,
74 recieve_handler: None,
53 } 75 }
54 } 76 }
55 77
@@ -58,9 +80,15 @@ impl<const N: usize> UsbLogger<N> {
58 Self { 80 Self {
59 buffer: Pipe::new(), 81 buffer: Pipe::new(),
60 custom_style: Some(custom_style), 82 custom_style: Some(custom_style),
83 recieve_handler: None,
61 } 84 }
62 } 85 }
63 86
87 /// Add a command handler to the logger
88 pub fn with_handler(&mut self, handler: T) {
89 self.recieve_handler = Some(handler);
90 }
91
64 /// Run the USB logger using the state and USB driver. Never returns. 92 /// Run the USB logger using the state and USB driver. Never returns.
65 pub async fn run<'d, D>(&'d self, state: &'d mut LoggerState<'d>, driver: D) -> ! 93 pub async fn run<'d, D>(&'d self, state: &'d mut LoggerState<'d>, driver: D) -> !
66 where 94 where
@@ -118,15 +146,22 @@ impl<const N: usize> UsbLogger<N> {
118 } 146 }
119 } 147 }
120 }; 148 };
121 let discard_fut = async { 149 let reciever_fut = async {
122 let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; 150 let mut reciever_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
123 receiver.wait_connection().await; 151 receiver.wait_connection().await;
124 loop { 152 loop {
125 let _ = receiver.read_packet(&mut discard_buf).await; 153 let n = receiver.read_packet(&mut reciever_buf).await.unwrap();
154 match &self.recieve_handler {
155 Some(handler) => {
156 let data = &reciever_buf[..n];
157 handler.handle_data(data).await;
158 }
159 None => (),
160 }
126 } 161 }
127 }; 162 };
128 163
129 join(log_fut, discard_fut).await; 164 join(log_fut, reciever_fut).await;
130 } 165 }
131 166
132 /// Creates the futures needed for the logger from a given class 167 /// Creates the futures needed for the logger from a given class
@@ -142,7 +177,7 @@ impl<const N: usize> UsbLogger<N> {
142 } 177 }
143} 178}
144 179
145impl<const N: usize> log::Log for UsbLogger<N> { 180impl<const N: usize, T: ReceiverHandler + Send + Sync> log::Log for UsbLogger<N, T> {
146 fn enabled(&self, _metadata: &Metadata) -> bool { 181 fn enabled(&self, _metadata: &Metadata) -> bool {
147 true 182 true
148 } 183 }
@@ -182,7 +217,7 @@ impl<'d, const N: usize> core::fmt::Write for Writer<'d, N> {
182 217
183/// Initialize and run the USB serial logger, never returns. 218/// Initialize and run the USB serial logger, never returns.
184/// 219///
185/// Arguments specify the buffer size, log level and the USB driver, respectively. 220/// Arguments specify the buffer size, log level and the USB driver, respectively. You can optionally add a RecieverHandler.
186/// 221///
187/// # Usage 222/// # Usage
188/// 223///
@@ -196,17 +231,27 @@ impl<'d, const N: usize> core::fmt::Write for Writer<'d, N> {
196#[macro_export] 231#[macro_export]
197macro_rules! run { 232macro_rules! run {
198 ( $x:expr, $l:expr, $p:ident ) => { 233 ( $x:expr, $l:expr, $p:ident ) => {
199 static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new(); 234 static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> =
235 ::embassy_usb_logger::UsbLogger::new();
200 unsafe { 236 unsafe {
201 let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); 237 let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
202 } 238 }
203 let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await; 239 let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await;
204 }; 240 };
241
242 ( $x:expr, $l:expr, $p:ident, $h:ty ) => {
243 unsafe {
244 static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> = ::embassy_usb_logger::UsbLogger::new();
245 LOGGER.with_handler(<$h>::new());
246 let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
247 let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await;
248 }
249 };
205} 250}
206 251
207/// Initialize the USB serial logger from a serial class and return the future to run it. 252/// Initialize the USB serial logger from a serial class and return the future to run it.
208/// 253///
209/// Arguments specify the buffer size, log level and the serial class, respectively. 254/// Arguments specify the buffer size, log level and the serial class, respectively. You can optionally add a RecieverHandler.
210/// 255///
211/// # Usage 256/// # Usage
212/// 257///
@@ -220,19 +265,29 @@ macro_rules! run {
220#[macro_export] 265#[macro_export]
221macro_rules! with_class { 266macro_rules! with_class {
222 ( $x:expr, $l:expr, $p:ident ) => {{ 267 ( $x:expr, $l:expr, $p:ident ) => {{
223 static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new(); 268 static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> =
269 ::embassy_usb_logger::UsbLogger::new();
224 unsafe { 270 unsafe {
225 let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); 271 let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
226 } 272 }
227 LOGGER.create_future_from_class($p) 273 LOGGER.create_future_from_class($p)
228 }}; 274 }};
275
276 ( $x:expr, $l:expr, $p:ident, $h:ty ) => {{
277 unsafe {
278 static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> = ::embassy_usb_logger::UsbLogger::new();
279 LOGGER.with_handler(<$h>::new());
280 let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
281 LOGGER.create_future_from_class($p)
282 }
283 }};
229} 284}
230 285
231/// Initialize the USB serial logger from a serial class and return the future to run it. 286/// Initialize the USB serial logger from a serial class and return the future to run it.
232/// This version of the macro allows for a custom style function to be passed in. 287/// This version of the macro allows for a custom style function to be passed in.
233/// The custom style function will be called for each log record and is responsible for writing the log message to the buffer. 288/// The custom style function will be called for each log record and is responsible for writing the log message to the buffer.
234/// 289///
235/// Arguments specify the buffer size, log level, the serial class and the custom style function, respectively. 290/// Arguments specify the buffer size, log level, the serial class and the custom style function, respectively. You can optionally add a RecieverHandler.
236/// 291///
237/// # Usage 292/// # Usage
238/// 293///
@@ -250,10 +305,21 @@ macro_rules! with_class {
250#[macro_export] 305#[macro_export]
251macro_rules! with_custom_style { 306macro_rules! with_custom_style {
252 ( $x:expr, $l:expr, $p:ident, $s:expr ) => {{ 307 ( $x:expr, $l:expr, $p:ident, $s:expr ) => {{
253 static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::with_custom_style($s); 308 static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> =
309 ::embassy_usb_logger::UsbLogger::with_custom_style($s);
254 unsafe { 310 unsafe {
255 let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); 311 let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
256 } 312 }
257 LOGGER.create_future_from_class($p) 313 LOGGER.create_future_from_class($p)
258 }}; 314 }};
315
316 ( $x:expr, $l:expr, $p:ident, $s:expr, $h:ty ) => {{
317 unsafe {
318 static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> =
319 ::embassy_usb_logger::UsbLogger::with_custom_style($s);
320 LOGGER.with_handler(<$h>::new());
321 let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
322 LOGGER.create_future_from_class($p)
323 }
324 }};
259} 325}
diff --git a/examples/rp/src/bin/usb_serial_with_handler.rs b/examples/rp/src/bin/usb_serial_with_handler.rs
new file mode 100644
index 000000000..a9e65be70
--- /dev/null
+++ b/examples/rp/src/bin/usb_serial_with_handler.rs
@@ -0,0 +1,64 @@
1//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.
2//!
3//! This creates the possibility to send log::info/warn/error/debug! to USB serial port.
4
5#![no_std]
6#![no_main]
7
8use core::str;
9
10use embassy_executor::Spawner;
11use embassy_rp::bind_interrupts;
12use embassy_rp::peripherals::USB;
13use embassy_rp::rom_data::reset_to_usb_boot;
14use embassy_rp::usb::{Driver, InterruptHandler};
15use embassy_time::Timer;
16use embassy_usb_logger::ReceiverHandler;
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 USBCTRL_IRQ => InterruptHandler<USB>;
21});
22
23struct Handler;
24
25impl ReceiverHandler for Handler {
26 async fn handle_data(&self, data: &[u8]) {
27 if let Ok(data) = str::from_utf8(data) {
28 let data = data.trim();
29
30 // If you are using elf2uf2-term with the '-t' flag, then when closing the serial monitor,
31 // this will automatically put the pico into boot mode
32 if data == "q" || data == "elf2uf2-term" {
33 reset_to_usb_boot(0, 0); // Restart the chip
34 } else if data.eq_ignore_ascii_case("hello") {
35 log::info!("World!");
36 } else {
37 log::info!("Recieved: {:?}", data);
38 }
39 }
40 }
41
42 fn new() -> Self {
43 Self
44 }
45}
46
47#[embassy_executor::task]
48async fn logger_task(driver: Driver<'static, USB>) {
49 embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver, Handler);
50}
51
52#[embassy_executor::main]
53async fn main(spawner: Spawner) {
54 let p = embassy_rp::init(Default::default());
55 let driver = Driver::new(p.USB, Irqs);
56 spawner.spawn(logger_task(driver)).unwrap();
57
58 let mut counter = 0;
59 loop {
60 counter += 1;
61 log::info!("Tick {}", counter);
62 Timer::after_secs(1).await;
63 }
64}