NaxGCC-FW/src/config.rs

832 lines
28 KiB
Rust

/**
* Storage for controller configuration, including helper functions & types, as well as sane defaults.
* Also includes necessary logic for configuring the controller & calibrating the sticks.
*/
use core::{cmp::min, f32::consts::PI};
use defmt::{debug, error, info, warn, Format};
use embassy_futures::yield_now;
use embassy_rp::{
flash::{Async, Flash, ERASE_SIZE},
peripherals::FLASH,
};
use packed_struct::{derive::PackedStruct, PackedStruct};
use crate::{
helpers::{PackedFloat, ToPackedFloatArray, ToRegularArray, XyValuePair},
input::{
read_ext_adc, Stick, StickAxis, StickState, FLOAT_ORIGIN, SPI_ACS_SHARED, SPI_CCS_SHARED,
SPI_SHARED,
},
stick::{
calc_stick_values, legalize_notches, AppliedCalibration, CleanedCalibrationPoints,
LinearizedCalibration, NotchCalibration, NotchStatus, CALIBRATION_ORDER,
NOTCH_ADJUSTMENT_ORDER, NO_OF_ADJ_NOTCHES, NO_OF_CALIBRATION_POINTS, NO_OF_NOTCHES,
},
ADDR_OFFSET, FLASH_SIZE,
};
use embassy_sync::{
blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex, ThreadModeRawMutex},
pubsub::{PubSubBehavior, Subscriber},
signal::Signal,
};
use embassy_time::{Duration, Ticker, Timer};
use crate::{gcc_hid::GcReport, input::CHANNEL_GCC_STATE};
/// Whether we are currently calibrating the sticks. Updates are dispatched when the status changes.
/// Initial status is assumed to be false.
pub static SIGNAL_IS_CALIBRATING: Signal<ThreadModeRawMutex, bool> = Signal::new();
/// Config change signalled to the stick task.
pub static SIGNAL_CONFIG_CHANGE: Signal<ThreadModeRawMutex, ControllerConfig> = Signal::new();
/// Signal used to override the stick state in order to display desired stick positions during calibration.
pub static SIGNAL_OVERRIDE_STICK_STATE: Signal<
CriticalSectionRawMutex,
Option<OverrideStickState>,
> = Signal::new();
/// Dispatched when we want to override the GCC state for a short amount of time.
pub static SIGNAL_OVERRIDE_GCC_STATE: Signal<CriticalSectionRawMutex, OverrideGcReportInstruction> =
Signal::new();
/// Struct used for overriding the GCC state for a given amount of
/// time, useful for providing feedback to the user e.g. if we just entered
/// a certain mode.
#[derive(Default, Debug, Clone, Format)]
pub struct OverrideGcReportInstruction {
pub report: GcReport,
pub duration_ms: u64,
}
const CONFIG_MODE_ENTRY_COMBO: [AwaitableButtons; 4] = [
AwaitableButtons::Start,
AwaitableButtons::A,
AwaitableButtons::X,
AwaitableButtons::Y,
];
const LSTICK_CALIBRATION_COMBO: [AwaitableButtons; 2] = [AwaitableButtons::A, AwaitableButtons::X];
const RSTICK_CALIBRATION_COMBO: [AwaitableButtons; 2] = [AwaitableButtons::A, AwaitableButtons::Y];
/// This doesn't need to be super fast, since it's only used
/// in config mode.
const BUTTON_POLL_INTERVAL_MILLIS: u64 = 20;
/// This needs to be incremented for ANY change to ControllerConfig
/// else we risk loading uninitialized memory.
pub const CONTROLLER_CONFIG_REVISION: u8 = 1;
pub const DEFAULT_NOTCH_STATUS: [NotchStatus; NO_OF_NOTCHES] = [
NotchStatus::Cardinal,
NotchStatus::TertActive,
NotchStatus::Secondary,
NotchStatus::TertActive,
NotchStatus::Cardinal,
NotchStatus::TertActive,
NotchStatus::Secondary,
NotchStatus::TertActive,
NotchStatus::Cardinal,
NotchStatus::TertActive,
NotchStatus::Secondary,
NotchStatus::TertActive,
NotchStatus::Cardinal,
NotchStatus::TertActive,
NotchStatus::Secondary,
NotchStatus::TertActive,
];
#[rustfmt::skip]
const DEFAULT_CAL_POINTS_X: [f32; NO_OF_CALIBRATION_POINTS] = [
0.3010610568,0.3603937084,// right
0.3010903951,0.3000194135,
0.3005567843,0.3471911134,// up right
0.3006904343,0.3009976295,
0.3000800899,0.300985051,// up
0.3001020858,0.300852804,
0.3008746305,0.2548450139,// up left
0.3001434092,0.3012600593,
0.3011594091,0.2400535218,// left
0.3014621077,0.3011248469,
0.3010860944,0.2552106305,// down left
0.3002197989,0.3001679513,
0.3004438517,0.300486505,// down
0.3002766984,0.3012828579,
0.3014959877,0.346512936,// down right
0.3013398149,0.3007809916
];
#[rustfmt::skip]
const DEFAULT_CAL_POINTS_Y: [f32; NO_OF_CALIBRATION_POINTS] = [
0.300092277, 0.3003803475,// right
0.3002205792,0.301004752,
0.3001241394,0.3464200104,// up right
0.3001331245,0.3011881186,
0.3010685972,0.3606900641,// up
0.3001520488,0.3010662947,
0.3008837105,0.3461478452,// up left
0.3011732026,0.3007367683,
0.3011345742,0.3000566197,// left
0.3006843288,0.3009673425,
0.3011228978,0.2547579852,// down left
0.3011177285,0.301264851,
0.3002376991,0.2403885431,// down
0.3006540818,0.3010588401,
0.3011093054,0.2555000655,// down right
0.3000802760,0.3008482317
];
pub const DEFAULT_ANGLES: [f32; NO_OF_NOTCHES] = [
0.,
PI / 8.0,
PI * 2. / 8.,
PI * 3. / 8.,
PI * 4. / 8.,
PI * 5. / 8.,
PI * 6. / 8.,
PI * 7. / 8.,
PI * 8. / 8.,
PI * 9. / 8.,
PI * 10. / 8.,
PI * 11. / 8.,
PI * 12. / 8.,
PI * 13. / 8.,
PI * 14. / 8.,
PI * 15. / 8.,
];
#[derive(Clone, Format)]
pub struct OverrideStickState {
pub x: u8,
pub y: u8,
pub which_stick: Stick,
}
#[derive(Clone, Copy, Debug, Format)]
enum AwaitableButtons {
A,
B,
X,
Y,
Up,
Down,
Left,
Right,
Start,
L,
R,
}
#[derive(Clone, Copy, Debug, Format)]
enum NotchAdjustmentType {
Clockwise,
CounterClockwise,
Reset,
None,
}
#[derive(Debug, Clone, Format, PackedStruct)]
#[packed_struct(endian = "msb")]
pub struct StickConfig {
#[packed_field(size_bits = "8")]
pub x_waveshaping: u8,
#[packed_field(size_bits = "8")]
pub y_waveshaping: u8,
#[packed_field(size_bits = "8")]
pub analog_scaler: u8,
#[packed_field(size_bits = "8")]
pub x_snapback: i8, // not used for CStick
#[packed_field(size_bits = "8")]
pub y_snapback: i8, // not used for CStick
#[packed_field(size_bits = "8")]
pub cardinal_snapping: i8, // not used for CStick
#[packed_field(size_bits = "8")]
pub x_smoothing: u8,
#[packed_field(size_bits = "8")]
pub y_smoothing: u8,
#[packed_field(element_size_bytes = "4")]
pub cal_points_x: [PackedFloat; 32],
#[packed_field(element_size_bytes = "4")]
pub cal_points_y: [PackedFloat; 32],
#[packed_field(element_size_bytes = "4")]
pub angles: [PackedFloat; 16],
}
impl Default for StickConfig {
fn default() -> Self {
Self {
x_waveshaping: 0,
y_waveshaping: 0,
x_snapback: 4,
y_snapback: 4,
x_smoothing: 0,
y_smoothing: 0,
cardinal_snapping: 6,
analog_scaler: 100,
cal_points_x: *DEFAULT_CAL_POINTS_X.to_packed_float_array(),
cal_points_y: *DEFAULT_CAL_POINTS_Y.to_packed_float_array(),
angles: *DEFAULT_ANGLES.to_packed_float_array(),
}
}
}
#[derive(Debug, Clone, Format, PackedStruct)]
#[packed_struct(endian = "msb")]
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 input_consistency_mode: bool,
#[packed_field(size_bytes = "328")]
pub astick_config: StickConfig,
#[packed_field(size_bytes = "328")]
pub cstick_config: StickConfig,
}
impl Default for ControllerConfig {
fn default() -> Self {
Self {
config_revision: CONTROLLER_CONFIG_REVISION,
input_consistency_mode: true,
astick_config: StickConfig::default(),
cstick_config: StickConfig::default(),
}
}
}
impl ControllerConfig {
pub fn from_flash_memory(
mut flash: &mut Flash<'static, FLASH, Async, FLASH_SIZE>,
) -> Result<Self, embassy_rp::flash::Error> {
let mut controller_config_packed: <ControllerConfig as packed_struct::PackedStruct>::ByteArray = [0u8; 658]; // ControllerConfig byte size
flash.blocking_read(ADDR_OFFSET, &mut controller_config_packed)?;
match ControllerConfig::unpack(&controller_config_packed).unwrap() {
a if a.config_revision == CONTROLLER_CONFIG_REVISION => {
info!("Controller config loaded from flash: {}", a);
Ok(a)
}
a => {
warn!("Outdated controller config detected ({:02X}), or controller config was never present, using default.", a.config_revision);
let cfg = ControllerConfig::default();
info!("Going to save default controller config.");
cfg.write_to_flash(&mut flash)?;
Ok(cfg)
}
}
}
pub fn write_to_flash(
&self,
flash: &mut Flash<'static, FLASH, Async, FLASH_SIZE>,
) -> Result<(), embassy_rp::flash::Error> {
info!("Writing controller config to flash.");
flash.blocking_erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32)?;
flash.blocking_write(ADDR_OFFSET, &self.pack().unwrap())?;
Ok(())
}
}
trait WaitForButtonPress {
/// Wait for a single button press.
async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons);
/// Wait for a single button release.
async fn wait_for_button_release(&mut self, button_to_wait_for: &AwaitableButtons);
/// Wait for multiple buttons to be pressed simultaneously.
async fn wait_for_simultaneous_button_presses<const N: usize>(
&mut self,
buttons_to_wait_for: &[AwaitableButtons; N],
);
/// Wait for a single button press of specified buttons, and return the button that was pressed.
async fn wait_and_filter_button_press<const N: usize>(
&mut self,
buttons_to_wait_for: &[AwaitableButtons; N],
) -> AwaitableButtons;
/// See if one of the buttons in buttons_to_look_out_for is pressed, and return the pressed button, otherwise None.
fn filter_button_press_if_present<const N: usize>(
&mut self,
buttons_to_look_out_for: &[AwaitableButtons; N],
) -> Option<AwaitableButtons>;
/// Wait for multiple possible button combinations to be pressed simultaneously, and return the index of the combination that was pressed.
async fn wait_and_filter_simultaneous_button_presses<const N: usize, const M: usize>(
&mut self,
buttons_to_wait_for: &[[AwaitableButtons; N]; M],
) -> usize;
}
impl<'a, T: RawMutex, const I: usize, const J: usize, const K: usize> WaitForButtonPress
for Subscriber<'a, T, GcReport, I, J, K>
{
async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons) {
loop {
let report = self.next_message_pure().await;
if is_awaitable_button_pressed(&report, button_to_wait_for) {
break;
}
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
}
}
async fn wait_for_button_release(&mut self, button_to_wait_for: &AwaitableButtons) {
loop {
let report = self.next_message_pure().await;
if !is_awaitable_button_pressed(&report, button_to_wait_for) {
break;
}
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
}
}
async fn wait_for_simultaneous_button_presses<const N: usize>(
&mut self,
buttons_to_wait_for: &[AwaitableButtons; N],
) {
loop {
let report = self.next_message_pure().await;
if buttons_to_wait_for
.iter()
.all(|button| is_awaitable_button_pressed(&report, button))
{
break;
}
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
}
}
async fn wait_and_filter_button_press<const N: usize>(
&mut self,
buttons_to_wait_for: &[AwaitableButtons; N],
) -> AwaitableButtons {
loop {
let report = self.next_message_pure().await;
for button in buttons_to_wait_for {
if is_awaitable_button_pressed(&report, button) {
return *button;
}
}
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
}
}
async fn wait_and_filter_simultaneous_button_presses<const N: usize, const M: usize>(
&mut self,
buttons_to_wait_for: &[[AwaitableButtons; N]; M],
) -> usize {
loop {
let report = self.next_message_pure().await;
for (i, buttons) in buttons_to_wait_for.iter().enumerate() {
if buttons
.iter()
.all(|button| is_awaitable_button_pressed(&report, button))
{
return i;
}
}
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
}
}
fn filter_button_press_if_present<const N: usize>(
&mut self,
buttons_to_look_out_for: &[AwaitableButtons; N],
) -> Option<AwaitableButtons> {
let report = self.try_next_message_pure();
if let Some(report) = report {
for button in buttons_to_look_out_for {
if is_awaitable_button_pressed(&report, button) {
return Some(*button);
}
}
}
return None;
}
}
fn is_awaitable_button_pressed(report: &GcReport, button_to_wait_for: &AwaitableButtons) -> bool {
match button_to_wait_for {
AwaitableButtons::A => report.buttons_1.button_a,
AwaitableButtons::B => report.buttons_1.button_b,
AwaitableButtons::X => report.buttons_1.button_x,
AwaitableButtons::Y => report.buttons_1.button_y,
AwaitableButtons::Up => report.buttons_1.dpad_up,
AwaitableButtons::Down => report.buttons_1.dpad_down,
AwaitableButtons::Left => report.buttons_1.dpad_left,
AwaitableButtons::Right => report.buttons_1.dpad_right,
AwaitableButtons::Start => report.buttons_2.button_start,
AwaitableButtons::L => report.buttons_2.button_l || report.trigger_l > 10,
AwaitableButtons::R => report.buttons_2.button_r || report.trigger_r > 10,
}
}
#[derive(Debug, Format)]
struct StickCalibrationProcess<'a> {
which_stick: Stick,
calibration_step: u8,
gcc_config: &'a mut ControllerConfig,
cal_points: [XyValuePair<f32>; NO_OF_CALIBRATION_POINTS],
applied_calibration: AppliedCalibration,
}
impl<'a> StickCalibrationProcess<'a> {
pub fn new(gcc_config: &'a mut ControllerConfig, which_stick: Stick) -> Self {
Self {
which_stick,
calibration_step: 0,
gcc_config,
cal_points: [XyValuePair::default(); NO_OF_CALIBRATION_POINTS],
applied_calibration: AppliedCalibration::default(),
}
}
fn adjust_notch(&mut self, notch_adjustment_type: NotchAdjustmentType) {
let stick_config = match self.which_stick {
Stick::ControlStick => &mut self.gcc_config.astick_config,
Stick::CStick => &mut self.gcc_config.cstick_config,
};
let notch_idx =
NOTCH_ADJUSTMENT_ORDER[self.calibration_step as usize - NO_OF_CALIBRATION_POINTS];
if self.applied_calibration.cleaned_calibration.notch_status[notch_idx]
== NotchStatus::TertInactive
{}
// assumes a tick rate of 1ms
match notch_adjustment_type {
NotchAdjustmentType::Clockwise => {
stick_config.angles[notch_idx] -= 0.0075;
}
NotchAdjustmentType::CounterClockwise => {
stick_config.angles[notch_idx] += 0.0075;
}
NotchAdjustmentType::Reset => {
stick_config.angles[notch_idx] =
PackedFloat(self.applied_calibration.measured_notch_angles[notch_idx]);
}
NotchAdjustmentType::None => {}
}
match notch_adjustment_type {
NotchAdjustmentType::Clockwise
| NotchAdjustmentType::CounterClockwise
| NotchAdjustmentType::Reset => {
let cleaned_calibration_points =
CleanedCalibrationPoints::from_temp_calibration_points(
&self.cal_points.map(|e| e.x),
&self.cal_points.map(|e| e.y),
&self.applied_calibration.measured_notch_angles,
);
let linearized_calibration =
LinearizedCalibration::from_calibration_points(&cleaned_calibration_points);
self.applied_calibration.stick_params.fit_coeffs = XyValuePair {
x: linearized_calibration.fit_coeffs.x.map(|e| e as f32),
y: linearized_calibration.fit_coeffs.y.map(|e| e as f32),
};
let notch_calibration = NotchCalibration::from_cleaned_and_linearized_calibration(
&cleaned_calibration_points,
&linearized_calibration,
);
self.applied_calibration.stick_params.affine_coeffs =
notch_calibration.affine_coeffs;
self.applied_calibration.stick_params.boundary_angles =
notch_calibration.boundary_angles;
stick_config.angles = *legalize_notches(
self.calibration_step as usize,
&self.applied_calibration.measured_notch_angles,
&stick_config.angles.to_regular_array(),
)
.to_packed_float_array();
SIGNAL_CONFIG_CHANGE.signal(self.gcc_config.clone());
}
NotchAdjustmentType::None => {}
}
}
async fn calibration_advance(&mut self) -> bool {
info!(
"Running calibration advance on stick {} at step {}",
self.which_stick, self.calibration_step
);
let stick_config = match self.which_stick {
Stick::ControlStick => &mut self.gcc_config.astick_config,
Stick::CStick => &mut self.gcc_config.cstick_config,
};
let mut spi_unlocked = SPI_SHARED.lock().await;
let mut spi_acs_unlocked = SPI_ACS_SHARED.lock().await;
let mut spi_ccs_unlocked = SPI_CCS_SHARED.lock().await;
let spi = spi_unlocked.as_mut().unwrap();
let spi_acs = spi_acs_unlocked.as_mut().unwrap();
let spi_ccs = spi_ccs_unlocked.as_mut().unwrap();
if self.calibration_step < NO_OF_CALIBRATION_POINTS as u8 {
let mut x: f32 = 0.;
let mut y: f32 = 0.;
for _ in 0..128 {
x += read_ext_adc(self.which_stick, StickAxis::XAxis, spi, spi_acs, spi_ccs) as f32
/ 4096.0;
y += read_ext_adc(self.which_stick, StickAxis::YAxis, spi, spi_acs, spi_ccs) as f32
/ 4096.0;
}
x /= 128.;
y /= 128.;
let idx = CALIBRATION_ORDER[self.calibration_step as usize];
self.cal_points[idx] = XyValuePair { x, y };
}
self.calibration_step += 1;
// TODO: phob does something related to undo here
if self.calibration_step == NO_OF_CALIBRATION_POINTS as u8 {
stick_config.angles = *legalize_notches(
self.calibration_step as usize,
&self.applied_calibration.measured_notch_angles,
&self.applied_calibration.notch_angles,
)
.to_packed_float_array();
self.applied_calibration = AppliedCalibration::from_points(
&self.cal_points.map(|e| e.x),
&self.cal_points.map(|e| e.y),
&stick_config,
self.which_stick,
);
}
if self.calibration_step >= NO_OF_CALIBRATION_POINTS as u8 {
let mut notch_idx = NOTCH_ADJUSTMENT_ORDER[min(
self.calibration_step - NO_OF_CALIBRATION_POINTS as u8,
NO_OF_ADJ_NOTCHES as u8 - 1,
) as usize];
while self.applied_calibration.cleaned_calibration.notch_status[notch_idx]
== NotchStatus::TertInactive
&& self.calibration_step < NO_OF_CALIBRATION_POINTS as u8 + NO_OF_ADJ_NOTCHES as u8
{
stick_config.angles = *legalize_notches(
self.calibration_step as usize,
&self.applied_calibration.measured_notch_angles,
&stick_config.angles.to_regular_array(),
)
.to_packed_float_array();
self.calibration_step += 1;
notch_idx = NOTCH_ADJUSTMENT_ORDER[min(
self.calibration_step - NO_OF_CALIBRATION_POINTS as u8,
NO_OF_ADJ_NOTCHES as u8 - 1,
) as usize];
}
}
if self.calibration_step >= NO_OF_CALIBRATION_POINTS as u8 + NO_OF_ADJ_NOTCHES as u8 {
stick_config.cal_points_x = self.cal_points.map(|p| p.x.into());
stick_config.cal_points_y = self.cal_points.map(|p| p.y.into());
SIGNAL_CONFIG_CHANGE.signal(self.gcc_config.clone());
info!("Finished calibrating stick {}", self.which_stick);
return true;
}
return false;
}
pub async fn calibrate_stick(&mut self) {
info!("Beginning stick calibration for {}", self.which_stick);
let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
SIGNAL_IS_CALIBRATING.signal(true);
while {
if self.calibration_step < NO_OF_CALIBRATION_POINTS as u8 {
// Calibration phase
let (x, y) = get_stick_display_coords(self.calibration_step as usize);
debug!(
"Raw display coords for step {}: {}, {}",
self.calibration_step, x, y
);
SIGNAL_OVERRIDE_STICK_STATE.signal(Some(OverrideStickState {
x: x as u8,
y: y as u8,
which_stick: match self.which_stick {
Stick::ControlStick => Stick::CStick,
Stick::CStick => Stick::ControlStick,
},
}));
gcc_subscriber
.wait_for_button_release(&AwaitableButtons::A)
.await;
// Prevent accidental double presses
Timer::after_millis(100).await;
gcc_subscriber
.wait_for_button_press(&AwaitableButtons::A)
.await;
} else {
// Notch adjustment phase
gcc_subscriber
.wait_for_button_release(&AwaitableButtons::A)
.await;
Timer::after_millis(100).await;
let mut ticker = Ticker::every(Duration::from_millis(20));
let notch_idx = NOTCH_ADJUSTMENT_ORDER
[self.calibration_step as usize - NO_OF_CALIBRATION_POINTS];
let (init_x, init_y) =
calc_stick_values(self.applied_calibration.measured_notch_angles[notch_idx]);
SIGNAL_OVERRIDE_STICK_STATE.signal(Some(OverrideStickState {
x: (init_x + FLOAT_ORIGIN) as u8,
y: (init_y + FLOAT_ORIGIN) as u8,
which_stick: match self.which_stick {
Stick::ControlStick => Stick::CStick,
Stick::CStick => Stick::ControlStick,
},
}));
'adjust: loop {
let btn_result = gcc_subscriber.filter_button_press_if_present(&[
AwaitableButtons::A,
AwaitableButtons::B,
AwaitableButtons::X,
AwaitableButtons::Y,
]);
match btn_result {
Some(btn) => match btn {
AwaitableButtons::A => {
debug!("Btn A release pressed");
break 'adjust;
}
AwaitableButtons::B => self.adjust_notch(NotchAdjustmentType::Reset),
AwaitableButtons::X => {
self.adjust_notch(NotchAdjustmentType::Clockwise)
}
AwaitableButtons::Y => {
self.adjust_notch(NotchAdjustmentType::CounterClockwise)
}
_ => self.adjust_notch(NotchAdjustmentType::None),
},
None => self.adjust_notch(NotchAdjustmentType::None),
};
ticker.next().await;
yield_now().await;
}
};
!self.calibration_advance().await
} {}
SIGNAL_IS_CALIBRATING.signal(false);
SIGNAL_OVERRIDE_STICK_STATE.signal(None);
}
}
fn get_stick_display_coords(current_step: usize) -> (f32, f32) {
let idx = CALIBRATION_ORDER[current_step];
if idx % 2 != 0 {
let notch_idx = idx / 2;
match calc_stick_values(DEFAULT_ANGLES[notch_idx]) {
(x, y) => (x + FLOAT_ORIGIN, y + FLOAT_ORIGIN),
}
} else {
(127.5, 127.5)
}
}
async fn configuration_main_loop<
'a,
M: RawMutex,
const C: usize,
const S: usize,
const P: usize,
>(
current_config: &ControllerConfig,
mut flash: &mut Flash<'static, FLASH, Async, FLASH_SIZE>,
gcc_subscriber: &mut Subscriber<'a, M, GcReport, C, S, P>,
) {
let mut final_config = current_config.clone();
let config_options = [LSTICK_CALIBRATION_COMBO, RSTICK_CALIBRATION_COMBO];
'main: loop {
match gcc_subscriber
.wait_and_filter_simultaneous_button_presses(&config_options)
.await
{
selection => match selection {
0 => {
StickCalibrationProcess::new(&mut final_config, Stick::ControlStick)
.calibrate_stick()
.await;
}
1 => {
StickCalibrationProcess::new(&mut final_config, Stick::CStick)
.calibrate_stick()
.await;
}
s => {
error!("Invalid selection in config loop: {}", s);
continue;
}
},
};
final_config.write_to_flash(&mut flash).unwrap();
break 'main;
}
info!("Exiting config main loop.");
}
#[embassy_executor::task]
pub async fn config_task(
current_config: ControllerConfig,
mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>,
) {
let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
info!("Config task is running.");
loop {
gcc_subscriber
.wait_for_simultaneous_button_presses(&CONFIG_MODE_ENTRY_COMBO)
.await;
info!("Entering config mode.");
SIGNAL_OVERRIDE_GCC_STATE.signal(OverrideGcReportInstruction {
report: match GcReport::default() {
mut a => {
a.trigger_r = 255;
a.trigger_l = 255;
a.buttons_2.button_l = true;
a.buttons_2.button_r = true;
a.buttons_1.button_x = true;
a.buttons_1.button_y = true;
a.buttons_1.button_a = true;
a.stick_x = 127;
a.stick_y = 127;
a.cstick_x = 127;
a.cstick_y = 127;
a
}
},
duration_ms: 1000,
});
// Wait for the user to release the buttons
Timer::after_millis(1500).await;
configuration_main_loop(&current_config, &mut flash, &mut gcc_subscriber).await;
info!("Exiting config mode.");
}
}