From 35c0f755f2001dbb9fe29d3f364749ae6192436d Mon Sep 17 00:00:00 2001 From: Naxdy Date: Mon, 1 Apr 2024 21:06:41 +0200 Subject: [PATCH] feat(input): implement input consistency mode --- src/config.rs | 8 ++++++-- src/gcc_hid.rs | 44 +++++++++++++++++++++++++++++++++++++++----- src/input.rs | 36 ++++++++++++++++++++++++++---------- src/main.rs | 11 ++++++++++- 4 files changed, 81 insertions(+), 18 deletions(-) diff --git a/src/config.rs b/src/config.rs index 4763376..d74c27e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -225,8 +225,12 @@ impl Default for StickConfig { pub struct ControllerConfig { #[packed_field(size_bits = "8")] pub config_revision: u8, + /// Toggle for input consistency mode. If true, the controller + /// will trick the Switch into updating the state every 8.33ms + /// instead of every 8ms. The tradeoff is a slight increase in + /// input lag. #[packed_field(size_bits = "8")] - pub config_version: u8, + pub input_consistency_mode: bool, #[packed_field(size_bytes = "328")] pub astick_config: StickConfig, #[packed_field(size_bytes = "328")] @@ -237,7 +241,7 @@ impl Default for ControllerConfig { fn default() -> Self { Self { config_revision: CONTROLLER_CONFIG_REVISION, - config_version: 0, + input_consistency_mode: true, astick_config: StickConfig::default(), cstick_config: StickConfig::default(), } diff --git a/src/gcc_hid.rs b/src/gcc_hid.rs index c41a114..d1758ea 100644 --- a/src/gcc_hid.rs +++ b/src/gcc_hid.rs @@ -8,7 +8,7 @@ use defmt::{debug, info, trace, warn, Format}; use embassy_futures::join::join; use embassy_rp::{peripherals::USB, usb::Driver}; -use embassy_time::Instant; +use embassy_time::{Duration, Instant, Ticker}; use embassy_usb::{ class::hid::{HidReaderWriter, ReportId, RequestHandler, State}, control::OutResponse, @@ -111,7 +111,7 @@ pub struct Buttons2 { pub blank1: u8, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct, Format)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PackedStruct, Format)] #[packed_struct(bit_numbering = "msb0", size_bytes = "8")] pub struct GcReport { #[packed_field(bits = "0..=7")] @@ -132,6 +132,21 @@ pub struct GcReport { pub trigger_r: u8, } +impl Default for GcReport { + fn default() -> Self { + Self { + buttons_1: Buttons1::default(), + buttons_2: Buttons2::default(), + stick_x: 127, + stick_y: 127, + cstick_x: 127, + cstick_y: 127, + trigger_l: 0, + trigger_r: 0, + } + } +} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(C, align(8))] pub struct RawConsoleReport { @@ -234,7 +249,11 @@ impl Handler for MyDeviceHandler { } #[embassy_executor::task] -pub async fn usb_transfer_task(driver: Driver<'static, USB>, raw_serial: [u8; 8]) { +pub async fn usb_transfer_task( + driver: Driver<'static, USB>, + raw_serial: [u8; 8], + input_consistency_mode: bool, +) { let mut serial_buffer = [0u8; 64]; let serial = format_no_std::show( @@ -294,7 +313,7 @@ pub async fn usb_transfer_task(driver: Driver<'static, USB>, raw_serial: [u8; 8] let hid_config = embassy_usb::class::hid::Config { report_descriptor: GCC_REPORT_DESCRIPTOR, request_handler: Some(&request_handler), - poll_ms: 8, + poll_ms: if input_consistency_mode { 4 } else { 8 }, max_packet_size_in: 37, max_packet_size_out: 5, }; @@ -318,7 +337,22 @@ pub async fn usb_transfer_task(driver: Driver<'static, USB>, raw_serial: [u8; 8] let in_fut = async { let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap(); + let mut ticker = Ticker::every(Duration::from_micros(8333)); + loop { + if input_consistency_mode { + // This is what we like to call a "hack". + // It forces reports to be sent every 8.33ms instead of every 8ms. + // 8.33ms is a multiple of the game's frame interval (16.66ms), so if we + // send a report every 8.33ms, it should (in theory) ensure (close to) + // 100% input accuracy. + // + // From the console's perspective, we are basically a laggy adapter, taking + // a minimum of 333 extra us to send a report every time it's polled, but it + // works to our advantage. + ticker.next().await; + } + let state = gcc_subscriber.next_message_pure().await; let report = get_gcinput_hid_report(&state); match writer.write(&report).await { @@ -326,7 +360,7 @@ pub async fn usb_transfer_task(driver: Driver<'static, USB>, raw_serial: [u8; 8] trace!("Report Written: {:08b}", report); let currtime = Instant::now(); let polltime = currtime.duration_since(lasttime); - trace!("Report written in {}us", polltime.as_micros()); + debug!("Report written in {}us", polltime.as_micros()); lasttime = currtime; } Err(e) => warn!("Failed to send report: {:?}", e), diff --git a/src/input.rs b/src/input.rs index 7c70dc1..482b02e 100644 --- a/src/input.rs +++ b/src/input.rs @@ -21,8 +21,8 @@ use libm::{fmaxf, fminf}; use crate::{ config::{ - ControllerConfig, OverrideStickState, SIGNAL_IS_CALIBRATING, SIGNAL_OVERRIDE_GCC_STATE, - SIGNAL_OVERRIDE_STICK_STATE, + ControllerConfig, OverrideGcReportInstruction, OverrideStickState, SIGNAL_IS_CALIBRATING, + SIGNAL_OVERRIDE_GCC_STATE, SIGNAL_OVERRIDE_STICK_STATE, }, filter::{run_waveshaping, FilterGains, KalmanState, WaveshapingValues, FILTER_GAINS}, gcc_hid::GcReport, @@ -379,6 +379,24 @@ fn update_button_states< gcc_state.buttons_1.dpad_down = btn_ddown.is_low(); } +#[embassy_executor::task] +pub async fn input_integrity_benchmark() { + loop { + SIGNAL_OVERRIDE_GCC_STATE.signal(OverrideGcReportInstruction { + report: { + let mut report = GcReport::default(); + report.buttons_1.dpad_up = true; + report.cstick_x = 0; + report.cstick_y = 0; + report + }, + duration_ms: 100, + }); + + Timer::after_millis(200).await; + } +} + /// Task responsible for updating the button states. /// Publishes the result to CHANNEL_GCC_STATE. #[embassy_executor::task] @@ -405,12 +423,6 @@ pub async fn update_button_state_task( let mut gcc_state = GcReport::default(); - // Set the stick states to the center - gcc_state.stick_x = 127; - gcc_state.stick_y = 127; - gcc_state.cstick_x = 127; - gcc_state.cstick_y = 127; - let gcc_publisher = CHANNEL_GCC_STATE.publisher().unwrap(); let mut override_stick_state: Option = None; @@ -447,8 +459,12 @@ pub async fn update_button_state_task( // check for a gcc state override (usually coming from the config task) if let Some(override_gcc_state) = SIGNAL_OVERRIDE_GCC_STATE.try_take() { - gcc_publisher.publish_immediate(override_gcc_state.report); - Timer::after_millis(override_gcc_state.duration_ms).await; + trace!("Overridden gcc state: {:?}", override_gcc_state.report); + let end_time = Instant::now() + Duration::from_millis(override_gcc_state.duration_ms); + while Instant::now() < end_time { + gcc_publisher.publish_immediate(override_gcc_state.report); + yield_now().await; + } }; if let Some(override_state) = &override_stick_state { diff --git a/src/main.rs b/src/main.rs index b2fac74..939a359 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,8 @@ use gpio::{Level, Output}; use input::{update_button_state_task, update_stick_states_task}; use static_cell::StaticCell; +use crate::input::input_integrity_benchmark; + use {defmt_rtt as _, panic_probe as _}; static mut CORE1_STACK: Stack<4096> = Stack::new(); @@ -86,7 +88,14 @@ fn main() -> ! { let executor1 = EXECUTOR1.init(Executor::new()); debug!("Mana"); executor1.run(|spawner| { - spawner.spawn(usb_transfer_task(driver, uid)).unwrap(); + spawner + .spawn(usb_transfer_task( + driver, + uid, + controller_config.input_consistency_mode, + )) + .unwrap(); + // spawner.spawn(input_integrity_benchmark()).unwrap(); spawner .spawn(update_button_state_task( Input::new(AnyPin::from(p.PIN_20), gpio::Pull::Up),