use embassy_time::{Duration, Instant, Timer};
use embedded_hal::i2c::Operation;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::i2c::I2c;
use rmk_macro::input_device;
use crate::event::{AxisEvent, PointingEvent};
use crate::fmt::Debug;
const I2C_ADDR: u8 = 0x74;
const END_SESSION: [u8; 2] = [0xEE, 0xEE];
#[input_device(publish = PointingEvent)]
pub struct Iqs5xx<I, RDY>
where
I: I2c,
I::Error: Debug,
RDY: Wait,
{
pointing_device_id: u8,
i2c: I,
window_detection: WindowDetection<RDY>,
initialized: bool,
}
pub enum WindowDetection<RDY> {
Rdy(RDY),
Poll { last_end: Instant, interval_ms: u16 },
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
enum Error<I2cError> {
I2c { tag: &'static str, inner: I2cError },
InvalidProductInfo([u8; 4]),
Reset,
}
async fn i2c_force_tx<'a, I: I2c>(
i2c: &mut I,
tag: &'static str,
operations: &mut [Operation<'a>],
) -> Result<(), Error<I::Error>> {
const MAX_ATTEMPTS: usize = 3;
let mut attempt = 0;
while let Err(e) = i2c.transaction(I2C_ADDR, operations).await {
attempt += 1;
if attempt == MAX_ATTEMPTS {
return Err(Error::I2c { tag, inner: e });
}
Timer::after(Duration::from_micros(200)).await;
}
Ok(())
}
async fn i2c_tx<'a, I: I2c>(
i2c: &mut I,
tag: &'static str,
operations: &mut [Operation<'a>],
) -> Result<(), Error<I::Error>> {
i2c.transaction(I2C_ADDR, operations)
.await
.map_err(|inner| Error::I2c { tag, inner })
}
impl<I: I2c, RDY> Iqs5xx<I, RDY>
where
I: I2c,
I::Error: Debug,
RDY: Wait,
{
pub fn new(rmk_id: u8, i2c: I, rdy: Option<RDY>) -> Self {
Self {
i2c,
window_detection: match rdy {
None => WindowDetection::Poll {
last_end: Instant::now(),
interval_ms: 15, },
Some(rdy) => WindowDetection::Rdy(rdy),
},
initialized: false,
pointing_device_id: rmk_id,
}
}
async fn init(&mut self) -> Result<(), Error<I::Error>> {
let mut product_info = [0u8; 4]; i2c_force_tx(
&mut self.i2c,
"read_product_info",
&mut [Operation::Write(&[0, 0]), Operation::Read(&mut product_info)],
)
.await?;
let ic = match product_info {
[0, 40, 0, 15] => "IQS550",
[0, 58, 0, 15] => "IQS572",
[0, 52, 0, 15] => "IQS525",
_ => {
return Err(Error::InvalidProductInfo(product_info));
}
};
let mut channels = [0u8; 2]; i2c_tx(
&mut self.i2c,
"read_channels",
&mut [Operation::Write(&[0x06, 0x3D]), Operation::Read(&mut channels)],
)
.await?;
let x_resolution = u16::from(channels[0].saturating_sub(1)) * 256;
let y_resolution = u16::from(channels[1].saturating_sub(1)) * 256;
let (system_config_0, system_config_1, i2c_timeout_ms, active_interval_ms) = match &mut self.window_detection {
WindowDetection::Rdy(_) => (0b01100100, 0b00001111, 30, 9),
WindowDetection::Poll { interval_ms, .. } => (0b11100100, 0, 100, *interval_ms),
};
let i2c_timeout = [0x05, 0x8A, i2c_timeout_ms];
#[rustfmt::skip]
let config = [
0x05, 0x8E, system_config_0,
system_config_1, ];
#[rustfmt::skip]
let report_rates = [
0x05, 0x7A, (active_interval_ms >> 8) as u8, active_interval_ms as u8, (active_interval_ms >> 8) as u8, active_interval_ms as u8, 0, 25, ];
const ACK_RESET: [u8; 3] = [
0x04,
0x31, 0b1000_0000, ];
#[rustfmt::skip]
const XY_CONFIG: [u8; 3] = [
0x06, 0x69, 0b0001, ];
#[rustfmt::skip]
let gestures = [
0x06, 0xB7, 0, 0, ];
#[rustfmt::skip]
let xy_resolution = [
0x06, 0x6E,
(x_resolution >> 8) as u8, x_resolution as u8,
(y_resolution >> 8) as u8, y_resolution as u8,
];
for (tag, write) in [
("i2c_timeout", &i2c_timeout[..]),
("config", &config[..]),
("report_rates", &report_rates[..]),
("ack_reset", &ACK_RESET[..]),
("xy_config", &XY_CONFIG[..]),
("gestures", &gestures[..]),
("xy_resolution", &xy_resolution[..]),
] {
i2c_tx(&mut self.i2c, tag, &mut [Operation::Write(write)]).await?;
}
i2c_tx(&mut self.i2c, "end_session", &mut [Operation::Write(&END_SESSION[..])]).await?;
if let WindowDetection::Poll { ref mut last_end, .. } = self.window_detection {
*last_end = Instant::now();
}
self.initialized = true;
info!(
"iqs5xx {}: initialized {} (rx={}, tx={} => x_res={}, y_res={})",
self.pointing_device_id, ic, channels[0], channels[1], x_resolution, y_resolution,
);
Ok(())
}
async fn read_motion(&mut self) -> Result<PointingEvent, Error<I::Error>> {
let mut data = [0u8; 10];
let mut operations = [
Operation::Write(&[0x00, 0x0C]),
Operation::Read(&mut data),
Operation::Write(&END_SESSION[..]),
];
match self.window_detection {
WindowDetection::Rdy(ref mut rdy) => {
rdy.wait_for_high().await.expect("pin wait failure");
i2c_tx(&mut self.i2c, "read_motion", &mut operations).await?;
}
WindowDetection::Poll {
ref mut last_end,
interval_ms,
} => {
Timer::at(last_end.saturating_add(Duration::from_millis(u64::from(interval_ms)))).await;
i2c_force_tx(&mut self.i2c, "read_motion", &mut operations).await?;
*last_end = Instant::now();
}
}
let prev_cycle_time_ms = data[0];
let gesture_events_0 = data[1];
let gesture_events_1 = data[2];
let system_info_0 = data[3]; let system_info_1 = data[4]; let number_of_fingers = data[5];
let dx = i16::from_be_bytes(unwrap!(data[6..8].try_into()));
let dy = i16::from_be_bytes(unwrap!(data[8..10].try_into()));
let charging_mode = match system_info_0 & 0b111 {
0b000 => "active",
0b001 => "idle-touch",
0b010 => "idle",
0b011 => "lp1",
0b100 => "lp2",
_ => "invalid",
};
if (system_info_0 & 0b0001_0000) != 0 {
debug!("iqs5xx {} re-ati", self.pointing_device_id);
}
if (system_info_0 & 0b0000_1000) != 0 {
error!("iqs5xx {} ati error", self.pointing_device_id);
}
if (system_info_0 & 0b1000_0000) != 0 {
self.initialized = false;
return Err(Error::Reset);
}
debug!(
"iqs5xx {} motion data: cycle_ms={} gestures=[{},{}] mode={} system=[{},{}] n_fingers={} dx={} dy={}",
self.pointing_device_id,
prev_cycle_time_ms,
gesture_events_0,
gesture_events_1,
charging_mode,
system_info_0,
system_info_1,
number_of_fingers,
dx,
dy,
);
Ok(PointingEvent {
device_id: self.pointing_device_id,
axes: [
AxisEvent {
typ: crate::event::AxisValType::Rel,
axis: crate::event::Axis::X,
value: dx,
},
AxisEvent {
typ: crate::event::AxisValType::Rel,
axis: crate::event::Axis::Y,
value: dy,
},
AxisEvent {
typ: crate::event::AxisValType::Rel,
axis: crate::event::Axis::Z,
value: 0,
},
],
})
}
async fn read_pointing_event(&mut self) -> PointingEvent {
loop {
if !self.initialized
&& let Err(e) = self.init().await
{
error!(
"iqs5xx {} initialization failed: {:?}; will retry in 1 second",
self.pointing_device_id, e,
);
Timer::after_secs(1).await;
continue;
}
match self.read_motion().await {
Ok(e) => {
if e.axes.iter().any(|axis| axis.value != 0) {
return e;
}
}
Err(e) => {
error!("iqs5xx {} failure: {:?}", self.pointing_device_id, e);
Timer::after_millis(5).await;
}
}
}
}
}