use crate::{addresses, Error, I2cDevice, Result};
use embedded_hal::i2c::I2c;
pub struct Knob<I2C> {
device: I2cDevice<I2C>,
value: i16,
pressed: bool,
range: Option<(i16, i16)>,
bug_on_set: bool,
last_position: i16,
last_debounce_time: u32,
}
impl<I2C, E> Knob<I2C>
where
I2C: I2c<Error = E>,
{
pub fn new(i2c: I2C) -> Result<Self, E> {
Self::new_with_address(i2c, addresses::KNOB[0])
}
pub fn discover(i2c: &mut I2C) -> Result<u8, E> {
let addresses = addresses::KNOB;
for &addr in &addresses {
if i2c.write(addr, &[]).is_ok() {
return Ok(addr);
}
}
i2c.write(addresses[0], &[])
.map(|_| addresses[0])
.map_err(Error::I2c)
}
pub fn new_with_address(i2c: I2C, address: u8) -> Result<Self, E> {
let mut knob = Self {
device: I2cDevice::new(i2c, address),
value: 0,
pressed: false,
range: None,
bug_on_set: false,
last_position: 0,
last_debounce_time: 0,
};
let (initial_val, pressed) = knob.read_data()?;
knob.set_value_internal(100)?;
let (test_val, _) = knob.read_data()?;
if test_val != 100 {
knob.bug_on_set = true;
knob.set_value_internal(-initial_val)?;
} else {
knob.set_value_internal(initial_val)?;
}
knob.value = initial_val;
knob.pressed = pressed;
knob.last_position = initial_val;
knob.update()?;
Ok(knob)
}
pub fn address(&self) -> u8 {
self.device.address
}
fn read_data(&mut self) -> Result<(i16, bool), E> {
let mut buf = [0u8; 4]; self.device.read(&mut buf)?;
let raw_value = i16::from_le_bytes([buf[1], buf[2]]);
let pressed = buf[3] != 0;
Ok((raw_value, pressed))
}
pub fn update(&mut self) -> Result<bool, E> {
let previous_value = self.value;
let previous_pressed = self.pressed;
let (mut new_value, new_pressed) = self.read_data()?;
if let Some((min, max)) = self.range {
if new_value < min {
new_value = min;
self.set_value_internal(min)?;
} else if new_value > max {
new_value = max;
self.set_value_internal(max)?;
}
}
self.value = new_value;
self.pressed = new_pressed;
Ok(self.value != previous_value || self.pressed != previous_pressed)
}
pub fn value(&self) -> i16 {
self.value
}
pub fn set_value(&mut self, value: i16) -> Result<(), E> {
if let Some((min, max)) = self.range {
if value < min || value > max {
return Err(Error::OutOfRange);
}
}
self.set_value_internal(value)?;
self.value = value;
Ok(())
}
fn set_value_internal(&mut self, mut value: i16) -> Result<(), E> {
if self.bug_on_set {
value = -value;
}
let bytes = value.to_le_bytes();
let data = [bytes[0], bytes[1], 0, 0];
self.device.write(&data)?;
Ok(())
}
pub fn reset(&mut self) -> Result<(), E> {
self.set_value(0)
}
pub fn pressed(&self) -> bool {
self.pressed
}
pub fn set_range(&mut self, min: i16, max: i16) {
self.range = Some((min, max));
if self.value < min {
self.value = min;
} else if self.value > max {
self.value = max;
}
}
pub fn clear_range(&mut self) {
self.range = None;
}
pub fn range(&self) -> Option<(i16, i16)> {
self.range
}
pub fn rotation_delta(&self, previous_value: i16) -> i16 {
let diff = self.value.wrapping_sub(previous_value);
if !(-16384..=16384).contains(&diff) {
diff.wrapping_add(i16::MIN)
} else {
diff
}
}
pub fn direction(&mut self, now_ms: u32) -> Result<i8, E> {
if now_ms.wrapping_sub(self.last_debounce_time) < 30 {
return Ok(0);
}
self.update()?;
let current = self.value;
let mut dir = 0;
if current > self.last_position {
dir = 1;
} else if current < self.last_position {
dir = -1;
}
if dir != 0 {
self.last_debounce_time = now_ms;
self.last_position = current;
}
Ok(dir)
}
pub fn release(self) -> I2C {
self.device.release()
}
}