-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(example): add a USB HID keyboard example
- Loading branch information
1 parent
14946f4
commit ca737cd
Showing
7 changed files
with
262 additions
and
2 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
name = "embassy-usb-keyboard" | ||
version = "0.1.0" | ||
authors.workspace = true | ||
edition.workspace = true | ||
publish = false | ||
|
||
[dependencies] | ||
riot-rs = { path = "../../src/riot-rs", features = ["time", "usb"] } | ||
riot-rs-boards = { path = "../../src/riot-rs-boards" } | ||
embassy-executor = { workspace = true, default-features = false } | ||
embassy-nrf = { workspace = true, default-features = false } | ||
embassy-sync = { workspace = true } | ||
embassy-usb = { workspace = true, features = ["usbd-hid"] } | ||
embassy-time = { workspace = true, default-features = false } | ||
static_cell = { workspace = true } | ||
usbd-hid = { version = "0.6.1"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# embassy-usb-keyboard | ||
|
||
## About | ||
|
||
This application is testing basic | ||
[embassy](https://github.com/embassy-rs/embassy) _USB HID_ usage with RIOT-rs. | ||
|
||
## How to run | ||
|
||
In this folder, run | ||
|
||
laze build -b nrf52840dk run | ||
|
||
With the device USB cable connected, pressing Button 1 should send the keycode | ||
0x04 ('a') to the connected computer, and pressing Button 2 should send keycode | ||
0x05 ('b'). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
apps: | ||
- name: embassy-usb-keyboard | ||
context: nrf52840dk | ||
selects: | ||
- ?release |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
#![no_std] | ||
#![no_main] | ||
#![feature(type_alias_impl_trait)] | ||
#![feature(used_with_arg)] | ||
|
||
use embassy_time::Duration; | ||
use embassy_usb::class::hid::{self, HidReaderWriter}; | ||
use riot_rs::embassy::{make_static, UsbDriver}; | ||
use riot_rs::linkme::distributed_slice; | ||
use riot_rs::rt::debug::println; | ||
|
||
use usbd_hid::descriptor::KeyboardReport; | ||
|
||
mod pins; | ||
|
||
// TODO: wrap in macro | ||
use riot_rs::embassy::delegate::Delegate; | ||
static USB_BUILDER_HOOK: Delegate<riot_rs::embassy::UsbBuilder> = Delegate::new(); | ||
|
||
#[distributed_slice(riot_rs::embassy::USB_BUILDER_HOOKS)] | ||
#[linkme(crate=riot_rs::embassy::linkme)] | ||
static _USB_BUILDER_HOOK: &Delegate<riot_rs::embassy::UsbBuilder> = &USB_BUILDER_HOOK; | ||
|
||
#[embassy_executor::task] | ||
async fn usb_keyboard(button_peripherals: pins::Buttons) { | ||
let mut buttons = Buttons::new(button_peripherals); | ||
|
||
let config = embassy_usb::class::hid::Config { | ||
report_descriptor: <usbd_hid::descriptor::KeyboardReport as usbd_hid::descriptor::SerializedDescriptor>::desc(), | ||
request_handler: None, | ||
poll_ms: 60, | ||
max_packet_size: 64, | ||
}; | ||
|
||
let hid_state = make_static!(hid::State::new()); | ||
let hid_rw: HidReaderWriter<'static, UsbDriver, 1, 8> = USB_BUILDER_HOOK | ||
.with(|usb_builder| hid::HidReaderWriter::new(usb_builder, hid_state, config)) | ||
.await; | ||
|
||
let (_hid_reader, mut hid_writer) = hid_rw.split(); | ||
|
||
loop { | ||
for (i, button) in buttons.get_mut().iter_mut().enumerate() { | ||
if button.is_pressed() { | ||
println!("Button #{} pressed", i + 1); | ||
|
||
let report = keyboard_report(KEYCODE_MAPPING[i]); | ||
if let Err(e) = hid_writer.write_serialize(&report).await { | ||
println!("Failed to send report: {:?}", e); | ||
} | ||
let report = keyboard_report(KEY_RELEASED); | ||
if let Err(e) = hid_writer.write_serialize(&report).await { | ||
println!("Failed to send report: {:?}", e); | ||
} | ||
} | ||
} | ||
|
||
// Debounce events | ||
embassy_time::Timer::after(Duration::from_millis(50)).await; | ||
} | ||
} | ||
|
||
// TODO: macro up this | ||
use riot_rs::embassy::{arch::OptionalPeripherals, Spawner}; | ||
#[riot_rs::embassy::distributed_slice(riot_rs::embassy::EMBASSY_TASKS)] | ||
#[linkme(crate = riot_rs::embassy::linkme)] | ||
fn __init_usb_keyboard(spawner: &Spawner, peripherals: &mut OptionalPeripherals) { | ||
spawner | ||
.spawn(usb_keyboard(pins::Buttons::take_from(peripherals).unwrap())) | ||
.unwrap(); | ||
} | ||
|
||
use crate::buttons::{Buttons, KEY_COUNT}; | ||
|
||
// Assuming a QWERTY US layout, see https://docs.qmk.fm/#/how_keyboards_work | ||
// and https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf | ||
const KC_A: u8 = 0x04; | ||
const KC_C: u8 = 0x06; | ||
const KC_G: u8 = 0x0a; | ||
const KC_T: u8 = 0x17; | ||
|
||
const KEY_RELEASED: u8 = 0x00; | ||
|
||
fn keyboard_report(keycode: u8) -> KeyboardReport { | ||
KeyboardReport { | ||
keycodes: [keycode, 0, 0, 0, 0, 0], | ||
leds: 0, | ||
modifier: 0, | ||
reserved: 0, | ||
} | ||
} | ||
|
||
// Maps physical buttons to keycodes/characters | ||
const KEYCODE_MAPPING: [u8; KEY_COUNT as usize] = [KC_A, KC_C, KC_G, KC_T]; | ||
|
||
mod buttons { | ||
use embassy_nrf::gpio::{AnyPin, Input, Pin, Pull}; | ||
|
||
use crate::pins; | ||
|
||
pub const KEY_COUNT: u8 = 4; | ||
|
||
pub struct Button(Input<'static>); | ||
|
||
impl Button { | ||
pub fn new(button: AnyPin) -> Self { | ||
Self(Input::new(button, Pull::Up)) | ||
} | ||
|
||
pub fn is_pressed(&mut self) -> bool { | ||
self.0.is_low() | ||
} | ||
} | ||
|
||
pub struct Buttons([Button; KEY_COUNT as usize]); | ||
|
||
impl Buttons { | ||
pub fn new(button_peripherals: pins::Buttons) -> Self { | ||
Self([ | ||
Button::new(button_peripherals.btn1.degrade()), | ||
Button::new(button_peripherals.btn2.degrade()), | ||
Button::new(button_peripherals.btn3.degrade()), | ||
Button::new(button_peripherals.btn4.degrade()), | ||
]) | ||
} | ||
|
||
pub fn get(&self) -> &[Button] { | ||
&self.0 | ||
} | ||
|
||
pub fn get_mut(&mut self) -> &mut [Button] { | ||
&mut self.0 | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
use embassy_nrf::peripherals; | ||
use riot_rs::define_peripherals; | ||
|
||
#[cfg(builder = "nrf52840dk")] | ||
define_peripherals! { | ||
Buttons { | ||
btn1: P0_11, | ||
btn2: P0_12, | ||
btn3: P0_24, | ||
btn4: P0_25, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters