use std::error::Error;
use std::thread;
use std::time::Duration;
use std::fmt;
use core::I2CDevice;
pub const NUNCHUCK_SLAVE_ADDR: u16 = 0x52;
#[derive(Debug)]
pub enum NunchuckError<E> {
Error(E),
ParseError,
}
impl<E: Error> fmt::Display for NunchuckError<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
NunchuckError::Error(ref e) => fmt::Display::fmt(e, f),
NunchuckError::ParseError => fmt::Display::fmt(self.description(), f),
}
}
}
impl<E: Error> Error for NunchuckError<E> {
fn description(&self) -> &str {
match *self {
NunchuckError::Error(ref e) => e.description(),
NunchuckError::ParseError => "Unable to Parse Data",
}
}
fn cause(&self) -> Option<&Error> {
match *self {
NunchuckError::Error(ref e) => Some(e),
NunchuckError::ParseError => None,
}
}
}
#[derive(Debug)]
pub struct NunchuckReading {
joystick_x: u8,
joystick_y: u8,
accel_x: u16, accel_y: u16, accel_z: u16, c_button_pressed: bool,
z_button_pressed: bool,
}
impl NunchuckReading {
pub fn from_data(data: &[u8]) -> Option<NunchuckReading> {
if data.len() < 6 {
None
} else {
Some(NunchuckReading {
joystick_x: data[0],
joystick_y: data[1],
accel_x: (data[2] as u16) << 2 | ((data[5] as u16 >> 6) & 0b11),
accel_y: (data[3] as u16) << 2 | ((data[5] as u16 >> 4) & 0b11),
accel_z: (data[4] as u16) << 2 | ((data[5] as u16 >> 2) & 0b11),
c_button_pressed: (data[5] & 0b10) == 0,
z_button_pressed: (data[5] & 0b01) == 0,
})
}
}
}
pub struct Nunchuck<T: I2CDevice> {
i2cdev: T,
}
impl<T> Nunchuck<T> where T: I2CDevice
{
pub fn new(i2cdev: T) -> Result<Nunchuck<T>, T::Error> {
let mut nunchuck = Nunchuck { i2cdev: i2cdev };
try!(nunchuck.init());
Ok(nunchuck)
}
#[cfg(test)]
pub fn get_i2cdev(&mut self) -> &mut T {
&mut self.i2cdev
}
pub fn init(&mut self) -> Result<(), T::Error> {
try!(self.i2cdev.smbus_write_byte_data(0xF0, 0x55));
try!(self.i2cdev.smbus_write_byte_data(0xFB, 0x00));
Ok(())
}
pub fn read(&mut self) -> Result<NunchuckReading, NunchuckError<T::Error>> {
let mut buf: [u8; 6] = [0; 6];
try!(self.i2cdev.smbus_write_byte(0x00).map_err(NunchuckError::Error));
thread::sleep(Duration::from_millis(10));
try!(self.i2cdev.read(&mut buf).map_err(NunchuckError::Error));
NunchuckReading::from_data(&buf).ok_or(NunchuckError::ParseError)
}
}
#[cfg(test)]
mod test {
use super::*;
use core::I2CDevice;
use mock::MockI2CDevice;
#[test]
fn test_intialization() {
let mut i2cdev = MockI2CDevice::new();
i2cdev.smbus_write_byte_data(0xF0, 0xFF).unwrap();
i2cdev.smbus_write_byte_data(0xFB, 0xFF).unwrap();
let mut dev = Nunchuck::new(i2cdev).unwrap();
assert_eq!(dev.get_i2cdev().smbus_read_byte_data(0xF0).unwrap(), 0x55);
assert_eq!(dev.get_i2cdev().smbus_read_byte_data(0xFB).unwrap(), 0x00);
}
#[test]
fn test_read_zeroed_out() {
let mut dev = Nunchuck::new(MockI2CDevice::new()).unwrap();
let reading = dev.read().unwrap();
assert_eq!(reading.joystick_x, 0);
assert_eq!(reading.joystick_y, 0);
assert_eq!(reading.accel_x, 0);
assert_eq!(reading.accel_y, 0);
assert_eq!(reading.accel_z, 0);
assert_eq!(reading.c_button_pressed, true);
assert_eq!(reading.z_button_pressed, true);
}
#[test]
fn test_read_sample_data() {
let mut i2cdev = MockI2CDevice::new();
i2cdev.write(&[0, 127, 128, 191, 129, 144, 71]).unwrap();
let mut dev = Nunchuck::new(i2cdev).unwrap();
let reading = dev.read().unwrap();
assert_eq!(reading.joystick_x, 127);
assert_eq!(reading.joystick_y, 128);
assert_eq!(reading.accel_x, 765);
assert_eq!(reading.accel_y, 516);
assert_eq!(reading.accel_z, 577);
assert_eq!(reading.c_button_pressed, false);
assert_eq!(reading.z_button_pressed, false);
}
}