use embassy_time::{Duration, Instant, Timer};
use crate::driver::ft6336u;
use crate::io::buttons::{ButtonAction, ButtonEvent, ButtonId};
use crate::io::shared_i2c::SharedI2cBus;
pub struct TouchButtonsConfig {
pub poll_ms: u64,
pub long_press_ms: u64,
pub multi_tap_ms: u64,
pub zone_y_min: u16,
pub zone_x_left: u16,
pub zone_x_right: u16,
}
impl Default for TouchButtonsConfig {
fn default() -> Self {
Self {
poll_ms: 20,
long_press_ms: 500,
multi_tap_ms: 300,
zone_y_min: 200,
zone_x_left: 107,
zone_x_right: 213,
}
}
}
pub struct TouchButtons {
i2c: &'static SharedI2cBus,
config: TouchButtonsConfig,
probed: bool,
pressed: Option<ButtonId>,
press_start: Instant,
long_fired: bool,
tap_zone: Option<ButtonId>,
tap_count: usize,
last_release: Instant,
}
impl TouchButtons {
pub fn new(i2c: &'static SharedI2cBus, config: TouchButtonsConfig) -> Self {
Self {
i2c,
config,
probed: false,
pressed: None,
press_start: Instant::now(),
long_fired: false,
tap_zone: None,
tap_count: 0,
last_release: Instant::now(),
}
}
fn classify(&self, x: u16, y: u16) -> Option<ButtonId> {
if y < self.config.zone_y_min {
return None;
}
Some(if x < self.config.zone_x_left {
ButtonId::Left
} else if x < self.config.zone_x_right {
ButtonId::Center
} else {
ButtonId::Right
})
}
async fn probe(&mut self) {
let mut attempts = 0u32;
loop {
match ft6336u::read_touch(self.i2c).await {
Ok(_) => {
info!("FT6336U found at 0x{:02X}", ft6336u::ADDR);
self.probed = true;
return;
}
Err(e) => {
attempts += 1;
if attempts <= 3 || attempts % 50 == 0 {
warn!("FT6336U probe #{}: {:?}", attempts, e);
}
}
}
Timer::after(Duration::from_millis(100)).await;
}
}
pub async fn next_event(&mut self) -> ButtonEvent {
if !self.probed {
self.probe().await;
}
loop {
Timer::after(Duration::from_millis(self.config.poll_ms)).await;
let touch = match ft6336u::read_touch(self.i2c).await {
Ok(t) => t,
Err(e) => {
warn!("touch I2C err: {:?}", e);
continue;
}
};
let cur_zone = touch.and_then(|(x, y)| self.classify(x, y));
match (self.pressed, cur_zone) {
(None, None) => {
if self.tap_count > 0
&& self.last_release.elapsed()
> Duration::from_millis(self.config.multi_tap_ms)
{
let id = self.tap_zone.take().unwrap();
let count = core::mem::take(&mut self.tap_count);
return ButtonEvent { id, action: ButtonAction::Short(count) };
}
}
(None, Some(zone)) => {
self.pressed = Some(zone);
self.press_start = Instant::now();
self.long_fired = false;
}
(Some(zone), Some(_cur)) => {
if !self.long_fired
&& self.press_start.elapsed()
> Duration::from_millis(self.config.long_press_ms)
{
self.long_fired = true;
self.tap_count = 0;
self.tap_zone = None;
return ButtonEvent { id: zone, action: ButtonAction::Long };
}
}
(Some(zone), None) => {
self.pressed = None;
if !self.long_fired {
if self.tap_zone == Some(zone) {
self.tap_count += 1;
self.last_release = Instant::now();
} else {
let pending = self
.tap_zone
.replace(zone)
.filter(|_| self.tap_count > 0)
.map(|old| ButtonEvent {
id: old,
action: ButtonAction::Short(self.tap_count),
});
self.tap_count = 1;
self.last_release = Instant::now();
if let Some(event) = pending {
return event;
}
}
}
}
}
}
}
}