Compare commits

8 Commits

Author SHA1 Message Date
189a8c463f Merge pull request #5 from nicholasmello/multikey
Some checks are pending
Rust CI / Check (push) Waiting to run
Rust CI / Clippy (push) Waiting to run
Rust CI / Format (push) Waiting to run
Rust CI / Build (push) Blocked by required conditions
Rust CI / Documentation (push) Waiting to run
Rust CI / Deploy Docs (push) Blocked by required conditions
Multikey support
2026-07-25 16:23:34 -05:00
ea01fe2071 Multikey support
Signed-off-by: Nicholas Mello <nick@nmello.dev>
2026-07-25 16:20:51 -05:00
2b99a56a4a Merge pull request #4 from nicholasmello/multiple-buttons
Multiple buttons part 1
2026-07-25 15:17:09 -05:00
681c93f558 Multiple buttons part 1
Add and hook up all 6 buttons

Signed-off-by: Nicholas Mello <nick@nmello.dev>
2026-07-25 15:13:28 -05:00
0dc8e721b2 Merge pull request #3 from nicholasmello/add-ci
ci: Add rust workflow
2026-07-25 14:10:35 -05:00
e49e5b1311 ci: Add rust workflow
Signed-off-by: Nicholas Mello <nick@nmello.dev>
2026-07-25 14:08:16 -05:00
f8ee319fbe Merge pull request #2 from nicholasmello/nmello/bulk-send
keyboard: Update reporting logic
2026-07-24 18:37:25 -05:00
30f8cda48e keyboard: Update reporting logic
Updates the reporting logic to wait for any key press or depress before
reading all keys and sending an update of all of the keys

Signed-off-by: Nicholas Mello <nick@nmello.dev>
2026-07-24 18:33:48 -05:00
6 changed files with 397 additions and 127 deletions

152
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,152 @@
# .github/workflows/ci.yml
name: Rust CI
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
schedule:
- cron: '0 0 * * 1' # Weekly Sunday midnight UTC
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-Dwarnings"
CARGO_TERM_COLOR: always
jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1.17.0
with:
target: thumbv6m-none-eabi
- name: Cache dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: "."
- name: Run cargo check
run: cargo check
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1.17.0
with:
components: clippy
target: thumbv6m-none-eabi
- name: Cache dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: "."
- name: Run clippy
run: cargo clippy -- -D warnings
fmt:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1.17.0
with:
components: rustfmt
target: thumbv6m-none-eabi
- name: Check formatting
run: cargo fmt --all -- --check
build:
name: Build
runs-on: ubuntu-latest
needs: [check, clippy, fmt]
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1.17.0
with:
target: thumbv6m-none-eabi
- name: Cache dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: "."
- name: Build debug
run: cargo build --verbose
- name: Build release
run: cargo build --release --verbose
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: target/
retention-days: 7
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1.17.0
with:
components: rust-docs
target: thumbv6m-none-eabi
- name: Build documentation
run: cargo doc --no-deps --document-private-items
- name: Upload documentation
uses: actions/upload-artifact@v4
with:
name: documentation
path: target/thumbv6m-none-eabi/doc/
retention-days: 7
pages:
name: Deploy Docs
runs-on: ubuntu-latest
needs: [docs]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Build documentation
run: cargo doc --no-deps
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: target/doc/
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2

145
Cargo.lock generated
View File

@@ -36,7 +36,10 @@ dependencies = [
"embassy-sync",
"embassy-time",
"embassy-usb",
"embedded-alloc",
"futures-util",
"panic-probe",
"portable-atomic",
"usbd-hid",
]
@@ -76,6 +79,12 @@ dependencies = [
"rustc_version",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "bit-set"
version = "0.8.0"
@@ -173,6 +182,12 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "const-default"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa"
[[package]]
name = "cordyceps"
version = "0.3.4"
@@ -212,7 +227,7 @@ checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -276,7 +291,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -287,7 +302,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -324,7 +339,7 @@ dependencies = [
"defmt-parser",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -411,7 +426,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -587,6 +602,18 @@ dependencies = [
"embedded-io-async 0.7.0",
]
[[package]]
name = "embedded-alloc"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2db634e07f552215f7a3572dc0a4d29760c637fcea5a8c18d2cc291f782c216c"
dependencies = [
"const-default",
"critical-section",
"linked_list_allocator",
"rlsf",
]
[[package]]
name = "embedded-hal"
version = "0.2.7"
@@ -720,9 +747,12 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "futures-core"
version = "0.3.32"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
dependencies = [
"portable-atomic",
]
[[package]]
name = "futures-sink"
@@ -730,6 +760,26 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
[[package]]
name = "futures-task"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
[[package]]
name = "futures-util"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"portable-atomic",
"portable-atomic-util",
"slab",
]
[[package]]
name = "generator"
version = "0.8.9"
@@ -888,6 +938,12 @@ version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "linked_list_allocator"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897"
[[package]]
name = "litrs"
version = "1.0.0"
@@ -994,7 +1050,7 @@ checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -1118,14 +1174,26 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
name = "portable-atomic"
version = "1.13.1"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
dependencies = [
"critical-section",
]
[[package]]
name = "portable-atomic-util"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
dependencies = [
"portable-atomic",
]
[[package]]
name = "precomputed-hash"
@@ -1152,7 +1220,7 @@ dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -1238,6 +1306,19 @@ version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
[[package]]
name = "rlsf"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1646a59a9734b8b7a0ac51689388a60fe1625d4b956348e9de07591a1478457a"
dependencies = [
"cfg-if",
"const-default",
"libc",
"rustversion",
"svgbobdoc",
]
[[package]]
name = "rp-pac"
version = "7.0.0"
@@ -1334,7 +1415,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -1374,6 +1455,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
version = "1.15.2"
@@ -1432,6 +1519,30 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "svgbobdoc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50"
dependencies = [
"base64",
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-width",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.118"
@@ -1478,7 +1589,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -1509,7 +1620,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -1618,7 +1729,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn",
"syn 2.0.118",
"usbd-hid-descriptors",
]
@@ -1715,5 +1826,5 @@ checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]

View File

@@ -13,5 +13,8 @@ embassy-rp = { version = "0.10.0", features = ["chrono", "defmt", "rp2040", "rt"
embassy-sync = { version = "0.8.0", features = ["defmt"] }
embassy-time = "0.5.1"
embassy-usb = { version = "0.6.0", features = ["defmt"] }
embedded-alloc = "0.7.0"
futures-util = { version = "0.3.33", default-features = false, features = ["alloc", "portable-atomic-alloc"] }
panic-probe = { version = "1.0.0", features = ["defmt"] }
portable-atomic = { version = "1.14.0", default-features = false, features = ["critical-section"] }
usbd-hid = { version = "0.10.0", features = ["defmt"] }

View File

@@ -1,45 +1,14 @@
use crate::usb::{KeyboardAction, KeyboardEvent};
use defmt::*;
use alloc::vec::Vec;
use embassy_rp::gpio::Input;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::channel::Sender;
use futures_util::stream::{FuturesUnordered, StreamExt};
pub struct Key<'a> {
button: Input<'a>,
value: char,
}
impl Key<'_> {
pub async fn process<'ch, M, const N: usize>(
&mut self,
sender: Sender<'ch, M, KeyboardEvent, N>,
) where
M: RawMutex,
{
loop {
self.button.wait_for_high().await;
info!("Button {} pressed", self.value);
sender
.send(KeyboardEvent {
key: self.value,
action: KeyboardAction::Press,
})
.await;
self.button.wait_for_low().await;
info!("Button {} unpressed", self.value);
sender
.send(KeyboardEvent {
key: self.value,
action: KeyboardAction::Depress,
})
.await;
}
}
}
impl Key<'_> {
#[allow(unused)]
pub async fn set_value(mut self, value: char) {
@@ -52,6 +21,7 @@ pub struct Keyboard<'d, const KEY_N: usize> {
num_keys: usize,
}
#[derive(Debug, defmt::Format)]
pub enum KeyboardError {
MaxKeys,
}
@@ -76,11 +46,25 @@ impl<'d, const KEY_N: usize> Keyboard<'d, KEY_N> {
Ok(())
}
pub async fn process<'ch, M, const N: usize>(mut self, sender: Sender<'ch, M, KeyboardEvent, N>)
pub async fn process<'ch, M, const N: usize>(mut self, sender: Sender<'ch, M, Vec<char>, N>)
where
M: RawMutex,
{
// TODO: Make this a loop that is concatinated together
self.keys[0].as_mut().unwrap().process(sender).await;
loop {
self.keys
.iter_mut()
.filter_map(|opt| opt.as_mut().map(|s| s.button.wait_for_any_edge()))
.collect::<FuturesUnordered<_>>()
.next()
.await;
let mut pressed: Vec<char> = Vec::new();
for key in self.keys.iter_mut().filter_map(|opt| opt.as_mut()) {
if key.button.is_low() {
pressed.push(key.value);
}
}
sender.send(pressed).await;
}
}
}

View File

@@ -5,8 +5,10 @@ mod heartbeat;
mod keyboard;
mod usb;
extern crate alloc;
use crate::heartbeat::heartbeat;
use crate::keyboard::Keyboard;
use alloc::vec::Vec;
use defmt::*;
use embassy_executor::Spawner;
use embassy_futures::join::join;
@@ -16,6 +18,7 @@ use embassy_rp::peripherals::USB;
use embassy_rp::usb::{Driver, InterruptHandler};
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
use embassy_sync::channel::Channel;
use embedded_alloc::LlffHeap as Heap;
use {defmt_rtt as _, panic_probe as _};
@@ -23,9 +26,20 @@ bind_interrupts!(struct Irqs {
USBCTRL_IRQ => InterruptHandler<USB>;
});
#[global_allocator]
static HEAP: Heap = Heap::empty();
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let keyboard_events: Channel<ThreadModeRawMutex, usb::KeyboardEvent, 64> = Channel::new();
static mut HEAP_MEM: [u8; 4096] = [0; 4096];
unsafe {
HEAP.init(
core::ptr::addr_of!(HEAP_MEM) as usize,
core::mem::size_of::<[u8; 4096]>(),
);
}
let keyboard_events: Channel<ThreadModeRawMutex, Vec<char>, 64> = Channel::new();
let p = embassy_rp::init(Default::default());
@@ -33,18 +47,54 @@ async fn main(_spawner: Spawner) {
let driver = Driver::new(p.USB, Irqs);
let mut usb_buf = usb::UsbKeyboardBuf::new();
let usbkey = usb::UsbKeyboard::create_usb(&mut usb_buf, driver);
let mut keyboard = Keyboard::<6>::new();
// Set up the signal pin that will be used to trigger the keyboard.
let mut signal_pin = Input::new(p.PIN_16, Pull::None);
// Enable the schmitt trigger to slightly debounce.
let mut signal_pin = Input::new(p.PIN_15, Pull::None);
signal_pin.set_schmitt(true);
let mut keyboard = Keyboard::<1>::new();
match keyboard.add_key(signal_pin, 'a') {
Ok(()) => info!("Key 'a' registered"),
Err(_) => {
defmt::panic!("Failed to register key!");
Err(e) => {
defmt::panic!("Failed to register key! {:?}", e);
}
}
let mut signal_pin = Input::new(p.PIN_16, Pull::None);
signal_pin.set_schmitt(true);
match keyboard.add_key(signal_pin, 's') {
Ok(()) => info!("Key 's' registered"),
Err(e) => {
defmt::panic!("Failed to register key! {:?}", e);
}
}
let mut signal_pin = Input::new(p.PIN_17, Pull::None);
signal_pin.set_schmitt(true);
match keyboard.add_key(signal_pin, 'x') {
Ok(()) => info!("Key 'x' registered"),
Err(e) => {
defmt::panic!("Failed to register key! {:?}", e);
}
}
let mut signal_pin = Input::new(p.PIN_18, Pull::None);
signal_pin.set_schmitt(true);
match keyboard.add_key(signal_pin, 'd') {
Ok(()) => info!("Key 'd' registered"),
Err(e) => {
defmt::panic!("Failed to register key! {:?}", e);
}
}
let mut signal_pin = Input::new(p.PIN_19, Pull::None);
signal_pin.set_schmitt(true);
match keyboard.add_key(signal_pin, 'c') {
Ok(()) => info!("Key 'c' registered"),
Err(e) => {
defmt::panic!("Failed to register key! {:?}", e);
}
}
let mut signal_pin = Input::new(p.PIN_20, Pull::None);
signal_pin.set_schmitt(true);
match keyboard.add_key(signal_pin, 'w') {
Ok(()) => info!("Key 'w' registered"),
Err(e) => {
defmt::panic!("Failed to register key! {:?}", e);
}
}

View File

@@ -1,3 +1,4 @@
use alloc::vec::Vec;
use core::cell::OnceCell;
use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use defmt::*;
@@ -17,18 +18,6 @@ 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.
@@ -118,7 +107,7 @@ impl<'d> UsbKeyboard<'d> {
}
}
pub async fn run<'ch, M, const N: usize>(self, receiver: Receiver<'ch, M, KeyboardEvent, N>)
pub async fn run<'ch, M, const N: usize>(self, receiver: Receiver<'ch, M, Vec<char>, N>)
where
M: RawMutex,
{
@@ -138,58 +127,39 @@ impl<'d> UsbKeyboard<'d> {
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!");
}
let mut boot_keys = [0, 0, 0, 0, 0, 0, 0, 0];
for (&key, idx) in event.iter().zip(2usize..) {
let boot_key = match key {
'd' => 79,
'a' => 80,
's' => 81,
'w' => 82,
('a'..='z') => (key as u8) - b'a' + 4,
_ => defmt::panic!("Not supported key: {}", key),
};
boot_keys[idx] = boot_key;
}
if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {
match writer.write(&boot_keys).await {
Ok(()) => {}
Err(e) => warn!("Failed to send boot report: {:?}", e),
};
} else {
defmt::unimplemented!("Only supports boot mode keyboards!");
// // 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),
// };
}
}
};