From 164cf3761a817a10c1e32b64ba4c3908b9f478fd Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sun, 21 Apr 2024 22:03:29 +1000 Subject: [PATCH] Remove unnecessary functions and simplify comments --- wii-ext/src/async_impl.rs | 4 +- wii-ext/src/async_impl/classic.rs | 61 +++++++------------------- wii-ext/src/async_impl/interface.rs | 42 +++++++----------- wii-ext/src/async_impl/nunchuk.rs | 31 ++----------- wii-ext/src/blocking_impl/classic.rs | 57 +++++------------------- wii-ext/src/blocking_impl/interface.rs | 5 +-- wii-ext/src/blocking_impl/nunchuk.rs | 41 +++++------------ wii-ext/src/lib.rs | 16 +++++++ wii-ext/tests/classic_pdp_clone.rs | 2 +- wii-ext/tests/classic_pro.rs | 2 +- wii-ext/tests/classic_regular.rs | 2 +- 11 files changed, 81 insertions(+), 182 deletions(-) diff --git a/wii-ext/src/async_impl.rs b/wii-ext/src/async_impl.rs index 7c62e0f..eaa2cd8 100644 --- a/wii-ext/src/async_impl.rs +++ b/wii-ext/src/async_impl.rs @@ -1,6 +1,6 @@ -/// Async classic controller code +/// Async classic controller driver pub mod classic; /// Async i2c interface code pub mod interface; - +/// Async nunchuk controller driver pub mod nunchuk; diff --git a/wii-ext/src/async_impl/classic.rs b/wii-ext/src/async_impl/classic.rs index b017465..2487c9c 100644 --- a/wii-ext/src/async_impl/classic.rs +++ b/wii-ext/src/async_impl/classic.rs @@ -1,18 +1,6 @@ -// See https://www.raphnet-tech.com/support/classic_controller_high_res/ for data on high-precision mode - -// Abridged version of the above: -// To enable High Resolution Mode, you simply write 0x03 to address 0xFE in the extension controller memory. -// Then you poll the controller by reading 8 bytes at address 0x00 instead of only 6. -// You can also restore the original format by writing the original value back to address 0xFE at any time. -// -// Classic mode: -// http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller -// -// See `decode_classic_report` and `decode_classic_hd_report` for data format - use crate::async_impl::interface::{AsyncImplError, InterfaceAsync}; use crate::core::classic::*; -use crate::core::{ControllerIdReport, ControllerType}; +use crate::core::ControllerType; use embedded_hal_async; pub struct ClassicAsync { @@ -26,11 +14,7 @@ where I2C: embedded_hal_async::i2c::I2c, Delay: embedded_hal_async::delay::DelayNs, { - /// Create a new Wii Nunchuck - /// - /// This method will open the provide i2c device file and will - /// send the required init sequence in order to read data in - /// the future. + /// Create a new Wii Classic Controller pub fn new(i2cdev: I2C, delay: Delay) -> Self { let interface = InterfaceAsync::new(i2cdev, delay); Self { @@ -40,15 +24,15 @@ where } } - /// Recover data members + /// Destroy this driver, recovering the i2c bus and delay used to create it pub fn destroy(self) -> (I2C, Delay) { self.interface.destroy() } - // / Update the stored calibration for this controller - // / - // / Since each device will have different tolerances, we take a snapshot of some analog data - // / to use as the "baseline" center. + /// Update the stored calibration for this controller + /// + /// Since each device will have different tolerances, we take a snapshot of some analog data + /// to use as the "baseline" center. pub async fn update_calibration(&mut self) -> Result<(), AsyncImplError> { let data = self.read_report().await?; self.calibration = CalibrationData { @@ -62,23 +46,15 @@ where Ok(()) } - /// Send the init sequence to the Wii extension controller - /// - /// This could be a bit faster with DelayUs, but since you only init once we'll re-use delay_ms + /// Send the init sequence to the controller and calibrate it pub async fn init(&mut self) -> Result<(), AsyncImplError> { - // Extension controllers by default will use encrypted communication, as that is what the Wii does. - // We can disable this encryption by writing some magic values - // This is described at https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way - - // Reset to base register first - this should recover a controller in a weird state. - // Use longer delays here than normal reads - the system seems more unreliable performing these commands self.interface.init().await?; self.update_calibration().await?; Ok(()) } - /// poll the controller for the latest data - async fn read_classic_report(&mut self) -> Result { + /// Read uncalibrated data from the controller + async fn read_report(&mut self) -> Result { if self.hires { let buf = self.interface.read_hd_report().await?; ClassicReading::from_data(&buf).ok_or(AsyncImplError::InvalidInputData) @@ -88,27 +64,24 @@ where } } - /// Simple blocking read helper that will start a sample, wait 10ms, then read the value - async fn read_report(&mut self) -> Result { - self.read_classic_report().await - } - /// Do a read, and report axis values relative to calibration pub async fn read(&mut self) -> Result { Ok(ClassicReadingCalibrated::new( - self.read_classic_report().await?, + self.read_report().await?, &self.calibration, )) } + /// Switch the driver from standard to hi-resolution reporting + /// + /// This enables the controllers high-resolution report data mode, which returns each + /// analogue axis as a u8, rather than packing smaller integers in a structure. + /// If your controllers supports this mode, you should use it. It is much better. pub async fn enable_hires(&mut self) -> Result<(), AsyncImplError> { self.interface.enable_hires().await } - pub async fn read_id(&mut self) -> Result { - self.interface.read_id().await - } - + /// Determine the controller type based on the type ID of the extension controller pub async fn identify_controller(&mut self) -> Result, AsyncImplError> { self.interface.identify_controller().await } diff --git a/wii-ext/src/async_impl/interface.rs b/wii-ext/src/async_impl/interface.rs index e49cb86..5abfc48 100644 --- a/wii-ext/src/async_impl/interface.rs +++ b/wii-ext/src/async_impl/interface.rs @@ -1,15 +1,3 @@ -// See https://www.raphnet-tech.com/support/classic_controller_high_res/ for data on high-precision mode - -// Abridged version of the above: -// To enable High Resolution Mode, you simply write 0x03 to address 0xFE in the extension controller memory. -// Then you poll the controller by reading 8 bytes at address 0x00 instead of only 6. -// You can also restore the original format by writing the original value back to address 0xFE at any time. -// -// Classic mode: -// http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller -// -// See `decode_classic_report` and `decode_classic_hd_report` for data format - use crate::core::{ ControllerIdReport, ControllerType, ExtHdReport, ExtReport, EXT_I2C_ADDR, INTERMESSAGE_DELAY_MICROSEC_U32, @@ -33,31 +21,27 @@ pub struct InterfaceAsync { delay: Delay, } -// use crate::nunchuk; impl InterfaceAsync where I2C: embedded_hal_async::i2c::I2c, Delay: embedded_hal_async::delay::DelayNs, { - /// Create a new Wii Nunchuck - /// - /// This method will open the provide i2c device file and will - /// send the required init sequence in order to read data in - /// the future. + /// Create async interface for wii-extension controller pub fn new(i2cdev: I2C, delay: Delay) -> Self { Self { i2cdev, delay } } - /// Recover data members + /// Destroy i2c interface, allowing recovery of i2c and delay pub fn destroy(self) -> (I2C, Delay) { (self.i2cdev, self.delay) } + /// Access delay stored in interface pub(super) async fn delay_us(&mut self, micros: u32) { self.delay.delay_us(micros).await } - /// Read the button/axis data from the classic controller + /// Read report data from the wii-extension controller pub(super) async fn read_ext_report(&mut self) -> Result { self.start_sample().await?; self.delay_us(INTERMESSAGE_DELAY_MICROSEC_U32).await; @@ -69,7 +53,7 @@ where .and(Ok(buffer)) } - /// Read a high-resolution version of the button/axis data from the classic controller + /// Read a high-resolution version of the report data from the wii-extension controller pub(super) async fn read_hd_report(&mut self) -> Result { self.start_sample().await?; self.delay_us(INTERMESSAGE_DELAY_MICROSEC_U32).await; @@ -82,8 +66,6 @@ where } /// Send the init sequence to the Wii extension controller - /// - /// This could be a bit faster with DelayUs, but since you only init once we'll re-use delay_ms pub(super) async fn init(&mut self) -> Result<(), AsyncImplError> { // Extension controllers by default will use encrypted communication, as that is what the Wii does. // We can disable this encryption by writing some magic values @@ -101,7 +83,7 @@ where /// Switch the driver from standard to hi-resolution reporting /// - /// This enables the controllers high-resolution report data mode, which returns each + /// This enables the controller's high-resolution report data mode, which returns each /// analogue axis as a u8, rather than packing smaller integers in a structure. /// If your controllers supports this mode, you should use it. It is much better. pub(super) async fn enable_hires(&mut self) -> Result<(), AsyncImplError> { @@ -127,6 +109,13 @@ where .and(Ok(())) } + /// Set the cursor position for the next i2c read after a small delay + /// + /// This hardware has a range of 100 registers and automatically + /// increments the register read postion on each read operation, and also on + /// every write operation. + /// This should be called before a read operation to ensure you get the correct data + /// The delay helps ensure that required timings are met pub(super) async fn set_read_register_address_with_delay( &mut self, byte0: u8, @@ -145,6 +134,7 @@ where .and(Ok(())) } + /// Set a single register at target address after a small delay pub(super) async fn set_register_with_delay( &mut self, addr: u8, @@ -155,12 +145,14 @@ where res.await } + /// Read the controller type ID register from the extension controller pub(super) async fn read_id(&mut self) -> Result { self.set_read_register_address(0xfa).await?; let i2c_id = self.read_ext_report().await?; Ok(i2c_id) } + /// Determine the controller type based on the type ID of the extension controller pub(super) async fn identify_controller( &mut self, ) -> Result, AsyncImplError> { @@ -168,7 +160,7 @@ where Ok(crate::core::identify_controller(i2c_id)) } - /// tell the extension controller to prepare a sample by setting the read cursor to 0 + /// Instruct the extension controller to start preparing a sample by setting the read cursor to 0 pub(super) async fn start_sample(&mut self) -> Result<(), AsyncImplError> { self.set_read_register_address(0x00).await?; Ok(()) diff --git a/wii-ext/src/async_impl/nunchuk.rs b/wii-ext/src/async_impl/nunchuk.rs index a9839a2..eb6ad53 100644 --- a/wii-ext/src/async_impl/nunchuk.rs +++ b/wii-ext/src/async_impl/nunchuk.rs @@ -1,14 +1,6 @@ -// The nunchuk portion of this crate is derived from -// https://github.com/rust-embedded/rust-i2cdev/blob/master/examples/nunchuck.rs -// which is Copyright 2015, Paul Osborne -// -// All the bugs are Copyright 2024, 9names. - -// Nunchuk technically supports HD report, but the last two bytes will be zeroes -// There's no benefit, so we're leaving that unimplemented use crate::async_impl::interface::{AsyncImplError, InterfaceAsync}; use crate::core::nunchuk::*; -use crate::core::{ControllerIdReport, ControllerType}; +use crate::core::ControllerType; use embedded_hal_async; pub struct NunchukAsync { @@ -22,10 +14,6 @@ where Delay: embedded_hal_async::delay::DelayNs, { /// Create a new Wii Nunchuck - /// - /// This method will open the provide i2c device file and will - /// send the required init sequence in order to read data in - /// the future. pub fn new(i2cdev: I2C, delay: Delay) -> Self { let interface = InterfaceAsync::new(i2cdev, delay); Self { @@ -34,7 +22,7 @@ where } } - /// Recover data members + /// Destroy this driver, recovering the i2c bus and delay used to create it pub fn destroy(self) -> (I2C, Delay) { self.interface.destroy() } @@ -52,16 +40,8 @@ where Ok(()) } - /// Send the init sequence to the Wii extension controller - /// - /// This could be a bit faster with DelayUs, but since you only init once we'll re-use delay_ms + /// Send the init sequence to the controller and calibrate it pub async fn init(&mut self) -> Result<(), AsyncImplError> { - // Extension controllers by default will use encrypted communication, as that is what the Wii does. - // We can disable this encryption by writing some magic values - // This is described at https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way - - // Reset to base register first - this should recover a controller in a weird state. - // Use longer delays here than normal reads - the system seems more unreliable performing these commands self.interface.init().await?; self.update_calibration().await?; Ok(()) @@ -81,10 +61,7 @@ where )) } - pub async fn read_id(&mut self) -> Result { - self.interface.read_id().await - } - + /// Determine the controller type based on the type ID of the extension controller pub async fn identify_controller(&mut self) -> Result, AsyncImplError> { self.interface.identify_controller().await } diff --git a/wii-ext/src/blocking_impl/classic.rs b/wii-ext/src/blocking_impl/classic.rs index 13435f5..f9841cd 100644 --- a/wii-ext/src/blocking_impl/classic.rs +++ b/wii-ext/src/blocking_impl/classic.rs @@ -1,15 +1,3 @@ -// See https://www.raphnet-tech.com/support/classic_controller_high_res/ for data on high-precision mode - -// Abridged version of the above: -// To enable High Resolution Mode, you simply write 0x03 to address 0xFE in the extension controller memory. -// Then you poll the controller by reading 8 bytes at address 0x00 instead of only 6. -// You can also restore the original format by writing the original value back to address 0xFE at any time. -// -// Classic mode: -// http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller -// -// See `decode_classic_report` and `decode_classic_hd_report` for data format - use crate::blocking_impl::interface::{BlockingImplError, Interface}; use crate::core::classic::{CalibrationData, ClassicReading, ClassicReadingCalibrated}; use crate::core::ControllerType; @@ -37,11 +25,7 @@ where T: I2c, DELAY: embedded_hal::delay::DelayNs, { - /// Create a new Wii Nunchuck - /// - /// This method will open the provide i2c device file and will - /// send the required init sequence in order to read data in - /// the future. + /// Create a new Wii Classic Controller pub fn new(i2cdev: T, delay: DELAY) -> Result, BlockingImplError> { let interface = Interface::new(i2cdev, delay); let mut classic = Classic { @@ -53,7 +37,7 @@ where Ok(classic) } - /// Recover data members + /// Destroy this driver, recovering the i2c bus and delay used to create it pub fn destroy(self) -> (T, DELAY) { self.interface.destroy() } @@ -63,7 +47,7 @@ where /// Since each device will have different tolerances, we take a snapshot of some analog data /// to use as the "baseline" center. pub fn update_calibration(&mut self) -> Result<(), BlockingImplError> { - let data = self.read_report_blocking()?; + let data = self.read_uncalibrated()?; self.calibration = CalibrationData { joystick_left_x: data.joystick_left_x, @@ -76,19 +60,10 @@ where Ok(()) } - /// Send the init sequence to the Wii extension controller - /// - /// This could be a bit faster with DelayNs, but since you only init once we'll re-use delay_ms + /// Send the init sequence to the controller pub fn init(&mut self) -> Result<(), BlockingImplError> { - // Extension controllers by default will use encrypted communication, as that is what the Wii does. - // We can disable this encryption by writing some magic values - // This is described at https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way - - // Reset to base register first - this should recover a controller in a weird state. - // Use longer delays here than normal reads - the system seems more unreliable performing these commands self.interface.init()?; self.update_calibration()?; - // TODO: do we need more delay here? Ok(()) } @@ -120,12 +95,14 @@ where Ok(()) } + /// Determine the controller type based on the type ID of the extension controller pub fn identify_controller(&mut self) -> Result, BlockingImplError> { self.interface.identify_controller() } - /// poll the controller for the latest data - fn read_classic_report(&mut self) -> Result> { + /// Do a read, and return button and axis values without applying calibration + pub fn read_uncalibrated(&mut self) -> Result> { + self.interface.start_sample_and_wait()?; if self.hires { let buf = self.interface.read_hd_report()?; ClassicReading::from_data(&buf).ok_or(BlockingImplError::InvalidInputData) @@ -135,22 +112,10 @@ where } } - /// Simple read helper helper with no delay. Works for testing, not on real hardware - pub fn read_classic_no_wait(&mut self) -> Result> { - self.interface.start_sample()?; - self.read_classic_report() - } - - /// Simple blocking read helper that will start a sample, wait 10ms, then read the value - pub fn read_report_blocking(&mut self) -> Result> { - self.interface.start_sample_and_wait()?; - self.read_classic_report() - } - - /// Do a read, and report axis values relative to calibration - pub fn read_blocking(&mut self) -> Result> { + /// Do a read, and return button and axis values relative to calibration + pub fn read(&mut self) -> Result> { Ok(ClassicReadingCalibrated::new( - self.read_report_blocking()?, + self.read_uncalibrated()?, &self.calibration, )) } diff --git a/wii-ext/src/blocking_impl/interface.rs b/wii-ext/src/blocking_impl/interface.rs index cd1cee2..af18568 100644 --- a/wii-ext/src/blocking_impl/interface.rs +++ b/wii-ext/src/blocking_impl/interface.rs @@ -48,9 +48,6 @@ where self.delay.delay_us(INTERMESSAGE_DELAY_MICROSEC * 2); self.set_register(0xFB, 0x00)?; self.delay.delay_us(INTERMESSAGE_DELAY_MICROSEC * 2); - // TODO: move calibration to each impl - //self.update_calibration()?; - self.delay.delay_us(INTERMESSAGE_DELAY_MICROSEC * 2); Ok(()) } @@ -60,6 +57,7 @@ where Ok(i2c_id) } + /// Determine the controller type based on the type ID of the extension controller pub(super) fn identify_controller( &mut self, ) -> Result, BlockingImplError> { @@ -135,5 +133,4 @@ where .map_err(BlockingImplError::I2C) .and(Ok(buffer)) } - } diff --git a/wii-ext/src/blocking_impl/nunchuk.rs b/wii-ext/src/blocking_impl/nunchuk.rs index 9d610ec..5f30de9 100644 --- a/wii-ext/src/blocking_impl/nunchuk.rs +++ b/wii-ext/src/blocking_impl/nunchuk.rs @@ -1,12 +1,3 @@ -// The nunchuk portion of this crate is derived from -// https://github.com/rust-embedded/rust-i2cdev/blob/master/examples/nunchuck.rs -// which is Copyright 2015, Paul Osborne -// -// All the bugs are Copyright 2021, 9names. - -// TODO: nunchuk technically supports HD report, but the last two bytes will be zeroes -// work out if it's worth supporting that - use crate::blocking_impl::interface::{BlockingImplError, Interface}; use crate::core::nunchuk::{CalibrationData, NunchukReading, NunchukReadingCalibrated}; use crate::core::ControllerType; @@ -29,10 +20,6 @@ where DELAY: embedded_hal::delay::DelayNs, { /// Create a new Wii Nunchuk - /// - /// This method will open the provide i2c device file and will - /// send the required init sequence in order to read data in - /// the future. pub fn new(i2cdev: I2C, delay: DELAY) -> Result, BlockingImplError> { let interface = Interface::new(i2cdev, delay); let mut nunchuk = Nunchuk { @@ -43,7 +30,7 @@ where Ok(nunchuk) } - /// Recover data members + /// Destroy this driver, recovering the i2c bus and delay used to create it pub fn destroy(self) -> (I2C, DELAY) { self.interface.destroy() } @@ -53,7 +40,7 @@ where /// Since each device will have different tolerances, we take a snapshot of some analog data /// to use as the "baseline" center. pub fn update_calibration(&mut self) -> Result<(), BlockingImplError> { - let data = self.read_report_blocking()?; + let data = self.read_uncalibrated()?; self.calibration = CalibrationData { joystick_x: data.joystick_x, @@ -62,39 +49,31 @@ where Ok(()) } - /// Send the init sequence to the Wii extension controller + /// Send the init sequence to the Nunchuk pub fn init(&mut self) -> Result<(), BlockingImplError> { - // These registers must be written to disable encryption.; the documentation is a bit - // lacking but it appears this is some kind of handshake to - // perform unencrypted data tranfers self.interface.init()?; self.update_calibration() } + /// Determine the controller type based on the type ID of the extension controller pub fn identify_controller( &mut self, ) -> Result, BlockingImplError> { self.interface.identify_controller() } - /// Read the button/axis data from the nunchuk - fn read_nunchuk(&mut self) -> Result> { + /// Do a read, and return button and axis values without applying calibration + pub fn read_uncalibrated(&mut self) -> Result> { + self.interface.start_sample()?; let buf = self.interface.read_report()?; NunchukReading::from_data(&buf).ok_or(BlockingImplError::InvalidInputData) } - /// Simple blocking read helper that will start a sample, wait `INTERMESSAGE_DELAY_MICROSEC`, then read the value - pub fn read_report_blocking(&mut self) -> Result> { - self.interface.start_sample()?; - self.read_nunchuk() - } - - /// Do a read, and report axis values relative to calibration - pub fn read_blocking(&mut self) -> Result> { + /// Do a read, and return button and axis values relative to calibration + pub fn read(&mut self) -> Result> { Ok(NunchukReadingCalibrated::new( - self.read_report_blocking()?, + self.read_uncalibrated()?, &self.calibration, )) } - } diff --git a/wii-ext/src/lib.rs b/wii-ext/src/lib.rs index b3e469c..24b38b5 100644 --- a/wii-ext/src/lib.rs +++ b/wii-ext/src/lib.rs @@ -1,3 +1,19 @@ +#![doc = include_str!("../../README.md")] +// See https://www.raphnet-tech.com/support/classic_controller_high_res/ for data on high-precision mode + +// Abridged version of the above: +// To enable High Resolution Mode, you simply write 0x03 to address 0xFE in the extension controller memory. +// Then you poll the controller by reading 8 bytes at address 0x00 instead of only 6. +// You can also restore the original format by writing the original value back to address 0xFE at any time. +// +// Classic mode: +// http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller +// +// See `decode_classic_report` and `decode_classic_hd_report` for data format +// +// The nunchuk portion of this crate is derived from +// https://github.com/rust-embedded/rust-i2cdev/blob/master/examples/nunchuck.rs +// which is Copyright 2015, Paul Osborne #![cfg_attr(not(test), no_std)] /// Async I2C implementations diff --git a/wii-ext/tests/classic_pdp_clone.rs b/wii-ext/tests/classic_pdp_clone.rs index 207897a..6d520df 100644 --- a/wii-ext/tests/classic_pdp_clone.rs +++ b/wii-ext/tests/classic_pdp_clone.rs @@ -54,7 +54,7 @@ fn classic_idle() { let mut i2c = i2c::Mock::new(&expectations); let delay = NoopDelay::new(); let mut classic = Classic::new(i2c.clone(), delay).unwrap(); - let report = classic.read_report_blocking().unwrap(); + let report = classic.read_report().unwrap(); assert_digital_eq(report, ClassicReading::default()); i2c.done(); } diff --git a/wii-ext/tests/classic_pro.rs b/wii-ext/tests/classic_pro.rs index fceb6b4..d2791cf 100644 --- a/wii-ext/tests/classic_pro.rs +++ b/wii-ext/tests/classic_pro.rs @@ -54,7 +54,7 @@ fn classic_idle() { let mut i2c = i2c::Mock::new(&expectations); let delay = NoopDelay::new(); let mut classic = Classic::new(i2c.clone(), delay).unwrap(); - let report = classic.read_report_blocking().unwrap(); + let report = classic.read_report().unwrap(); assert_digital_eq(report, ClassicReading::default()); i2c.done(); } diff --git a/wii-ext/tests/classic_regular.rs b/wii-ext/tests/classic_regular.rs index e66c401..ff010e0 100644 --- a/wii-ext/tests/classic_regular.rs +++ b/wii-ext/tests/classic_regular.rs @@ -52,7 +52,7 @@ fn classic_idle() { let mut i2c = i2c::Mock::new(&expectations); let delay = NoopDelay::new(); let mut classic = Classic::new(i2c.clone(), delay).unwrap(); - let report = classic.read_report_blocking().unwrap(); + let report = classic.read_report().unwrap(); assert_digital_eq(report, ClassicReading::default()); i2c.done(); }