Skip to content

Commit

Permalink
feat(example): add a USB HID keyboard example
Browse files Browse the repository at this point in the history
  • Loading branch information
ROMemories authored and kaspar030 committed Feb 19, 2024
1 parent 489c831 commit 1865c07
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 2 deletions.
78 changes: 76 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions examples/embassy-usb-keyboard/Cargo.toml
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"}
16 changes: 16 additions & 0 deletions examples/embassy-usb-keyboard/README.md
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').
5 changes: 5 additions & 0 deletions examples/embassy-usb-keyboard/laze.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apps:
- name: embassy-usb-keyboard
context: nrf52840dk
selects:
- ?release
135 changes: 135 additions & 0 deletions examples/embassy-usb-keyboard/src/main.rs
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, usb::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::usb::UsbBuilder> = Delegate::new();

#[distributed_slice(riot_rs::embassy::usb::USB_BUILDER_HOOKS)]
#[linkme(crate=riot_rs::embassy::linkme)]
static _USB_BUILDER_HOOK: &Delegate<riot_rs::embassy::usb::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
}
}
}
12 changes: 12 additions & 0 deletions examples/embassy-usb-keyboard/src/pins.rs
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,
}
}
1 change: 1 addition & 0 deletions examples/laze.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ subdirs:
- embassy-net-tcp
- embassy-net-udp
# - embassy-gpio
- embassy-usb-keyboard
- hello-world
- minimal
- riot-app
Expand Down

0 comments on commit 1865c07

Please sign in to comment.