use crate::{addresses, Error, I2cDevice, Result};
use embedded_hal::i2c::I2c;
pub struct Joystick<I2C> {
device: I2cDevice<I2C>,
x: i8,
y: i8,
button_pressed: bool,
deadzone: u8,
}
impl<I2C, E> Joystick<I2C>
where
I2C: I2c<Error = E>,
{
pub const DEFAULT_DEADZONE: u8 = 26;
pub fn new(i2c: I2C) -> Result<Self, E> {
Self::new_with_address(i2c, addresses::JOYSTICK)
}
pub fn discover(i2c: &mut I2C) -> Result<u8, E> {
let addresses = [addresses::JOYSTICK];
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 joystick = Self {
device: I2cDevice::new(i2c, address),
x: 0,
y: 0,
button_pressed: false,
deadzone: Self::DEFAULT_DEADZONE,
};
joystick.update()?;
Ok(joystick)
}
pub fn address(&self) -> u8 {
self.device.address
}
pub fn update(&mut self) -> Result<bool, E> {
let previous_x = self.x;
let previous_y = self.y;
let previous_button = self.button_pressed;
let mut buf = [0u8; 4]; self.device.read(&mut buf)?;
let mut raw_x = buf[1];
let mut raw_y = buf[2];
let dx = (raw_x as i16) - 128;
let dy = (raw_y as i16) - 128;
if dx.abs() < self.deadzone as i16 && dy.abs() < self.deadzone as i16 {
raw_x = 128;
raw_y = 128;
}
self.x = (128 - (raw_x as i16)) as i8;
self.y = (128 - (raw_y as i16)) as i8;
self.button_pressed = buf[3] != 0;
Ok(self.x != previous_x || self.y != previous_y || self.button_pressed != previous_button)
}
pub fn x(&self) -> i8 {
self.x
}
pub fn y(&self) -> i8 {
self.y
}
pub fn position(&self) -> (i8, i8) {
(self.x, self.y)
}
pub fn button_pressed(&self) -> bool {
self.button_pressed
}
pub fn deadzone(&self) -> u8 {
self.deadzone
}
pub fn set_deadzone(&mut self, deadzone: u8) {
self.deadzone = deadzone;
}
pub fn is_centered(&self) -> bool {
self.x == 0 && self.y == 0
}
pub fn magnitude(&self) -> f32 {
let x = self.x as f32;
let y = self.y as f32;
libm::sqrtf(x * x + y * y)
}
pub fn angle(&self) -> f32 {
if self.is_centered() {
0.0
} else {
libm::atan2f(self.y as f32, self.x as f32)
}
}
pub fn release(self) -> I2C {
self.device.release()
}
}