From bd5b8f4e85e8d4d1f740101581d4f7b74dc10475 Mon Sep 17 00:00:00 2001 From: Brandon Maier Date: Mon, 13 Jul 2026 09:27:37 -0500 Subject: [PATCH] Send keyboard events using async Channel --- src/keyboard.rs | 86 +++++++++++++--------------------------- src/main.rs | 14 +++++-- src/usb.rs | 103 +++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 133 insertions(+), 70 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 80c96e0..c30471d 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,18 +1,9 @@ -use core::sync::atomic::Ordering; - +use crate::usb::{KeyboardAction, KeyboardEvent}; use defmt::*; use embassy_rp::gpio::Input; -use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex}; +use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::channel::Sender; use embassy_time::{Duration, Timer}; -use embassy_usb::{ - class::hid::{HidProtocolMode, HidWriter}, - driver::Driver, -}; -use usbd_hid::descriptor::KeyboardReport; - -use crate::usb::HID_PROTOCOL_MODE; - -const WRITE_N: usize = 8; pub struct Key<'a> { button: Input<'a>, @@ -20,33 +11,22 @@ pub struct Key<'a> { } impl Key<'_> { - pub async fn process<'d, D: Driver<'d>>( + pub async fn process<'ch, M, const N: usize>( &mut self, - mut writer: Mutex>, - ) { + sender: Sender<'ch, M, KeyboardEvent, N>, + ) where + M: RawMutex, + { loop { self.button.wait_for_high().await; info!("Button {} pressed", self.value); - if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { - match writer.get_mut().write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { - Ok(()) => {} - Err(e) => warn!("Failed to send boot report: {:?}", e), - }; - } else { - // Create a report with the A key pressed. (no shift modifier) - let report = KeyboardReport { - keycodes: [4, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - // Send the report. - match writer.get_mut().write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; - } + sender + .send(KeyboardEvent { + key: self.value, + action: KeyboardAction::Press, + }) + .await; // Debounce Timer::after(Duration::from_millis(50)).await; @@ -54,23 +34,12 @@ impl Key<'_> { self.button.wait_for_low().await; info!("Button {} unpressed", self.value); - if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { - match writer.get_mut().write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { - Ok(()) => {} - Err(e) => warn!("Failed to send boot report: {:?}", e), - }; - } else { - let report = KeyboardReport { - keycodes: [0, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - match writer.get_mut().write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; - } + sender + .send(KeyboardEvent { + key: self.value, + action: KeyboardAction::Depress, + }) + .await; // Debounce Timer::after(Duration::from_millis(50)).await; @@ -85,8 +54,7 @@ impl Key<'_> { } } -pub struct Keyboard<'d, D: Driver<'d>, const KEY_N: usize> { - writer: Mutex>, +pub struct Keyboard<'d, const KEY_N: usize> { keys: [Option>; KEY_N], num_keys: usize, } @@ -95,10 +63,9 @@ pub enum KeyboardError { MaxKeys, } -impl<'d, D: Driver<'d>, const KEY_N: usize> Keyboard<'d, D, KEY_N> { - pub fn new(writer: HidWriter<'d, D, WRITE_N>) -> Self { +impl<'d, const KEY_N: usize> Keyboard<'d, KEY_N> { + pub fn new() -> Self { Self { - writer: Mutex::new(writer), keys: [const { None }; KEY_N], num_keys: 0, } @@ -116,8 +83,11 @@ impl<'d, D: Driver<'d>, const KEY_N: usize> Keyboard<'d, D, KEY_N> { Ok(()) } - pub async fn process(mut self) { + pub async fn process<'ch, M, const N: usize>(mut self, sender: Sender<'ch, M, KeyboardEvent, N>) + where + M: RawMutex, + { // TODO: Make this a loop that is concatinated together - self.keys[0].as_mut().unwrap().process(self.writer).await; + self.keys[0].as_mut().unwrap().process(sender).await; } } diff --git a/src/main.rs b/src/main.rs index d39ae1c..e96bccd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,8 @@ use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; use {defmt_rtt as _, panic_probe as _}; @@ -23,12 +25,14 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { + let keyboard_events: Channel = Channel::new(); + let p = embassy_rp::init(Default::default()); // Create the driver, from the HAL. let driver = Driver::new(p.USB, Irqs); let mut usb_buf = usb::UsbKeyboardBuf::new(); - let (usbkey, writer) = usb::UsbKeyboard::create_usb(&mut usb_buf, driver); + let usbkey = usb::UsbKeyboard::create_usb(&mut usb_buf, driver); // Set up the signal pin that will be used to trigger the keyboard. let mut signal_pin = Input::new(p.PIN_16, Pull::None); @@ -36,7 +40,7 @@ async fn main(_spawner: Spawner) { // Enable the schmitt trigger to slightly debounce. signal_pin.set_schmitt(true); - let mut keyboard = Keyboard::<_, 1>::new(writer); + let mut keyboard = Keyboard::<1>::new(); match keyboard.add_key(signal_pin, 'a') { Ok(()) => info!("Key 'a' registered"), Err(_) => { @@ -46,5 +50,9 @@ async fn main(_spawner: Spawner) { let led = Output::new(p.PIN_25, Level::Low); - join(join(usbkey.run(), heartbeat(led)), keyboard.process()).await; + join( + join(usbkey.run(keyboard_events.receiver()), heartbeat(led)), + keyboard.process(keyboard_events.sender()), + ) + .await; } diff --git a/src/usb.rs b/src/usb.rs index cb67916..dcbd87a 100644 --- a/src/usb.rs +++ b/src/usb.rs @@ -4,6 +4,8 @@ use defmt::*; use embassy_futures::join::join; use embassy_rp::peripherals::USB; use embassy_rp::usb::Driver; +use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::channel::Receiver; use embassy_usb::UsbDevice; use embassy_usb::class::hid::{ HidBootProtocol, HidProtocolMode, HidReader, HidReaderWriter, HidSubclass, HidWriter, ReportId, @@ -15,6 +17,18 @@ use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; pub static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); +#[derive(Clone, Copy)] +pub enum KeyboardAction { + Press, + Depress, +} + +#[derive(Clone, Copy)] +pub struct KeyboardEvent { + pub key: char, + pub action: KeyboardAction, +} + pub struct UsbKeyboardBuf<'d> { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -44,14 +58,12 @@ impl<'d> UsbKeyboardBuf<'d> { pub struct UsbKeyboard<'d> { usb: UsbDevice<'d, Driver<'d, USB>>, reader: HidReader<'d, Driver<'d, USB>, 1>, + writer: HidWriter<'d, Driver<'d, USB>, 8>, } impl<'d> UsbKeyboard<'d> { // HidWriter<'_, Driver<'_, USB>, 8> - pub fn create_usb( - buf: &'d mut UsbKeyboardBuf<'d>, - driver: Driver<'d, USB>, - ) -> (Self, HidWriter<'d, Driver<'d, USB>, 8>) { + pub fn create_usb(buf: &'d mut UsbKeyboardBuf<'d>, driver: Driver<'d, USB>) -> Self { // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); config.manufacturer = Some("Nicholas Mello"); @@ -99,17 +111,90 @@ impl<'d> UsbKeyboard<'d> { let usb = builder.build(); - (Self { usb, reader }, writer) + Self { + usb, + reader, + writer, + } } - pub async fn run(mut self) { + pub async fn run<'ch, M, const N: usize>(self, receiver: Receiver<'ch, M, KeyboardEvent, N>) + where + M: RawMutex, + { let mut request_handler = MyRequestHandler {}; - let out_fut = async { - self.reader.run(false, &mut request_handler).await; + let Self { + mut usb, + reader, + mut writer, + } = self; + + let reader_fut = async { + reader.run(false, &mut request_handler).await; }; - join(self.usb.run(), out_fut).await; + let writer_fut = async { + loop { + let event = receiver.receive().await; + + match event { + KeyboardEvent { + key: 'a', + action: KeyboardAction::Press, + } => { + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 + { + match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + // Create a report with the A key pressed. (no shift modifier) + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } + } + KeyboardEvent { + key: 'a', + action: KeyboardAction::Depress, + } => { + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 + { + match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } + } + _ => { + defmt::panic!("Unexpected event!"); + } + } + } + }; + + join(usb.run(), join(reader_fut, writer_fut)).await; } }