use core::fmt;
use embassy_futures::select::{Either, select};
use embassy_time::{Duration, Timer};
use esp_hal::{
gpio::{Event, Input, InputConfig, Pull},
peripherals::GPIO11,
};
use crate::{
Error, I2cBus, I2cDevice, I2cPins, i2c,
keys::{KeyEvent, KeyInput},
tca8418::{Tca8418, Wake},
};
#[derive(Debug)]
pub struct KeyboardPins {
pub i2c: I2cPins,
pub interrupt: GPIO11<'static>,
}
const FALLBACK_POLL: Duration = Duration::from_secs(1);
const SPURIOUS_BACKOFF: Duration = Duration::from_millis(10);
pub struct Keyboard {
controller: Tca8418<I2cDevice>,
interrupt: Input<'static>,
}
impl Keyboard {
pub async fn wait_for_inputs(
&mut self,
) -> Result<impl Iterator<Item = KeyInput> + use<>, Error> {
loop {
let mut presses = self
.wait_for_events()
.await?
.filter(|event| event.is_press())
.map(KeyEvent::input)
.peekable();
if presses.peek().is_some() {
return Ok(presses);
}
}
}
pub async fn wait_for_events(
&mut self,
) -> Result<impl Iterator<Item = KeyEvent> + use<>, Error> {
loop {
if let Some(events) = self.controller.take_events() {
return Ok(events);
}
let wake = if self.controller.is_pending() {
Wake::Pending
} else {
match select(self.interrupt.wait_for_low(), Timer::after(FALLBACK_POLL)).await {
Either::First(()) => Wake::Interrupt,
Either::Second(()) => Wake::Fallback,
}
};
let count = self
.controller
.read_events(wake)
.await
.map_err(|_| Error::Keyboard)?;
if count == 0 && wake == Wake::Interrupt {
Timer::after(SPURIOUS_BACKOFF).await;
}
}
}
pub fn listen_for_wake(&mut self) {
self.interrupt.listen(Event::LowLevel);
}
#[must_use]
pub fn is_held(&self, input: KeyInput) -> bool {
self.controller.is_held(input)
}
#[must_use]
pub const fn missed_interrupts(&self) -> u32 {
self.controller.missed_interrupts()
}
}
pub async fn keyboard(pins: KeyboardPins) -> Result<Keyboard, Error> {
let bus = i2c(pins.i2c)?;
keyboard_on(bus, pins.interrupt).await
}
const KEYBOARD_TRIES: u32 = 5;
const KEYBOARD_RETRY: Duration = Duration::from_millis(20);
pub async fn keyboard_on(
bus: &'static I2cBus,
interrupt: GPIO11<'static>,
) -> Result<Keyboard, Error> {
let mut keyboard = Keyboard {
controller: Tca8418::new(I2cDevice::new(bus)),
interrupt: Input::new(interrupt, InputConfig::default().with_pull(Pull::Up)),
};
for attempt in 0..KEYBOARD_TRIES {
if attempt > 0 {
Timer::after(KEYBOARD_RETRY).await;
}
if keyboard.controller.set_up().await.is_ok() {
return Ok(keyboard);
}
}
Err(Error::Keyboard)
}
impl fmt::Debug for Keyboard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Keyboard")
.field("pending", &self.controller.is_pending())
.field("missed_interrupts", &self.missed_interrupts())
.finish_non_exhaustive()
}
}