From 176649e71ad442ca9856af6c11989b0b2f228c4b Mon Sep 17 00:00:00 2001 From: matteo Date: Wed, 1 Oct 2025 18:56:38 +0200 Subject: update hid mouse and keyboard examples --- examples/rp/src/bin/usb_hid_keyboard.rs | 80 +++++++++++++++++++--------- examples/rp/src/bin/usb_hid_mouse.rs | 54 ++++++++++++++----- examples/rp235x/src/bin/usb_hid_keyboard.rs | 77 +++++++++++++++++--------- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 78 ++++++++++++++++++--------- examples/stm32f4/src/bin/usb_hid_mouse.rs | 50 +++++++++++++---- examples/stm32l5/src/bin/usb_hid_mouse.rs | 50 +++++++++++++---- 6 files changed, 282 insertions(+), 107 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs index adf91439e..2f6d169bf 100644 --- a/examples/rp/src/bin/usb_hid_keyboard.rs +++ b/examples/rp/src/bin/usb_hid_keyboard.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; @@ -10,7 +10,9 @@ use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Input, Pull}; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; -use embassy_usb::class::hid::{HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; @@ -20,6 +22,8 @@ bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -92,30 +96,46 @@ async fn main(_spawner: Spawner) { info!("Waiting for HIGH on pin 16"); signal_pin.wait_for_high().await; info!("HIGH DETECTED"); - // 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), - }; + + 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), + }; + } + signal_pin.wait_for_low().await; info!("LOW DETECTED"); - 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), - }; + 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), + }; + } } }; @@ -141,6 +161,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs index 3e62e8891..dc331cbdd 100755 --- a/examples/rp/src/bin/usb_hid_mouse.rs +++ b/examples/rp/src/bin/usb_hid_mouse.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; @@ -11,7 +11,9 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; use embassy_time::Timer; -use embassy_usb::class::hid::{HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; use rand::Rng; @@ -22,6 +24,8 @@ bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -89,17 +93,29 @@ async fn main(_spawner: Spawner) { loop { // every 1 second _ = Timer::after_secs(1).await; - let report = MouseReport { - buttons: 0, - x: rng.random_range(-100..100), // random small x movement - y: rng.random_range(-100..100), // random small y movement - wheel: 0, - pan: 0, - }; - // Send the report. - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), + + let x = rng.random_range(-100..100); // random small x movement + let y = rng.random_range(-100..100); // random small y movement + + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + let buttons = 0u8; + match writer.write(&[buttons, x as u8, y as u8]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + } + } else { + let report = MouseReport { + buttons: 0, + x, + y, + wheel: 0, + pan: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + } } } }; @@ -126,6 +142,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/rp235x/src/bin/usb_hid_keyboard.rs b/examples/rp235x/src/bin/usb_hid_keyboard.rs index b740a07b3..d8f64c470 100644 --- a/examples/rp235x/src/bin/usb_hid_keyboard.rs +++ b/examples/rp235x/src/bin/usb_hid_keyboard.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; @@ -11,7 +11,7 @@ use embassy_rp::gpio::{Input, Pull}; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; use embassy_usb::class::hid::{ - HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State as HidState, + HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State as HidState, }; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; @@ -22,6 +22,8 @@ bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -94,30 +96,45 @@ async fn main(_spawner: Spawner) { info!("Waiting for HIGH on pin 16"); signal_pin.wait_for_high().await; info!("HIGH DETECTED"); - // 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), - }; + + 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), + }; + } signal_pin.wait_for_low().await; info!("LOW DETECTED"); - 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), - }; + 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), + }; + } } }; @@ -143,6 +160,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index 5521a8240..86b6fa95f 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; @@ -11,7 +11,9 @@ use embassy_stm32::gpio::Pull; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; -use embassy_usb::class::hid::{HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; @@ -21,6 +23,8 @@ bind_interrupts!(struct Irqs { OTG_FS => usb::InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // @@ -127,32 +131,46 @@ async fn main(_spawner: Spawner) { button.wait_for_rising_edge().await; // signal_pin.wait_for_high().await; info!("Button pressed!"); - // 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), - }; + 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), + }; + } button.wait_for_falling_edge().await; // signal_pin.wait_for_low().await; info!("Button released!"); - 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), - }; + 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), + }; + } } }; @@ -178,6 +196,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index 5cfa0aec4..977db4c15 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] +use core::sync::atomic::{AtomicU8, Ordering}; + use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; @@ -8,7 +10,9 @@ use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_time::Timer; -use embassy_usb::class::hid::{HidBootProtocol, HidSubclass, HidWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; @@ -18,6 +22,8 @@ bind_interrupts!(struct Irqs { OTG_FS => usb::InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // @@ -114,16 +120,26 @@ async fn main(_spawner: Spawner) { Timer::after_millis(500).await; y = -y; - let report = MouseReport { - buttons: 0, - x: 0, - y, - wheel: 0, - pan: 0, - }; - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), + + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + let buttons = 0u8; + let x = 0i8; + match writer.write(&[buttons, x as u8, y as u8]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + } + } else { + let report = MouseReport { + buttons: 0, + x: 0, + y, + wheel: 0, + pan: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + } } } }; @@ -146,6 +162,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index f64fde3cb..8c7cdbef5 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -1,13 +1,17 @@ #![no_std] #![no_main] +use core::sync::atomic::{AtomicU8, Ordering}; + use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_time::Timer; -use embassy_usb::class::hid::{HidBootProtocol, HidSubclass, HidWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; @@ -17,6 +21,8 @@ bind_interrupts!(struct Irqs { USB_FS => usb::InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -96,16 +102,26 @@ async fn main(_spawner: Spawner) { Timer::after_millis(500).await; y = -y; - let report = MouseReport { - buttons: 0, - x: 0, - y, - wheel: 0, - pan: 0, - }; - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), + + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + let buttons = 0u8; + let x = 0i8; + match writer.write(&[buttons, x as u8, y as u8]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + } + } else { + let report = MouseReport { + buttons: 0, + x: 0, + y, + wheel: 0, + pan: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + } } } }; @@ -128,6 +144,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } -- cgit