Send keyboard events using async Channel

This commit is contained in:
2026-07-13 09:27:37 -05:00
parent 937b3312eb
commit bd5b8f4e85
3 changed files with 133 additions and 70 deletions

View File

@@ -1,18 +1,9 @@
use core::sync::atomic::Ordering; use crate::usb::{KeyboardAction, KeyboardEvent};
use defmt::*; use defmt::*;
use embassy_rp::gpio::Input; 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_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> { pub struct Key<'a> {
button: Input<'a>, button: Input<'a>,
@@ -20,33 +11,22 @@ pub struct Key<'a> {
} }
impl Key<'_> { impl Key<'_> {
pub async fn process<'d, D: Driver<'d>>( pub async fn process<'ch, M, const N: usize>(
&mut self, &mut self,
mut writer: Mutex<CriticalSectionRawMutex, HidWriter<'d, D, WRITE_N>>, sender: Sender<'ch, M, KeyboardEvent, N>,
) { ) where
M: RawMutex,
{
loop { loop {
self.button.wait_for_high().await; self.button.wait_for_high().await;
info!("Button {} pressed", self.value); info!("Button {} pressed", self.value);
if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { sender
match writer.get_mut().write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { .send(KeyboardEvent {
Ok(()) => {} key: self.value,
Err(e) => warn!("Failed to send boot report: {:?}", e), action: KeyboardAction::Press,
}; })
} else { .await;
// 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),
};
}
// Debounce // Debounce
Timer::after(Duration::from_millis(50)).await; Timer::after(Duration::from_millis(50)).await;
@@ -54,23 +34,12 @@ impl Key<'_> {
self.button.wait_for_low().await; self.button.wait_for_low().await;
info!("Button {} unpressed", self.value); info!("Button {} unpressed", self.value);
if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { sender
match writer.get_mut().write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { .send(KeyboardEvent {
Ok(()) => {} key: self.value,
Err(e) => warn!("Failed to send boot report: {:?}", e), action: KeyboardAction::Depress,
}; })
} else { .await;
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),
};
}
// Debounce // Debounce
Timer::after(Duration::from_millis(50)).await; Timer::after(Duration::from_millis(50)).await;
@@ -85,8 +54,7 @@ impl Key<'_> {
} }
} }
pub struct Keyboard<'d, D: Driver<'d>, const KEY_N: usize> { pub struct Keyboard<'d, const KEY_N: usize> {
writer: Mutex<CriticalSectionRawMutex, HidWriter<'d, D, WRITE_N>>,
keys: [Option<Key<'d>>; KEY_N], keys: [Option<Key<'d>>; KEY_N],
num_keys: usize, num_keys: usize,
} }
@@ -95,10 +63,9 @@ pub enum KeyboardError {
MaxKeys, MaxKeys,
} }
impl<'d, D: Driver<'d>, const KEY_N: usize> Keyboard<'d, D, KEY_N> { impl<'d, const KEY_N: usize> Keyboard<'d, KEY_N> {
pub fn new(writer: HidWriter<'d, D, WRITE_N>) -> Self { pub fn new() -> Self {
Self { Self {
writer: Mutex::new(writer),
keys: [const { None }; KEY_N], keys: [const { None }; KEY_N],
num_keys: 0, num_keys: 0,
} }
@@ -116,8 +83,11 @@ impl<'d, D: Driver<'d>, const KEY_N: usize> Keyboard<'d, D, KEY_N> {
Ok(()) 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 // 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;
} }
} }

View File

@@ -14,6 +14,8 @@ use embassy_rp::bind_interrupts;
use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::gpio::{Input, Level, Output, Pull};
use embassy_rp::peripherals::USB; use embassy_rp::peripherals::USB;
use embassy_rp::usb::{Driver, InterruptHandler}; 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 _}; use {defmt_rtt as _, panic_probe as _};
@@ -23,12 +25,14 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let keyboard_events: Channel<ThreadModeRawMutex, usb::KeyboardEvent, 64> = Channel::new();
let p = embassy_rp::init(Default::default()); let p = embassy_rp::init(Default::default());
// Create the driver, from the HAL. // Create the driver, from the HAL.
let driver = Driver::new(p.USB, Irqs); let driver = Driver::new(p.USB, Irqs);
let mut usb_buf = usb::UsbKeyboardBuf::new(); 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. // Set up the signal pin that will be used to trigger the keyboard.
let mut signal_pin = Input::new(p.PIN_16, Pull::None); 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. // Enable the schmitt trigger to slightly debounce.
signal_pin.set_schmitt(true); 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') { match keyboard.add_key(signal_pin, 'a') {
Ok(()) => info!("Key 'a' registered"), Ok(()) => info!("Key 'a' registered"),
Err(_) => { Err(_) => {
@@ -46,5 +50,9 @@ async fn main(_spawner: Spawner) {
let led = Output::new(p.PIN_25, Level::Low); 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;
} }

View File

@@ -4,6 +4,8 @@ use defmt::*;
use embassy_futures::join::join; use embassy_futures::join::join;
use embassy_rp::peripherals::USB; use embassy_rp::peripherals::USB;
use embassy_rp::usb::Driver; use embassy_rp::usb::Driver;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::channel::Receiver;
use embassy_usb::UsbDevice; use embassy_usb::UsbDevice;
use embassy_usb::class::hid::{ use embassy_usb::class::hid::{
HidBootProtocol, HidProtocolMode, HidReader, HidReaderWriter, HidSubclass, HidWriter, ReportId, 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); 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> { pub struct UsbKeyboardBuf<'d> {
// Create embassy-usb DeviceBuilder using the driver and config. // Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors. // It needs some buffers for building the descriptors.
@@ -44,14 +58,12 @@ impl<'d> UsbKeyboardBuf<'d> {
pub struct UsbKeyboard<'d> { pub struct UsbKeyboard<'d> {
usb: UsbDevice<'d, Driver<'d, USB>>, usb: UsbDevice<'d, Driver<'d, USB>>,
reader: HidReader<'d, Driver<'d, USB>, 1>, reader: HidReader<'d, Driver<'d, USB>, 1>,
writer: HidWriter<'d, Driver<'d, USB>, 8>,
} }
impl<'d> UsbKeyboard<'d> { impl<'d> UsbKeyboard<'d> {
// HidWriter<'_, Driver<'_, USB>, 8> // HidWriter<'_, Driver<'_, USB>, 8>
pub fn create_usb( pub fn create_usb(buf: &'d mut UsbKeyboardBuf<'d>, driver: Driver<'d, USB>) -> Self {
buf: &'d mut UsbKeyboardBuf<'d>,
driver: Driver<'d, USB>,
) -> (Self, HidWriter<'d, Driver<'d, USB>, 8>) {
// Create embassy-usb Config // Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe); let mut config = Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Nicholas Mello"); config.manufacturer = Some("Nicholas Mello");
@@ -99,17 +111,90 @@ impl<'d> UsbKeyboard<'d> {
let usb = builder.build(); 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 mut request_handler = MyRequestHandler {};
let out_fut = async { let Self {
self.reader.run(false, &mut request_handler).await; 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;
} }
} }