From 89254b903f605073f6ba224a39f00751cd385f65 Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Sun, 25 May 2025 13:03:50 +0200 Subject: [PATCH 1/6] Changed to a simpler layout to support Linux better --- layout.drawio | 340 +++++++++++++++++++++++------- rp2040/src/main.rs | 283 +++++-------------------- rp2040/src/usb_joystick_device.rs | 81 +++---- 3 files changed, 353 insertions(+), 351 deletions(-) diff --git a/layout.drawio b/layout.drawio index fc14a26..062b926 100644 --- a/layout.drawio +++ b/layout.drawio @@ -1,6 +1,6 @@ - + - + @@ -193,195 +193,389 @@ - + - - - + + + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - + - + - + - + - + - + - + - + - + - - + + - - + + - + - + - + - + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rp2040/src/main.rs b/rp2040/src/main.rs index 0c9d2ad..799b39c 100644 --- a/rp2040/src/main.rs +++ b/rp2040/src/main.rs @@ -82,12 +82,6 @@ pub struct Button { pub usb_changed: bool, pub usb_changed_to_pressed: bool, pub usb_button: usize, - pub usb_button_sec_enable: bool, - pub usb_button_sec: usize, - pub usb_button_sec_trigger_index: usize, - pub usb_button_sec_pressed: bool, - pub usb_button_toggle_enable: bool, - pub usb_release_timeout: u16, } #[derive(Copy, Clone)] @@ -285,119 +279,57 @@ fn main() -> ! { // | | - | - | - | | - |C OK | - | | // | | - | | - | | // --------------------------------------------------------------- + // USB HID joystick map : + // --------------------------------------------------------------- + // | B4 L| B3 U| B7 U| | B26 | | B8 U| B1 U| B2 L| + // --------------------------------------------------------------- + // | | B5 | B14 | B9 | | B10 | B15 | B6 | | + // | | + // | | B13 | | B17 | | + // | | B16 | | B12 | | + // | X1/Y1 X2/Y2 | + // | | B18 | | B22 | | + // | | B21 | B11 | B19 | | B25 | TH | B23 | | + // | | B20 | | B24 | | + // --------------------------------------------------------------- + // pub const CONFIG_BUTTON: usize = 2; pub const BOOT_BUTTON: usize = 0; pub const CAL_BUTTON: usize = 1; pub const CAL_DONE_BUTTON: usize = 20; pub const CAL_M10_GIMBLE_BUTTON: usize = 8; pub const CAL_M7_GIMBLE_BUTTON: usize = 9; - // HW Button index map: - // --------------------------------------------------------------- - // | 0 L| 1 U| 25 U | | 2 | | 26 U | 4 U| 3 L| - // --------------------------------------------------------------- - // | | 5 | 6 | 7 | | 12 | 11 | 10 | | - // | | - // | | 8 | | 13 | | - // | | 9 | | 14 | | - // | X1/Y1 X2/Y2 | - // | | 16 | | 21 | | - // | | 19 | 15 | 17 | | 24 | 20 | 22 | | - // | | 18 | | 23 | | - // --------------------------------------------------------------- - // - // USB HID joystick map : - // --------------------------------------------------------------- - // | Fn L| B19 U| B7 U| | B32 | | B8 U| B1 U| B2 L| - // --------------------------------------------------------------- - // | | B5 | B14 | B9 | | B10 | B15 | B6 | | - // | | - // | | B13 | | B17 | | - // | | B16 | | B18 | | - // | X1/Y1 X2/Y2 | - // | | H1U | | H2U | | - // | | H1L | B11 | H1R | | H2L | B12 | H2R | | - // | | H1D | | H2D | | - // --------------------------------------------------------------- - // - // USB HID joystick map (Fn): - // --------------------------------------------------------------- - // | Fn L| B21 U| B27 U| | B32 | | B8 U| B3 U| B4 L| - // --------------------------------------------------------------- - // | | TH | B22 | B26 | | B28 | B24 | B23 | | - // | | - // | | B29 | | B31 | | - // | | B16 | | B18 | | - // | X1/Y1 X2/Y2 | - // | | H3U | | H4U | | - // | | H3L | B20 | H3R | | H4L | B25 | H4R | | - // | | H3D | | H4D | | - // --------------------------------------------------------------- // Special button functions // Throttle hold: - pub const TH_BUTTON: usize = 5; - pub const TH_BUTTON_TRIGGER: usize = 0; + pub const TH_BUTTON: usize = 20; - // Set up usb button layout (button 0 to 15, 20, 25 and 26) - buttons[0].usb_button = 0; // Fn. Setting to 0 to disable USB button generation - buttons[1].usb_button = 19; - buttons[1].usb_button_sec_enable = true; - buttons[1].usb_button_sec = 21; - buttons[1].usb_button_sec_trigger_index = 0; - buttons[2].usb_button = 32; // Button used as global config. Do not define secondary USB button! + // Set up usb button layout + buttons[0].usb_button = 4; + buttons[1].usb_button = 3; + buttons[2].usb_button = 26; // Button used as global config. Do not define secondary USB button! buttons[3].usb_button = 2; - buttons[3].usb_button_sec_enable = true; - buttons[3].usb_button_sec = 4; - buttons[3].usb_button_sec_trigger_index = 0; buttons[4].usb_button = 1; - buttons[4].usb_button_sec_enable = true; - buttons[4].usb_button_sec = 3; - buttons[4].usb_button_sec_trigger_index = 0; buttons[5].usb_button = 5; - buttons[5].usb_button_sec_enable = true; // Set TH_BUTTON to inhibit button press on throttle hold - buttons[5].usb_button_sec = 0; // Setting to 0 to disable USB buttton generation - buttons[5].usb_button_sec_trigger_index = 0; // Set same as TH_BUTTON_TRIGGER buttons[6].usb_button = 14; - buttons[6].usb_button_sec_enable = true; - buttons[6].usb_button_sec = 22; - buttons[6].usb_button_sec_trigger_index = 0; buttons[7].usb_button = 9; - buttons[7].usb_button_sec_enable = true; - buttons[7].usb_button_sec = 26; - buttons[7].usb_button_sec_trigger_index = 0; buttons[8].usb_button = 13; - buttons[8].usb_button_sec_enable = true; - buttons[8].usb_button_sec = 29; - buttons[8].usb_button_sec_trigger_index = 0; buttons[9].usb_button = 16; buttons[10].usb_button = 6; - buttons[10].usb_button_sec_enable = true; - buttons[10].usb_button_sec = 23; - buttons[10].usb_button_sec_trigger_index = 0; buttons[11].usb_button = 15; - buttons[11].usb_button_sec_enable = true; - buttons[11].usb_button_sec = 24; - buttons[11].usb_button_sec_trigger_index = 0; buttons[12].usb_button = 10; - buttons[12].usb_button_sec_enable = true; - buttons[12].usb_button_sec = 28; - buttons[12].usb_button_sec_trigger_index = 0; buttons[13].usb_button = 17; - buttons[13].usb_button_sec_enable = true; - buttons[13].usb_button_sec = 31; - buttons[13].usb_button_sec_trigger_index = 0; - buttons[14].usb_button = 18; + buttons[14].usb_button = 12; buttons[15].usb_button = 11; - buttons[15].usb_button_sec_enable = true; - buttons[15].usb_button_sec = 20; - buttons[15].usb_button_sec_trigger_index = 0; - buttons[20].usb_button = 12; - buttons[20].usb_button_sec_enable = true; - buttons[20].usb_button_sec = 25; - buttons[20].usb_button_sec_trigger_index = 0; + buttons[16].usb_button = 18; + buttons[17].usb_button = 19; + buttons[18].usb_button = 20; + buttons[19].usb_button = 21; + buttons[20].usb_button = 0; + buttons[21].usb_button = 22; + buttons[22].usb_button = 23; + buttons[23].usb_button = 24; + buttons[24].usb_button = 25; buttons[25].usb_button = 7; - buttons[25].usb_button_sec_enable = true; - buttons[25].usb_button_sec = 27; - buttons[25].usb_button_sec_trigger_index = 0; buttons[26].usb_button = 8; // Table for gimbal expo curve lookup insded of doing floating point math for every analog read @@ -496,6 +428,27 @@ fn main() -> ! { buttons[25].pressed = left_extra_button.is_low().unwrap(); buttons[26].pressed = right_extra_button.is_low().unwrap(); + // Filter left hat swith buttons + for i in 16..=19 { + if (16..=19).filter(|&j| j != i).any(|j| buttons[j].pressed) { + buttons[i].pressed = false; + } + } + // Fix button state for center hat press on hat + if buttons[16..=19].iter().any(|b| b.pressed) { + buttons[15].pressed = false; + } + // Filter right hat swith buttons + for i in 21..=24 { + if (21..=24).filter(|&j| j != i).any(|j| buttons[j].pressed) { + buttons[i].pressed = false; + } + } + // Fix button state for center hat press on hat + if buttons[21..=24].iter().any(|b| b.pressed) { + buttons[20].pressed = false; + } + // Secondary way to enter bootloader (pressing all left hands buttons except the hat if buttons[BOOT_BUTTON].pressed && buttons[CONFIG_BUTTON].pressed { status_led.update(StatusMode::Bootloader); @@ -615,20 +568,7 @@ fn main() -> ! { item.previous_value = item.value; } - // Make sure usb will be updated during timeout countdown - for key in buttons.iter() { - if key.usb_release_timeout != 0 { - usb_activity = true; - } - } - // Generate led activity when a button is pressed - let mut th_trigger_pressed: bool = false; - for (index, key) in buttons.iter_mut().enumerate() { - if key.pressed && index == TH_BUTTON_TRIGGER { - th_trigger_pressed = true; - } - } for (index, key) in buttons.iter_mut().enumerate() { if key.pressed != key.previous_pressed { key.usb_changed = true; @@ -639,7 +579,6 @@ fn main() -> ! { if key.pressed != key.previous_pressed && key.pressed && index == TH_BUTTON - && th_trigger_pressed && unprocessed_value != AXIS_CENTER { axis[GIMBAL_AXIS_LEFT_Y].hold = axis[GIMBAL_AXIS_LEFT_Y].value; @@ -647,7 +586,6 @@ fn main() -> ! { } else if key.pressed != key.previous_pressed && key.pressed && index == TH_BUTTON - && th_trigger_pressed && unprocessed_value == AXIS_CENTER { axis[GIMBAL_AXIS_LEFT_Y].hold = AXIS_CENTER; @@ -730,127 +668,12 @@ fn get_joystick_report( let y: i16 = axis_12bit_to_i16(ADC_MAX - axis[GIMBAL_AXIS_LEFT_Y].value); let z: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_RIGHT_X].value); let rx: i16 = axis_12bit_to_i16(ADC_MAX - axis[GIMBAL_AXIS_RIGHT_Y].value); - let mut hat1: u8 = 0xf; - let mut hat2: u8 = 0xf; - let mut hat3: u8 = 0xf; - let mut hat4: u8 = 0xf; - - // Store hat bits - let mut hat_left: u8 = 0; - let mut hat_right: u8 = 0; - for (index, key) in matrix_keys.iter_mut().enumerate() { - if (15..=19).contains(&index) && key.pressed { - hat_left |= 1 << (index - 15); - } - if (20..=24).contains(&index) && key.pressed { - hat_right |= 1 << (index - 20); - } - } - - // Convert hat switch data to HID code - let (hat_l, hat_button_l) = format_hat_value(hat_left); - let (hat_r, hat_button_r) = format_hat_value(hat_right); - - // Handle sec_button (Fn) for left hat switch - let mut sec_button_pressed: bool = false; - for (sec_index, sec_key) in matrix_keys.iter().enumerate() { - if matrix_keys[15].usb_button_sec_enable - && matrix_keys[15].usb_button_sec_trigger_index == sec_index - && sec_key.pressed - { - sec_button_pressed = true; - break; - } - } - if matrix_keys[15].usb_changed { - matrix_keys[15].usb_button_sec_pressed = sec_button_pressed; - } - if matrix_keys[15].usb_button_sec != 0 && matrix_keys[15].usb_button_sec_pressed { - hat2 = hat_l; - } else { - hat4 = hat_l; - } - - // Handle sec_button (Fn) for right hat switch - let mut sec_button_pressed: bool = false; - for (sec_index, sec_key) in matrix_keys.iter().enumerate() { - if matrix_keys[20].usb_button_sec_enable - && matrix_keys[20].usb_button_sec_trigger_index == sec_index - && sec_key.pressed - { - sec_button_pressed = true; - break; - } - } - if matrix_keys[20].usb_changed { - matrix_keys[20].usb_button_sec_pressed = sec_button_pressed; - } - if matrix_keys[20].usb_button_sec != 0 && matrix_keys[20].usb_button_sec_pressed { - hat1 = hat_r; - } else { - hat3 = hat_r; - } - - // Fix button state for center hat press on hat - matrix_keys[15].pressed = hat_button_l != 0; - matrix_keys[20].pressed = hat_button_r != 0; - - // Update button array with Sec button trigger status - // Using indexing instead of iterating to be able to iterate inside loop - for index in 0..NUMBER_OF_BUTTONS + 2 { - let mut sec_button_pressed: bool = false; - for (sec_index, sec_key) in matrix_keys.iter().enumerate() { - if matrix_keys[index].usb_button_sec_enable - && matrix_keys[index].usb_button_sec_trigger_index == sec_index - && sec_key.pressed - { - sec_button_pressed = true; - break; - } - } - // Only update button when "root key" is pressed (ie. Do not change sec button as soon as Fn - // key is pressed - if matrix_keys[index].usb_changed { - matrix_keys[index].usb_button_sec_pressed = sec_button_pressed; - } - } // Update button state for joystick buttons let mut buttons: u32 = 0; for key in matrix_keys.iter_mut() { - // Toggle mode button - if key.usb_changed && key.usb_button_toggle_enable { - key.usb_release_timeout = RELEASE_RIMEOUT; - } - if key.pressed - && key.usb_button != 0 - && key.usb_button_toggle_enable - && key.usb_release_timeout > 1 - { + if key.pressed && key.usb_button != 0 { buttons |= 1 << (key.usb_button - 1); - } else if !key.pressed - && key.usb_button_sec != 0 - && key.usb_button_toggle_enable - && key.usb_release_timeout > 1 - { - buttons |= 1 << (key.usb_button_sec - 1); - // Sec button mode - } else if key.pressed && key.usb_button_sec != 0 && key.usb_button_sec_pressed { - buttons |= 1 << (key.usb_button_sec - 1); - // Standard mode - } else if key.pressed - && key.usb_button != 0 - && !key.usb_button_toggle_enable - && !key.usb_button_sec_pressed - { - buttons |= 1 << (key.usb_button - 1); - } - } - - // Auto release button when in toggle mode - for key in matrix_keys.iter_mut() { - if key.usb_release_timeout > 0 { - key.usb_release_timeout -= 1; } } @@ -864,10 +687,6 @@ fn get_joystick_report( y, z, rx, - hat1, - hat2, - hat3, - hat4, buttons, } } diff --git a/rp2040/src/usb_joystick_device.rs b/rp2040/src/usb_joystick_device.rs index da44dc4..216f5ce 100644 --- a/rp2040/src/usb_joystick_device.rs +++ b/rp2040/src/usb_joystick_device.rs @@ -63,42 +63,37 @@ impl Try for Result { // Updated to 6x 12bit axis, 32x buttons and 4x hat switches #[rustfmt::skip] pub const JOYSTICK_DESCRIPTOR: &[u8] = &[ -0x05, 0x01, // Usage Page (Generic Desktop) -0x09, 0x04, // Usage (Joystick) -0xA1, 0x01, // Collection (Application) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + // 4 signed 16-bit axes: X, Y, Z, Rx - 0x09, 0x30, // Usage (X) - 0x09, 0x31, // Usage (Y) - 0x09, 0x32, // Usage (Z) - 0x09, 0x33, // Usage (Rx) - 0x16, 0x00, 0x80,// Logical Minimum (-32768) - 0x26, 0xFF, 0x7F,// Logical Maximum (32767) - 0x75, 0x10, // Report Size (16) - 0x95, 0x04, // Report Count (4) - 0x81, 0x02, // Input (Data,Var,Abs) - // 4 Hat Switches (4-bit) - 0x09, 0x39, // Usage (Hat switch) - 0x09, 0x39, - 0x09, 0x39, - 0x09, 0x39, - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x07, // Logical Maximum (7) - 0x35, 0x00, // Physical Minimum (0) - 0x46, 0x3B, 0x01,// Physical Maximum (315) - 0x65, 0x14, // Unit (Degrees) - 0x75, 0x04, // Report Size (4) - 0x95, 0x04, // Report Count (4) - 0x81, 0x02, // Input (Data,Var,Abs) - // 32 Buttons (1-bit each) - 0x05, 0x09, // Usage Page (Button) - 0x19, 0x01, // Usage Minimum (1) - 0x29, 0x20, // Usage Maximum (32) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x75, 0x01, // Report Size (1) - 0x95, 0x20, // Report Count (32) - 0x81, 0x02, // Input (Data,Var,Abs) -0xC0 // End Collection + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x32, // Usage (Z) + 0x09, 0x33, // Usage (Rx) + 0x16, 0x00, 0x80, // Logical Minimum (-32768) + 0x26, 0xFF, 0x7F, // Logical Maximum (32767) + 0x75, 0x10, // Report Size (16) + 0x95, 0x04, // Report Count (4) + 0x81, 0x02, // Input (Data,Var,Abs) + + // 26 Buttons (1-bit each) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (1) + 0x29, 0x1A, // Usage Maximum (26) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x1A, // Report Count (26) + 0x81, 0x02, // Input (Data,Var,Abs) + + // Padding to align buttons to byte boundary (26 bits → +6 bits padding) + 0x75, 0x01, // Report Size (1) + 0x95, 0x06, // Report Count (6) + 0x81, 0x03, // Input (Const,Var,Abs) + + 0xC0 // End Collection ]; #[derive(Clone, Copy, Debug, Eq, PartialEq, Default)] @@ -107,10 +102,6 @@ pub struct JoystickReport { pub y: i16, // 16bit pub z: i16, // 16bit pub rx: i16, // 16bit - pub hat1: u8, // 4bit - pub hat2: u8, // 4bit - pub hat3: u8, // 4bit - pub hat4: u8, // 4bit pub buttons: u32, // 32bit } @@ -120,7 +111,7 @@ pub struct Joystick<'a, B: UsbBus> { impl Joystick<'_, B> { pub fn write_report(&mut self, report: &JoystickReport) -> Result<(), UsbHidError> { - let mut data: [u8; 14] = [0; 14]; + let mut data: [u8; 12] = [0; 12]; // Did not make the packed struct work, so doing it manually data[0] = report.x as u8; @@ -131,12 +122,10 @@ impl Joystick<'_, B> { data[5] = (report.z >> 8) as u8; data[6] = report.rx as u8; data[7] = (report.rx >> 8) as u8; - data[8] = (report.hat3) | (report.hat4 << 4); - data[9] = (report.hat1) | (report.hat2 << 4); - data[10] = report.buttons as u8; - data[11] = (report.buttons >> 8) as u8; - data[12] = (report.buttons >> 16) as u8; - data[13] = (report.buttons >> 24) as u8; + data[8] = report.buttons as u8; + data[9] = (report.buttons >> 8) as u8; + data[10] = (report.buttons >> 16) as u8; + data[11] = (report.buttons >> 24) as u8; self.interface .write_report(&data) From 964c2cae0c941eb9e6bcab8926e7fbc47aa50025 Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Wed, 28 May 2025 20:38:42 +0200 Subject: [PATCH 2/6] Added virtual axis --- layout.drawio | 22 ++++---- rp2040/src/main.rs | 86 +++++++++++++++---------------- rp2040/src/usb_joystick_device.rs | 22 +++++--- 3 files changed, 67 insertions(+), 63 deletions(-) diff --git a/layout.drawio b/layout.drawio index 062b926..4c6c3a0 100644 --- a/layout.drawio +++ b/layout.drawio @@ -213,17 +213,17 @@ - + - + - + @@ -235,10 +235,10 @@ - + - + @@ -291,7 +291,7 @@ - + @@ -354,7 +354,7 @@ - + @@ -370,18 +370,18 @@ - + - + - + - + diff --git a/rp2040/src/main.rs b/rp2040/src/main.rs index 799b39c..bdd661a 100644 --- a/rp2040/src/main.rs +++ b/rp2040/src/main.rs @@ -251,6 +251,9 @@ fn main() -> ! { let mut axis: [GimbalAxis; NBR_OF_GIMBAL_AXIS] = [Default::default(); NBR_OF_GIMBAL_AXIS]; let mut buttons: [Button; NUMBER_OF_BUTTONS + 2] = [Button::default(); NUMBER_OF_BUTTONS + 2]; + let mut virtual_ry: i16 = 0; + let mut virtual_rz: i16 = 0; + let mut virtual_step: i16 = 512; let mut gimbal_mode: u8; // HW Button index map: @@ -281,7 +284,7 @@ fn main() -> ! { // --------------------------------------------------------------- // USB HID joystick map : // --------------------------------------------------------------- - // | B4 L| B3 U| B7 U| | B26 | | B8 U| B1 U| B2 L| + // | Ry- L| Ry+ U| Rz- U| | B26 | | Rz+ U| B1 U| B2 L| // --------------------------------------------------------------- // | | B5 | B14 | B9 | | B10 | B15 | B6 | | // | | @@ -560,6 +563,31 @@ fn main() -> ! { axis[GIMBAL_AXIS_LEFT_Y].value = axis[GIMBAL_AXIS_LEFT_Y].hold; } + // Update Virtual RY + if buttons[1].pressed && !buttons[0].pressed { + virtual_ry = virtual_ry.saturating_add(100); + usb_activity = true; + } else if buttons[0].pressed && !buttons[1].pressed { + virtual_ry = virtual_ry.saturating_sub(100); + usb_activity = true; + } else if virtual_ry != 0 && !buttons[1].pressed && !buttons[0].pressed { + // Optional: decay to center + virtual_ry = 0; + usb_activity = true; + } + // Update Virtual RZ + if buttons[25].pressed && !buttons[26].pressed { + virtual_rz = virtual_rz.saturating_add(100); + usb_activity = true; + } else if buttons[26].pressed && !buttons[25].pressed { + virtual_rz = virtual_rz.saturating_sub(100); + usb_activity = true; + } else if virtual_rz != 0 && !buttons[25].pressed && !buttons[26].pressed { + // Optional: decay to center + virtual_rz = 0; + usb_activity = true; + } + // Generate led activity when gimbal is moved from idle position for item in axis.iter_mut() { if item.value != item.previous_value { @@ -605,10 +633,12 @@ fn main() -> ! { // Dont send USB HID joystick report if there is no activity // This is to avoid preventing the computer from going to sleep if usb_update_count_down.wait().is_ok() && usb_activity { - match usb_hid_joystick - .device() - .write_report(&get_joystick_report(&mut buttons, &mut axis)) - { + match usb_hid_joystick.device().write_report(&get_joystick_report( + &mut buttons, + &mut axis, + &virtual_ry, + &virtual_rz, + )) { Err(UsbHidError::WouldBlock) => {} Ok(_) => {} Err(e) => { @@ -663,11 +693,15 @@ fn update_status_led( fn get_joystick_report( matrix_keys: &mut [Button; NUMBER_OF_BUTTONS + 2], axis: &mut [GimbalAxis; 4], + virtual_ry: &i16, + virtual_rz: &i16, ) -> JoystickReport { let x: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_LEFT_X].value); let y: i16 = axis_12bit_to_i16(ADC_MAX - axis[GIMBAL_AXIS_LEFT_Y].value); let z: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_RIGHT_X].value); let rx: i16 = axis_12bit_to_i16(ADC_MAX - axis[GIMBAL_AXIS_RIGHT_Y].value); + let ry: i16 = *virtual_ry; + let rz: i16 = *virtual_rz; // Update button state for joystick buttons let mut buttons: u32 = 0; @@ -687,50 +721,12 @@ fn get_joystick_report( y, z, rx, + ry, + rz, buttons, } } -/// Format hat value from 5 switches to USB HID coded value and button state -/// -/// # Arguments -/// * `input` - Hat value coded as -/// bit 2-5: direction (U L R D) -/// bit 1: button state -/// 0 = not pressed -/// 1 = pressed -fn format_hat_value(input: u8) -> (u8, u8) { - const HAT_CENTER: u8 = 8; //8 or 15 (OS-dependent; usually 8) - const HAT_UP: u8 = 0; - const HAT_UP_RIGHT: u8 = 1; - const HAT_RIGHT: u8 = 2; - const HAT_DOWN_RIGHT: u8 = 3; - const HAT_DOWN: u8 = 4; - const HAT_DOWN_LEFT: u8 = 5; - const HAT_LEFT: u8 = 6; - const HAT_UP_LEFT: u8 = 7; - - let direction: u8 = match input & 0xFE { - 2 => HAT_UP, - 4 => HAT_RIGHT, - 6 => HAT_UP_RIGHT, - 8 => HAT_DOWN, - 12 => HAT_DOWN_RIGHT, - 16 => HAT_LEFT, - 24 => HAT_DOWN_LEFT, - 18 => HAT_UP_LEFT, - _ => HAT_CENTER, - }; - - // Alpine hat switch button filter - let mut button_state: u8 = 0; - if input & 0x01 == 0x01 && direction == HAT_CENTER { - button_state = 1; - } - - (direction, button_state) -} - /// Calculate value for joystick axis /// /// # Arguments diff --git a/rp2040/src/usb_joystick_device.rs b/rp2040/src/usb_joystick_device.rs index 216f5ce..aac6b94 100644 --- a/rp2040/src/usb_joystick_device.rs +++ b/rp2040/src/usb_joystick_device.rs @@ -67,15 +67,17 @@ pub const JOYSTICK_DESCRIPTOR: &[u8] = &[ 0x09, 0x04, // Usage (Joystick) 0xA1, 0x01, // Collection (Application) - // 4 signed 16-bit axes: X, Y, Z, Rx + // 6 signed 16-bit axes: X, Y, Z, Rx, Ry, Rz 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) 0x09, 0x33, // Usage (Rx) + 0x09, 0x34, // Usage (Ry) + 0x09, 0x35, // Usage (Rz) 0x16, 0x00, 0x80, // Logical Minimum (-32768) 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 0x75, 0x10, // Report Size (16) - 0x95, 0x04, // Report Count (4) + 0x95, 0x06, // Report Count (6) 0x81, 0x02, // Input (Data,Var,Abs) // 26 Buttons (1-bit each) @@ -102,6 +104,8 @@ pub struct JoystickReport { pub y: i16, // 16bit pub z: i16, // 16bit pub rx: i16, // 16bit + pub ry: i16, // 16bit + pub rz: i16, // 16bit pub buttons: u32, // 32bit } @@ -111,7 +115,7 @@ pub struct Joystick<'a, B: UsbBus> { impl Joystick<'_, B> { pub fn write_report(&mut self, report: &JoystickReport) -> Result<(), UsbHidError> { - let mut data: [u8; 12] = [0; 12]; + let mut data: [u8; 16] = [0; 16]; // Did not make the packed struct work, so doing it manually data[0] = report.x as u8; @@ -122,10 +126,14 @@ impl Joystick<'_, B> { data[5] = (report.z >> 8) as u8; data[6] = report.rx as u8; data[7] = (report.rx >> 8) as u8; - data[8] = report.buttons as u8; - data[9] = (report.buttons >> 8) as u8; - data[10] = (report.buttons >> 16) as u8; - data[11] = (report.buttons >> 24) as u8; + data[8] = report.ry as u8; + data[9] = (report.ry >> 8) as u8; + data[10] = report.rz as u8; + data[11] = (report.rz >> 8) as u8; + data[12] = report.buttons as u8; + data[13] = (report.buttons >> 8) as u8; + data[14] = (report.buttons >> 16) as u8; + data[15] = (report.buttons >> 24) as u8; self.interface .write_report(&data) From 0ab690dae117fcc4e8fc88558e7f473aef3faa47 Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Thu, 29 May 2025 12:11:29 +0200 Subject: [PATCH 3/6] Fixed Linux issue that detect joystick as gamepad --- rp2040/src/usb_joystick_device.rs | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/rp2040/src/usb_joystick_device.rs b/rp2040/src/usb_joystick_device.rs index aac6b94..1d4c292 100644 --- a/rp2040/src/usb_joystick_device.rs +++ b/rp2040/src/usb_joystick_device.rs @@ -67,17 +67,19 @@ pub const JOYSTICK_DESCRIPTOR: &[u8] = &[ 0x09, 0x04, // Usage (Joystick) 0xA1, 0x01, // Collection (Application) - // 6 signed 16-bit axes: X, Y, Z, Rx, Ry, Rz + // 8 signed 16-bit axes: X, Y, Z, Rx, Ry, Rz, Slider, Dial 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) 0x09, 0x33, // Usage (Rx) 0x09, 0x34, // Usage (Ry) 0x09, 0x35, // Usage (Rz) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x37, // Usage (Dial) 0x16, 0x00, 0x80, // Logical Minimum (-32768) 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 0x75, 0x10, // Report Size (16) - 0x95, 0x06, // Report Count (6) + 0x95, 0x08, // Report Count (8) 0x81, 0x02, // Input (Data,Var,Abs) // 26 Buttons (1-bit each) @@ -106,16 +108,18 @@ pub struct JoystickReport { pub rx: i16, // 16bit pub ry: i16, // 16bit pub rz: i16, // 16bit + pub slider: i16, // 16bit + pub dial: i16, // 16bit pub buttons: u32, // 32bit } pub struct Joystick<'a, B: UsbBus> { - interface: Interface<'a, B, InBytes16, OutNone, ReportSingle>, + interface: Interface<'a, B, InBytes32, OutNone, ReportSingle>, } impl Joystick<'_, B> { pub fn write_report(&mut self, report: &JoystickReport) -> Result<(), UsbHidError> { - let mut data: [u8; 16] = [0; 16]; + let mut data: [u8; 20] = [0; 20]; // Did not make the packed struct work, so doing it manually data[0] = report.x as u8; @@ -130,10 +134,14 @@ impl Joystick<'_, B> { data[9] = (report.ry >> 8) as u8; data[10] = report.rz as u8; data[11] = (report.rz >> 8) as u8; - data[12] = report.buttons as u8; - data[13] = (report.buttons >> 8) as u8; - data[14] = (report.buttons >> 16) as u8; - data[15] = (report.buttons >> 24) as u8; + data[12] = report.slider as u8; + data[13] = (report.slider >> 8) as u8; + data[14] = report.dial as u8; + data[15] = (report.dial >> 8) as u8; + data[16] = report.buttons as u8; + data[17] = (report.buttons >> 8) as u8; + data[18] = (report.buttons >> 16) as u8; + data[19] = (report.buttons >> 24) as u8; self.interface .write_report(&data) @@ -143,7 +151,7 @@ impl Joystick<'_, B> { } impl<'a, B: UsbBus> DeviceClass<'a> for Joystick<'a, B> { - type I = Interface<'a, B, InBytes16, OutNone, ReportSingle>; + type I = Interface<'a, B, InBytes32, OutNone, ReportSingle>; fn interface(&mut self) -> &mut Self::I { &mut self.interface @@ -157,7 +165,7 @@ impl<'a, B: UsbBus> DeviceClass<'a> for Joystick<'a, B> { } pub struct JoystickConfig<'a> { - interface: InterfaceConfig<'a, InBytes16, OutNone, ReportSingle>, + interface: InterfaceConfig<'a, InBytes32, OutNone, ReportSingle>, } impl Default for JoystickConfig<'_> { @@ -178,7 +186,7 @@ impl Default for JoystickConfig<'_> { impl<'a> JoystickConfig<'a> { #[must_use] - pub fn new(interface: InterfaceConfig<'a, InBytes16, OutNone, ReportSingle>) -> Self { + pub fn new(interface: InterfaceConfig<'a, InBytes32, OutNone, ReportSingle>) -> Self { Self { interface } } } From 6243f552c3128e6390630bf1aa981fce4a2ead5a Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Thu, 29 May 2025 12:12:11 +0200 Subject: [PATCH 4/6] Added expo and direction compensation to the virtual axis --- rp2040/src/main.rs | 98 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/rp2040/src/main.rs b/rp2040/src/main.rs index bdd661a..1b5a7fb 100644 --- a/rp2040/src/main.rs +++ b/rp2040/src/main.rs @@ -251,9 +251,8 @@ fn main() -> ! { let mut axis: [GimbalAxis; NBR_OF_GIMBAL_AXIS] = [Default::default(); NBR_OF_GIMBAL_AXIS]; let mut buttons: [Button; NUMBER_OF_BUTTONS + 2] = [Button::default(); NUMBER_OF_BUTTONS + 2]; - let mut virtual_ry: i16 = 0; - let mut virtual_rz: i16 = 0; - let mut virtual_step: i16 = 512; + let mut virtual_ry: u16 = AXIS_CENTER; + let mut virtual_rz: u16 = AXIS_CENTER; let mut gimbal_mode: u8; // HW Button index map: @@ -337,6 +336,7 @@ fn main() -> ! { // Table for gimbal expo curve lookup insded of doing floating point math for every analog read let expo_lut: [u16; ADC_MAX as usize + 1] = generate_expo_lut(0.3); + let expo_lut_virtual: [u16; ADC_MAX as usize + 1] = generate_expo_lut(0.6); // Create dynamic smoother array for gimbal axis let mut smoother: [DynamicSmootherEcoI32; NBR_OF_GIMBAL_AXIS] = [ @@ -564,27 +564,59 @@ fn main() -> ! { } // Update Virtual RY - if buttons[1].pressed && !buttons[0].pressed { - virtual_ry = virtual_ry.saturating_add(100); + let virtual_step: u16 = 5; + // Compensate value when changing direction + if buttons[1].pressed && !buttons[0].pressed && virtual_ry < AXIS_CENTER { + virtual_ry = AXIS_CENTER + (AXIS_CENTER - virtual_ry); + } else if buttons[0].pressed && !buttons[1].pressed && virtual_ry > AXIS_CENTER { + virtual_ry = AXIS_CENTER - (virtual_ry - AXIS_CENTER); + } + // Move virtual axis + if buttons[1].pressed && !buttons[0].pressed && virtual_ry < ADC_MAX - virtual_step { + virtual_ry = virtual_ry + virtual_step; usb_activity = true; - } else if buttons[0].pressed && !buttons[1].pressed { - virtual_ry = virtual_ry.saturating_sub(100); + } else if buttons[0].pressed + && !buttons[1].pressed + && virtual_ry > ADC_MIN + virtual_step + { + virtual_ry = virtual_ry - virtual_step; usb_activity = true; - } else if virtual_ry != 0 && !buttons[1].pressed && !buttons[0].pressed { - // Optional: decay to center - virtual_ry = 0; + } else if (virtual_ry != AXIS_CENTER && !buttons[1].pressed && !buttons[0].pressed) + || (buttons[1].pressed && buttons[0].pressed) + { + if virtual_ry < AXIS_CENTER + virtual_step { + virtual_ry = virtual_ry + virtual_step; + } else if virtual_ry > AXIS_CENTER - virtual_step { + virtual_ry = virtual_ry - virtual_step; + } usb_activity = true; } + // Update Virtual RZ - if buttons[25].pressed && !buttons[26].pressed { - virtual_rz = virtual_rz.saturating_add(100); + // Compensate value when changing direction + if buttons[25].pressed && !buttons[26].pressed && virtual_rz < AXIS_CENTER { + virtual_rz = AXIS_CENTER + (AXIS_CENTER - virtual_rz); + } else if buttons[26].pressed && !buttons[25].pressed && virtual_rz > AXIS_CENTER { + virtual_rz = AXIS_CENTER - (virtual_rz - AXIS_CENTER); + } + // Move virtual axis + if buttons[25].pressed && !buttons[26].pressed && virtual_rz < ADC_MAX - virtual_step { + virtual_rz = virtual_rz + virtual_step; usb_activity = true; - } else if buttons[26].pressed && !buttons[25].pressed { - virtual_rz = virtual_rz.saturating_sub(100); + } else if buttons[26].pressed + && !buttons[25].pressed + && virtual_rz > ADC_MIN + virtual_step + { + virtual_rz = virtual_rz - virtual_step; usb_activity = true; - } else if virtual_rz != 0 && !buttons[25].pressed && !buttons[26].pressed { - // Optional: decay to center - virtual_rz = 0; + } else if (virtual_rz != AXIS_CENTER && !buttons[25].pressed && !buttons[26].pressed) + || (buttons[25].pressed && buttons[26].pressed) + { + if virtual_rz < AXIS_CENTER + virtual_step { + virtual_rz = virtual_rz + virtual_step; + } else if virtual_rz > AXIS_CENTER - virtual_step { + virtual_rz = virtual_rz - virtual_step; + } usb_activity = true; } @@ -636,8 +668,24 @@ fn main() -> ! { match usb_hid_joystick.device().write_report(&get_joystick_report( &mut buttons, &mut axis, - &virtual_ry, - &virtual_rz, + calculate_axis_value( + virtual_ry, + ADC_MIN, + ADC_MAX, + AXIS_CENTER, + (0, 0, 0), + true, + &expo_lut_virtual, + ), + calculate_axis_value( + virtual_rz, + ADC_MIN, + ADC_MAX, + AXIS_CENTER, + (0, 0, 0), + true, + &expo_lut_virtual, + ), )) { Err(UsbHidError::WouldBlock) => {} Ok(_) => {} @@ -693,15 +741,17 @@ fn update_status_led( fn get_joystick_report( matrix_keys: &mut [Button; NUMBER_OF_BUTTONS + 2], axis: &mut [GimbalAxis; 4], - virtual_ry: &i16, - virtual_rz: &i16, + virtual_ry: u16, + virtual_rz: u16, ) -> JoystickReport { let x: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_LEFT_X].value); let y: i16 = axis_12bit_to_i16(ADC_MAX - axis[GIMBAL_AXIS_LEFT_Y].value); let z: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_RIGHT_X].value); let rx: i16 = axis_12bit_to_i16(ADC_MAX - axis[GIMBAL_AXIS_RIGHT_Y].value); - let ry: i16 = *virtual_ry; - let rz: i16 = *virtual_rz; + let ry: i16 = axis_12bit_to_i16(virtual_ry); + let rz: i16 = axis_12bit_to_i16(virtual_rz); + let slider: i16 = 0; + let dial: i16 = 0; // Update button state for joystick buttons let mut buttons: u32 = 0; @@ -723,6 +773,8 @@ fn get_joystick_report( rx, ry, rz, + slider, + dial, buttons, } } From 9572df9e3b9406e5b26380a6fb1fefce4862b18e Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Thu, 17 Jul 2025 19:03:56 +0200 Subject: [PATCH 5/6] Changed to 7 axis and added back one HAT swith --- layout.drawio | 1101 +++-------------------------- rp2040/src/main.rs | 431 ++++++++--- rp2040/src/usb_joystick_device.rs | 47 +- 3 files changed, 434 insertions(+), 1145 deletions(-) diff --git a/layout.drawio b/layout.drawio index 4c6c3a0..4492d13 100644 --- a/layout.drawio +++ b/layout.drawio @@ -1,200 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -204,43 +10,19 @@ - + - + - - - - - - - - - - - - - - - - - - - + - + - - - - - - - + @@ -291,7 +73,7 @@ - + @@ -321,844 +103,143 @@ - - + + - - - - + - + - + - + - + - + - + - + - + - - + + - - + + - + - + - - + + - + - + - + - - + + - - + + - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/rp2040/src/main.rs b/rp2040/src/main.rs index 1b5a7fb..1e348c8 100644 --- a/rp2040/src/main.rs +++ b/rp2040/src/main.rs @@ -49,6 +49,73 @@ pub static BOOT2_FIRMWARE: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; const XTAL_FREQ_HZ: u32 = 12_000_000u32; // Public constants +// HW Button index map: +// --------------------------------------------------------------- +// | 0 L| 1 U| 25 U | | 2 | | 26 U | 4 U| 3 L| +// --------------------------------------------------------------- +// | | 5 | 6 | 7 | | 12 | 11 | 10 | | +// | | +// | | 8 | | 13 | | +// | | 9 | | 14 | | +// | X1/Y1 X2/Y2 | +// | | 16 | | 21 | | +// | | 19 | 15 | 17 | | 24 | 20 | 22 | | +// | | 18 | | 23 | | +// --------------------------------------------------------------- +// Config Layer (holding CONFIG button) +// --------------------------------------------------------------- +// |BOOT L| CAL U| | CONFIG | | - | - | +// --------------------------------------------------------------- +// | | - | - | - | | - | - | - | | +// | | +// | |C M10| | - | | +// | |C M7 | | - | | +// | -/- -/- | +// | | - | | - | | +// | | - | - | - | | - |C OK | - | | +// | | - | | - | | +// --------------------------------------------------------------- +// +pub const CONFIG_BUTTON: usize = 2; +pub const BOOT_BUTTON: usize = 0; +pub const CAL_BUTTON: usize = 1; +pub const CAL_DONE_BUTTON: usize = 20; +pub const CAL_M10_GIMBLE_BUTTON: usize = 8; +pub const CAL_M7_GIMBLE_BUTTON: usize = 9; + +pub const BUTTON_FRONT_LEFT_LOWER: usize = 0; +pub const BUTTON_FRONT_LEFT_UPPER: usize = 1; +pub const BUTTON_FRONT_LEFT_EXTRA: usize = 25; +pub const BUTTON_FRONT_CONFIG: usize = 2; +pub const BUTTON_FRONT_RIGHT_LOWER: usize = 3; +pub const BUTTON_FRONT_RIGHT_UPPER: usize = 4; +pub const BUTTON_FRONT_RIGHT_EXTRA: usize = 26; +pub const BUTTON_TOP_LEFT_LOW: usize = 5; +pub const BUTTON_TOP_LEFT_HIGH: usize = 6; +pub const BUTTON_TOP_LEFT_MODE: usize = 7; +pub const BUTTON_TOP_LEFT_UP: usize = 8; +pub const BUTTON_TOP_LEFT_DOWN: usize = 9; +pub const BUTTON_TOP_LEFT_HAT: usize = 15; +pub const BUTTON_TOP_LEFT_HAT_UP: usize = 16; +pub const BUTTON_TOP_LEFT_HAT_RIGHT: usize = 17; +pub const BUTTON_TOP_LEFT_HAT_DOWN: usize = 18; +pub const BUTTON_TOP_LEFT_HAT_LEFT: usize = 19; +pub const BUTTON_TOP_RIGHT_LOW: usize = 10; +pub const BUTTON_TOP_RIGHT_HIGH: usize = 11; +pub const BUTTON_TOP_RIGHT_MODE: usize = 12; +pub const BUTTON_TOP_RIGHT_UP: usize = 13; +pub const BUTTON_TOP_RIGHT_DOWN: usize = 14; +pub const BUTTON_TOP_RIGHT_HAT: usize = 20; +pub const BUTTON_TOP_RIGHT_HAT_UP: usize = 21; +pub const BUTTON_TOP_RIGHT_HAT_RIGHT: usize = 22; +pub const BUTTON_TOP_RIGHT_HAT_DOWN: usize = 23; +pub const BUTTON_TOP_RIGHT_HAT_LEFT: usize = 24; + +// Special button functions +// Throttle hold: +pub const TH_BUTTON: usize = 7; +pub const VT_BUTTON: usize = 12; + pub const BUTTON_ROWS: usize = 5; pub const BUTTON_COLS: usize = 5; pub const NUMBER_OF_BUTTONS: usize = BUTTON_ROWS * BUTTON_COLS; @@ -81,7 +148,17 @@ pub struct Button { pub previous_pressed: bool, pub usb_changed: bool, pub usb_changed_to_pressed: bool, - pub usb_button: usize, + pub usb_button: usize, // For short press + pub usb_button_long: usize, // For long press + pub enable_long_press: bool, // Flag to enable special behavior + pub enable_long_hold: bool, // Flag to enable special behavior + + // Internals + pub press_start_time: u32, // When physical press started + pub long_press_handled: bool, // True if long press activated + pub active_usb_button: usize, // Currently active USB button + pub usb_press_active: bool, // Is USB press currently "down" + pub usb_press_start_time: u32, // When USB press was sent } #[derive(Copy, Clone)] @@ -235,6 +312,9 @@ fn main() -> ! { let mut status_led_count_down = timer.count_down(); status_led_count_down.start(250.millis()); + let mut ms_count_down = timer.count_down(); + ms_count_down.start(1.millis()); + let mut scan_count_down = timer.count_down(); scan_count_down.start(200u32.micros()); @@ -248,6 +328,7 @@ fn main() -> ! { let mut usb_active: bool = false; let mut calibration_active: bool = false; let mut throttle_hold_enable: bool = false; + let mut vt_enable: bool = false; let mut axis: [GimbalAxis; NBR_OF_GIMBAL_AXIS] = [Default::default(); NBR_OF_GIMBAL_AXIS]; let mut buttons: [Button; NUMBER_OF_BUTTONS + 2] = [Button::default(); NUMBER_OF_BUTTONS + 2]; @@ -255,84 +336,58 @@ fn main() -> ! { let mut virtual_rz: u16 = AXIS_CENTER; let mut gimbal_mode: u8; - // HW Button index map: - // --------------------------------------------------------------- - // | 0 L| 1 U| 25 U | | 2 | | 26 U | 4 U| 3 L| - // --------------------------------------------------------------- - // | | 5 | 6 | 7 | | 12 | 11 | 10 | | - // | | - // | | 8 | | 13 | | - // | | 9 | | 14 | | - // | X1/Y1 X2/Y2 | - // | | 16 | | 21 | | - // | | 19 | 15 | 17 | | 24 | 20 | 22 | | - // | | 18 | | 23 | | - // --------------------------------------------------------------- - // Config Layer (holding CONFIG button) - // --------------------------------------------------------------- - // |BOOT L| CAL U| | CONFIG | | - | - | - // --------------------------------------------------------------- - // | | - | - | - | | - | - | - | | - // | | - // | |C M10| | - | | - // | |C M7 | | - | | - // | -/- -/- | - // | | - | | - | | - // | | - | - | - | | - |C OK | - | | - // | | - | | - | | - // --------------------------------------------------------------- - // USB HID joystick map : - // --------------------------------------------------------------- - // | Ry- L| Ry+ U| Rz- U| | B26 | | Rz+ U| B1 U| B2 L| - // --------------------------------------------------------------- - // | | B5 | B14 | B9 | | B10 | B15 | B6 | | - // | | - // | | B13 | | B17 | | - // | | B16 | | B12 | | - // | X1/Y1 X2/Y2 | - // | | B18 | | B22 | | - // | | B21 | B11 | B19 | | B25 | TH | B23 | | - // | | B20 | | B24 | | - // --------------------------------------------------------------- - // - pub const CONFIG_BUTTON: usize = 2; - pub const BOOT_BUTTON: usize = 0; - pub const CAL_BUTTON: usize = 1; - pub const CAL_DONE_BUTTON: usize = 20; - pub const CAL_M10_GIMBLE_BUTTON: usize = 8; - pub const CAL_M7_GIMBLE_BUTTON: usize = 9; - // Special button functions - // Throttle hold: - pub const TH_BUTTON: usize = 20; - // Set up usb button layout - buttons[0].usb_button = 4; - buttons[1].usb_button = 3; - buttons[2].usb_button = 26; // Button used as global config. Do not define secondary USB button! - buttons[3].usb_button = 2; - buttons[4].usb_button = 1; - buttons[5].usb_button = 5; - buttons[6].usb_button = 14; - buttons[7].usb_button = 9; - buttons[8].usb_button = 13; - buttons[9].usb_button = 16; - buttons[10].usb_button = 6; - buttons[11].usb_button = 15; - buttons[12].usb_button = 10; - buttons[13].usb_button = 17; - buttons[14].usb_button = 12; - buttons[15].usb_button = 11; - buttons[16].usb_button = 18; - buttons[17].usb_button = 19; - buttons[18].usb_button = 20; - buttons[19].usb_button = 21; - buttons[20].usb_button = 0; - buttons[21].usb_button = 22; - buttons[22].usb_button = 23; - buttons[23].usb_button = 24; - buttons[24].usb_button = 25; - buttons[25].usb_button = 7; - buttons[26].usb_button = 8; + buttons[BUTTON_FRONT_LEFT_LOWER].usb_button = 29; + buttons[BUTTON_FRONT_LEFT_UPPER].usb_button = 28; + buttons[BUTTON_FRONT_CONFIG].usb_button = 32; // Button used as global config. + buttons[BUTTON_FRONT_CONFIG].usb_button_long = 3; + buttons[BUTTON_FRONT_CONFIG].enable_long_press = true; + buttons[BUTTON_FRONT_RIGHT_LOWER].usb_button = 2; + buttons[BUTTON_FRONT_RIGHT_UPPER].usb_button = 1; + buttons[BUTTON_TOP_LEFT_LOW].usb_button = 4; + buttons[BUTTON_TOP_LEFT_LOW].usb_button_long = 5; + buttons[BUTTON_TOP_LEFT_LOW].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_LOW].enable_long_hold = true; + buttons[BUTTON_TOP_LEFT_HIGH].usb_button = 6; + buttons[BUTTON_TOP_LEFT_HIGH].usb_button_long = 7; + buttons[BUTTON_TOP_LEFT_HIGH].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_MODE].usb_button = 0; + buttons[BUTTON_TOP_LEFT_UP].usb_button = 12; + buttons[BUTTON_TOP_LEFT_UP].usb_button_long = 13; + buttons[BUTTON_TOP_LEFT_UP].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_DOWN].usb_button = 16; + buttons[BUTTON_TOP_LEFT_DOWN].usb_button_long = 17; + buttons[BUTTON_TOP_LEFT_DOWN].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_DOWN].enable_long_hold = true; + buttons[BUTTON_TOP_RIGHT_LOW].usb_button = 10; + buttons[BUTTON_TOP_RIGHT_LOW].usb_button_long = 11; + buttons[BUTTON_TOP_RIGHT_LOW].enable_long_press = true; + buttons[BUTTON_TOP_RIGHT_HIGH].usb_button = 8; + buttons[BUTTON_TOP_RIGHT_HIGH].usb_button_long = 9; + buttons[BUTTON_TOP_RIGHT_HIGH].enable_long_press = true; + buttons[BUTTON_TOP_RIGHT_MODE].usb_button = 0; + buttons[BUTTON_TOP_RIGHT_UP].usb_button = 14; + buttons[BUTTON_TOP_RIGHT_UP].usb_button_long = 15; + buttons[BUTTON_TOP_RIGHT_UP].enable_long_press = true; + buttons[BUTTON_TOP_RIGHT_DOWN].usb_button = 18; + buttons[BUTTON_TOP_RIGHT_DOWN].usb_button_long = 19; + buttons[BUTTON_TOP_RIGHT_DOWN].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_HAT].usb_button = 20; + buttons[BUTTON_TOP_LEFT_HAT].usb_button_long = 21; + buttons[BUTTON_TOP_LEFT_HAT].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_HAT_UP].usb_button = 22; + buttons[BUTTON_TOP_LEFT_HAT_RIGHT].usb_button = 23; + buttons[BUTTON_TOP_LEFT_HAT_DOWN].usb_button = 24; + buttons[BUTTON_TOP_LEFT_HAT_LEFT].usb_button = 25; + buttons[BUTTON_TOP_RIGHT_HAT].usb_button = 26; + buttons[BUTTON_TOP_RIGHT_HAT].usb_button_long = 27; + buttons[BUTTON_TOP_RIGHT_HAT].enable_long_press = true; + buttons[BUTTON_TOP_RIGHT_HAT_UP].usb_button = 33; + buttons[BUTTON_TOP_RIGHT_HAT_RIGHT].usb_button = 34; + buttons[BUTTON_TOP_RIGHT_HAT_DOWN].usb_button = 35; + buttons[BUTTON_TOP_RIGHT_HAT_LEFT].usb_button = 36; + buttons[BUTTON_FRONT_LEFT_EXTRA].usb_button = 30; + buttons[BUTTON_FRONT_RIGHT_EXTRA].usb_button = 31; // Table for gimbal expo curve lookup insded of doing floating point math for every analog read let expo_lut: [u16; ADC_MAX as usize + 1] = generate_expo_lut(0.3); @@ -418,6 +473,7 @@ fn main() -> ! { &usb_active, &calibration_active, &throttle_hold_enable, + &vt_enable, ); } @@ -428,28 +484,40 @@ fn main() -> ! { } // Updated extra buttons - buttons[25].pressed = left_extra_button.is_low().unwrap(); - buttons[26].pressed = right_extra_button.is_low().unwrap(); + buttons[BUTTON_FRONT_LEFT_EXTRA].pressed = left_extra_button.is_low().unwrap(); + buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed = right_extra_button.is_low().unwrap(); // Filter left hat swith buttons - for i in 16..=19 { - if (16..=19).filter(|&j| j != i).any(|j| buttons[j].pressed) { + for i in BUTTON_TOP_LEFT_HAT_UP..=BUTTON_TOP_LEFT_HAT_LEFT { + if (BUTTON_TOP_LEFT_HAT_UP..=BUTTON_TOP_LEFT_HAT_LEFT) + .filter(|&j| j != i) + .any(|j| buttons[j].pressed) + { buttons[i].pressed = false; } } // Fix button state for center hat press on hat - if buttons[16..=19].iter().any(|b| b.pressed) { - buttons[15].pressed = false; + if buttons[BUTTON_TOP_LEFT_HAT_UP..=BUTTON_TOP_LEFT_HAT_LEFT] + .iter() + .any(|b| b.pressed) + { + buttons[BUTTON_TOP_LEFT_HAT].pressed = false; } // Filter right hat swith buttons - for i in 21..=24 { - if (21..=24).filter(|&j| j != i).any(|j| buttons[j].pressed) { + for i in BUTTON_TOP_RIGHT_HAT_UP..=BUTTON_TOP_RIGHT_HAT_LEFT { + if (BUTTON_TOP_RIGHT_HAT_UP..=BUTTON_TOP_RIGHT_HAT_LEFT) + .filter(|&j| j != i) + .any(|j| buttons[j].pressed) + { buttons[i].pressed = false; } } // Fix button state for center hat press on hat - if buttons[21..=24].iter().any(|b| b.pressed) { - buttons[20].pressed = false; + if buttons[BUTTON_TOP_RIGHT_HAT_UP..=BUTTON_TOP_RIGHT_HAT_LEFT] + .iter() + .any(|b| b.pressed) + { + buttons[BUTTON_TOP_RIGHT_HAT].pressed = false; } // Secondary way to enter bootloader (pressing all left hands buttons except the hat @@ -564,25 +632,37 @@ fn main() -> ! { } // Update Virtual RY - let virtual_step: u16 = 5; + let virtual_step: u16 = 2; // Compensate value when changing direction - if buttons[1].pressed && !buttons[0].pressed && virtual_ry < AXIS_CENTER { - virtual_ry = AXIS_CENTER + (AXIS_CENTER - virtual_ry); - } else if buttons[0].pressed && !buttons[1].pressed && virtual_ry > AXIS_CENTER { - virtual_ry = AXIS_CENTER - (virtual_ry - AXIS_CENTER); + if buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && !buttons[BUTTON_FRONT_LEFT_LOWER].pressed + && virtual_ry < AXIS_CENTER + { + virtual_ry = AXIS_CENTER + (AXIS_CENTER - virtual_ry) / 2; + } else if buttons[BUTTON_FRONT_LEFT_LOWER].pressed + && !buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && virtual_ry > AXIS_CENTER + { + virtual_ry = AXIS_CENTER - (virtual_ry - AXIS_CENTER) / 2; } // Move virtual axis - if buttons[1].pressed && !buttons[0].pressed && virtual_ry < ADC_MAX - virtual_step { + if buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && !buttons[BUTTON_FRONT_LEFT_LOWER].pressed + && virtual_ry < ADC_MAX - virtual_step + { virtual_ry = virtual_ry + virtual_step; usb_activity = true; - } else if buttons[0].pressed - && !buttons[1].pressed + } else if buttons[BUTTON_FRONT_LEFT_LOWER].pressed + && !buttons[BUTTON_FRONT_LEFT_UPPER].pressed && virtual_ry > ADC_MIN + virtual_step { virtual_ry = virtual_ry - virtual_step; usb_activity = true; - } else if (virtual_ry != AXIS_CENTER && !buttons[1].pressed && !buttons[0].pressed) - || (buttons[1].pressed && buttons[0].pressed) + } else if (virtual_ry != AXIS_CENTER + && !buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && !buttons[BUTTON_FRONT_LEFT_LOWER].pressed) + || (buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && buttons[BUTTON_FRONT_LEFT_LOWER].pressed) { if virtual_ry < AXIS_CENTER + virtual_step { virtual_ry = virtual_ry + virtual_step; @@ -594,23 +674,35 @@ fn main() -> ! { // Update Virtual RZ // Compensate value when changing direction - if buttons[25].pressed && !buttons[26].pressed && virtual_rz < AXIS_CENTER { - virtual_rz = AXIS_CENTER + (AXIS_CENTER - virtual_rz); - } else if buttons[26].pressed && !buttons[25].pressed && virtual_rz > AXIS_CENTER { - virtual_rz = AXIS_CENTER - (virtual_rz - AXIS_CENTER); + if buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed + && !buttons[BUTTON_FRONT_LEFT_EXTRA].pressed + && virtual_rz < AXIS_CENTER + { + virtual_rz = AXIS_CENTER + (AXIS_CENTER - virtual_rz) / 2; + } else if buttons[BUTTON_FRONT_LEFT_EXTRA].pressed + && !buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed + && virtual_rz > AXIS_CENTER + { + virtual_rz = AXIS_CENTER - (virtual_rz - AXIS_CENTER) / 2; } // Move virtual axis - if buttons[25].pressed && !buttons[26].pressed && virtual_rz < ADC_MAX - virtual_step { + if buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed + && !buttons[BUTTON_FRONT_LEFT_EXTRA].pressed + && virtual_rz < ADC_MAX - virtual_step + { virtual_rz = virtual_rz + virtual_step; usb_activity = true; - } else if buttons[26].pressed - && !buttons[25].pressed + } else if buttons[BUTTON_FRONT_LEFT_EXTRA].pressed + && !buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed && virtual_rz > ADC_MIN + virtual_step { virtual_rz = virtual_rz - virtual_step; usb_activity = true; - } else if (virtual_rz != AXIS_CENTER && !buttons[25].pressed && !buttons[26].pressed) - || (buttons[25].pressed && buttons[26].pressed) + } else if (virtual_rz != AXIS_CENTER + && !buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed + && !buttons[BUTTON_FRONT_LEFT_EXTRA].pressed) + || (buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed + && buttons[BUTTON_FRONT_LEFT_EXTRA].pressed) { if virtual_rz < AXIS_CENTER + virtual_step { virtual_rz = virtual_rz + virtual_step; @@ -630,10 +722,10 @@ fn main() -> ! { // Generate led activity when a button is pressed for (index, key) in buttons.iter_mut().enumerate() { + update_button_press_type(key, (timer.get_counter().ticks() / 1000) as u32); + if key.pressed != key.previous_pressed { key.usb_changed = true; - key.usb_changed_to_pressed = key.pressed; - usb_activity = true; } // Set throttle_hold_value if key.pressed != key.previous_pressed @@ -650,6 +742,12 @@ fn main() -> ! { { axis[GIMBAL_AXIS_LEFT_Y].hold = AXIS_CENTER; axis[GIMBAL_AXIS_LEFT_Y].hold_pending = true; + } else if key.pressed != key.previous_pressed && key.pressed && index == VT_BUTTON { + vt_enable = !vt_enable; + } + + if key.usb_changed { + usb_activity = true; } key.previous_pressed = key.pressed; } @@ -686,6 +784,7 @@ fn main() -> ! { true, &expo_lut_virtual, ), + &vt_enable, )) { Err(UsbHidError::WouldBlock) => {} Ok(_) => {} @@ -699,6 +798,60 @@ fn main() -> ! { } } +fn update_button_press_type(button: &mut Button, current_time: u32) { + const LONG_PRESS_THRESHOLD: u32 = 200; + const USB_MIN_HOLD_MS: u32 = 50; + + // Pressing button + if button.pressed && !button.previous_pressed { + button.press_start_time = current_time; + button.long_press_handled = false; + } + + // While held: trigger long press if applicable + if button.pressed && button.enable_long_press && !button.long_press_handled { + if current_time - button.press_start_time >= LONG_PRESS_THRESHOLD { + button.active_usb_button = button.usb_button_long; + button.usb_press_start_time = current_time; + button.usb_press_active = true; + button.usb_changed = true; + button.long_press_handled = true; + } + } + + // Releasing button + if !button.pressed && button.previous_pressed { + // If long press wasn't triggered, it's a short press + if (!button.enable_long_press || !button.long_press_handled) && button.usb_button != 0 { + button.active_usb_button = button.usb_button; + button.usb_press_start_time = current_time; + button.usb_press_active = true; + button.usb_changed = true; + } + + // If long press was active, release now + if button.long_press_handled && button.usb_press_active { + button.usb_changed = true; + button.usb_press_active = false; + button.active_usb_button = 0; + } + } + + // Auto-release for short press after minimum hold time + if button.usb_press_active + && (!button.pressed + && !button.long_press_handled + && current_time - button.usb_press_start_time >= USB_MIN_HOLD_MS) + || (!button.enable_long_hold + && button.long_press_handled + && current_time - button.usb_press_start_time >= USB_MIN_HOLD_MS) + { + button.usb_changed = true; + button.usb_press_active = false; + button.active_usb_button = 0; + } +} + /// Update status LED colour /// /// Waiting for USB enumerate = flashing green @@ -715,6 +868,7 @@ fn update_status_led( usb_active: &bool, calibration_active: &bool, throttle_hold_enable: &bool, + vt_enable: &bool, ) where I: AnyPin, P: PIOExt, @@ -724,6 +878,8 @@ fn update_status_led( status_led.update(StatusMode::ActivityFlash); } else if !*usb_active { status_led.update(StatusMode::NormalFlash); + } else if *usb_active && *vt_enable { + status_led.update(StatusMode::Activity); } else if *usb_active && *throttle_hold_enable { status_led.update(StatusMode::Other); } else if *usb_active && !*throttle_hold_enable { @@ -743,21 +899,64 @@ fn get_joystick_report( axis: &mut [GimbalAxis; 4], virtual_ry: u16, virtual_rz: u16, + vt_enable: &bool, ) -> JoystickReport { let x: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_LEFT_X].value); - let y: i16 = axis_12bit_to_i16(ADC_MAX - axis[GIMBAL_AXIS_LEFT_Y].value); - let z: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_RIGHT_X].value); + let y: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_LEFT_Y].value); + let mut z: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_RIGHT_X].value); let rx: i16 = axis_12bit_to_i16(ADC_MAX - axis[GIMBAL_AXIS_RIGHT_Y].value); let ry: i16 = axis_12bit_to_i16(virtual_ry); let rz: i16 = axis_12bit_to_i16(virtual_rz); - let slider: i16 = 0; - let dial: i16 = 0; + let mut slider: i16 = axis_12bit_to_i16(ADC_MIN); + let mut hat: u8 = 8; // Hat center position + + // Virtual axix control. Disables z and rx axis and using girght gimbal Y axis to control + // slider axis. Values from center stick to max or min will be recalculated to min to max. + if *vt_enable { + if axis[GIMBAL_AXIS_RIGHT_X].value >= AXIS_CENTER { + slider = axis_12bit_to_i16(remap( + axis[GIMBAL_AXIS_RIGHT_X].value, + AXIS_CENTER, + ADC_MAX, + ADC_MIN, + ADC_MAX, + )); + } else { + slider = axis_12bit_to_i16( + ADC_MAX + - remap( + axis[GIMBAL_AXIS_RIGHT_X].value, + ADC_MIN, + AXIS_CENTER, + ADC_MIN, + ADC_MAX, + ), + ); + } + z = 0; + } // Update button state for joystick buttons let mut buttons: u32 = 0; for key in matrix_keys.iter_mut() { - if key.pressed && key.usb_button != 0 { - buttons |= 1 << (key.usb_button - 1); + if key.enable_long_press { + if key.active_usb_button != 0 { + // Check if key is assigned as hat switch + if key.active_usb_button >= 33 && key.active_usb_button <= 36 { + hat = (key.active_usb_button as u8 - 33) * 2; + } else { + buttons |= 1 << (key.active_usb_button - 1); + } + } + } else { + if key.pressed && key.usb_button != 0 { + // Check if key is assigned as hat switch + if key.usb_button >= 33 && key.usb_button <= 36 { + hat = (key.usb_button as u8 - 33) * 2; + } else { + buttons |= 1 << (key.usb_button - 1); + } + } } } @@ -774,7 +973,7 @@ fn get_joystick_report( ry, rz, slider, - dial, + hat, buttons, } } diff --git a/rp2040/src/usb_joystick_device.rs b/rp2040/src/usb_joystick_device.rs index 1d4c292..be6118c 100644 --- a/rp2040/src/usb_joystick_device.rs +++ b/rp2040/src/usb_joystick_device.rs @@ -67,7 +67,7 @@ pub const JOYSTICK_DESCRIPTOR: &[u8] = &[ 0x09, 0x04, // Usage (Joystick) 0xA1, 0x01, // Collection (Application) - // 8 signed 16-bit axes: X, Y, Z, Rx, Ry, Rz, Slider, Dial + // 7 signed 16-bit axes: X, Y, Z, Rx, Ry, Rz, Slider 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) @@ -75,28 +75,38 @@ pub const JOYSTICK_DESCRIPTOR: &[u8] = &[ 0x09, 0x34, // Usage (Ry) 0x09, 0x35, // Usage (Rz) 0x09, 0x36, // Usage (Slider) - 0x09, 0x37, // Usage (Dial) 0x16, 0x00, 0x80, // Logical Minimum (-32768) 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 0x75, 0x10, // Report Size (16) - 0x95, 0x08, // Report Count (8) + 0x95, 0x07, // Report Count (7) 0x81, 0x02, // Input (Data,Var,Abs) - // 26 Buttons (1-bit each) + // 1 Hat Switch + 0x09, 0x39, // Usage (Hat switch) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x07, // Logical Maximum (7) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01, // Physical Maximum (315) + 0x65, 0x14, // Unit (Eng Rot: Degrees) + 0x75, 0x04, // Report Size (4) + 0x95, 0x01, // Report Count (1) + 0x81, 0x42, // Input (Data,Var,Abs,Null) + + // Padding for 4 bits to align to byte + 0x75, 0x04, // Report Size (4) + 0x95, 0x01, // Report Count (1) + 0x81, 0x03, // Input (Const,Var,Abs) – padding + + // 32 Buttons (1-bit each) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (1) - 0x29, 0x1A, // Usage Maximum (26) + 0x29, 0x20, // Usage Maximum (32) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x75, 0x01, // Report Size (1) - 0x95, 0x1A, // Report Count (26) + 0x95, 0x20, // Report Count (32) 0x81, 0x02, // Input (Data,Var,Abs) - // Padding to align buttons to byte boundary (26 bits → +6 bits padding) - 0x75, 0x01, // Report Size (1) - 0x95, 0x06, // Report Count (6) - 0x81, 0x03, // Input (Const,Var,Abs) - 0xC0 // End Collection ]; @@ -109,7 +119,7 @@ pub struct JoystickReport { pub ry: i16, // 16bit pub rz: i16, // 16bit pub slider: i16, // 16bit - pub dial: i16, // 16bit + pub hat: u8, // 8bit pub buttons: u32, // 32bit } @@ -119,7 +129,7 @@ pub struct Joystick<'a, B: UsbBus> { impl Joystick<'_, B> { pub fn write_report(&mut self, report: &JoystickReport) -> Result<(), UsbHidError> { - let mut data: [u8; 20] = [0; 20]; + let mut data: [u8; 19] = [0; 19]; // Did not make the packed struct work, so doing it manually data[0] = report.x as u8; @@ -136,12 +146,11 @@ impl Joystick<'_, B> { data[11] = (report.rz >> 8) as u8; data[12] = report.slider as u8; data[13] = (report.slider >> 8) as u8; - data[14] = report.dial as u8; - data[15] = (report.dial >> 8) as u8; - data[16] = report.buttons as u8; - data[17] = (report.buttons >> 8) as u8; - data[18] = (report.buttons >> 16) as u8; - data[19] = (report.buttons >> 24) as u8; + data[14] = report.hat; + data[15] = report.buttons as u8; + data[16] = (report.buttons >> 8) as u8; + data[17] = (report.buttons >> 16) as u8; + data[18] = (report.buttons >> 24) as u8; self.interface .write_report(&data) From 2e9f2f9fa31327aaa3557f2610283d2cdc2c7f85 Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Thu, 24 Jul 2025 22:16:03 +0200 Subject: [PATCH 6/6] Code cleanup --- rp2040/src/main.rs | 95 +++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/rp2040/src/main.rs b/rp2040/src/main.rs index 1e348c8..fa5e844 100644 --- a/rp2040/src/main.rs +++ b/rp2040/src/main.rs @@ -62,27 +62,6 @@ const XTAL_FREQ_HZ: u32 = 12_000_000u32; // | | 19 | 15 | 17 | | 24 | 20 | 22 | | // | | 18 | | 23 | | // --------------------------------------------------------------- -// Config Layer (holding CONFIG button) -// --------------------------------------------------------------- -// |BOOT L| CAL U| | CONFIG | | - | - | -// --------------------------------------------------------------- -// | | - | - | - | | - | - | - | | -// | | -// | |C M10| | - | | -// | |C M7 | | - | | -// | -/- -/- | -// | | - | | - | | -// | | - | - | - | | - |C OK | - | | -// | | - | | - | | -// --------------------------------------------------------------- -// -pub const CONFIG_BUTTON: usize = 2; -pub const BOOT_BUTTON: usize = 0; -pub const CAL_BUTTON: usize = 1; -pub const CAL_DONE_BUTTON: usize = 20; -pub const CAL_M10_GIMBLE_BUTTON: usize = 8; -pub const CAL_M7_GIMBLE_BUTTON: usize = 9; - pub const BUTTON_FRONT_LEFT_LOWER: usize = 0; pub const BUTTON_FRONT_LEFT_UPPER: usize = 1; pub const BUTTON_FRONT_LEFT_EXTRA: usize = 25; @@ -111,10 +90,15 @@ pub const BUTTON_TOP_RIGHT_HAT_RIGHT: usize = 22; pub const BUTTON_TOP_RIGHT_HAT_DOWN: usize = 23; pub const BUTTON_TOP_RIGHT_HAT_LEFT: usize = 24; +pub const USB_HAT_UP: usize = 33; +pub const USB_HAT_RIGHT: usize = 34; +pub const USB_HAT_DOWN: usize = 35; +pub const USB_HAT_LEFT: usize = 36; + // Special button functions // Throttle hold: -pub const TH_BUTTON: usize = 7; -pub const VT_BUTTON: usize = 12; +pub const TH_BUTTON: usize = BUTTON_TOP_LEFT_MODE; +pub const VT_BUTTON: usize = BUTTON_TOP_RIGHT_MODE; pub const BUTTON_ROWS: usize = 5; pub const BUTTON_COLS: usize = 5; @@ -141,6 +125,8 @@ pub const DEBOUNCE: u8 = 10; pub const RELEASE_RIMEOUT: u16 = 30; // => 300ms +pub const EEPROM_DATA_LENGTH: usize = 25; + // Public types #[derive(Copy, Clone, Default)] pub struct Button { @@ -297,9 +283,10 @@ fn main() -> ! { // Scan matrix to get initial state and check if bootloader should be entered // This is done by holding button 0 pressed while power on the unit for _ in 0..10 { + // Scan 10 times to make sure debounce routine covered all buttons button_matrix.scan_matrix(&mut delay); } - if button_matrix.buttons_pressed()[0] { + if button_matrix.buttons_pressed()[BUTTON_FRONT_LEFT_LOWER] { status_led.update(StatusMode::Bootloader); let gpio_activity_pin_mask: u32 = 0; let disable_interface_mask: u32 = 0; @@ -434,7 +421,7 @@ fn main() -> ! { item.center <<= 8; item.center |= eeprom.read_byte((index as u32 * 6) + 5).unwrap() as u16; } - gimbal_mode = eeprom.read_byte(25).unwrap(); + gimbal_mode = eeprom.read_byte(EEPROM_DATA_LENGTH as u32).unwrap(); loop { // Take care of USB HID poll requests @@ -520,8 +507,24 @@ fn main() -> ! { buttons[BUTTON_TOP_RIGHT_HAT].pressed = false; } - // Secondary way to enter bootloader (pressing all left hands buttons except the hat - if buttons[BOOT_BUTTON].pressed && buttons[CONFIG_BUTTON].pressed { + // Config Layer + // --------------------------------------------------------------- + // |BOOT L| CAL U| | CONFIG | | - | - | + // --------------------------------------------------------------- + // | | - | - | - | | - | - | - | | + // | | + // | |C M10| | - | | + // | |C M7 | | - | | + // | -/- -/- | + // | | - | | - | | + // | | - | - | - | | - |C OK | - | | + // | | - | | - | | + // --------------------------------------------------------------- + // Secondary way to enter bootloader + if buttons[BUTTON_FRONT_LEFT_LOWER].pressed + && buttons[BUTTON_TOP_LEFT_MODE].pressed + && buttons[BUTTON_TOP_RIGHT_MODE].pressed + { status_led.update(StatusMode::Bootloader); let gpio_activity_pin_mask: u32 = 0; let disable_interface_mask: u32 = 0; @@ -531,9 +534,11 @@ fn main() -> ! { ); } - // Calibration of center position (pressing all right hands buttons except - // the hat switch) - if buttons[CAL_BUTTON].pressed && buttons[CONFIG_BUTTON].pressed { + // Calibration of center position + if buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && buttons[BUTTON_TOP_LEFT_MODE].pressed + && buttons[BUTTON_TOP_RIGHT_MODE].pressed + { for (index, item) in axis.iter_mut().enumerate() { item.center = smoother[index].value() as u16; item.min = item.center; @@ -553,14 +558,16 @@ fn main() -> ! { } } - if calibration_active && buttons[CAL_M10_GIMBLE_BUTTON].pressed { + // Calibration set M10 gimbal mode + if calibration_active && buttons[BUTTON_TOP_LEFT_UP].pressed { gimbal_mode = GIMBAL_MODE_M10; for (index, item) in axis.iter_mut().enumerate() { item.center = smoother[index].value() as u16; item.min = item.center; item.max = item.center; } - } else if calibration_active && buttons[CAL_M7_GIMBLE_BUTTON].pressed { + // Calibration set M7 gimbal mode + } else if calibration_active && buttons[BUTTON_TOP_LEFT_DOWN].pressed { gimbal_mode = GIMBAL_MODE_M7; for (index, item) in axis.iter_mut().enumerate() { item.center = smoother[index].value() as u16; @@ -569,8 +576,8 @@ fn main() -> ! { } } // Save calibration data to eeprom (pressing right hat switch) - else if calibration_active && buttons[CAL_DONE_BUTTON].pressed { - let mut eeprom_data: [u8; 25] = [0; 25]; + else if calibration_active && buttons[BUTTON_TOP_RIGHT_HAT].pressed { + let mut eeprom_data: [u8; EEPROM_DATA_LENGTH] = [0; EEPROM_DATA_LENGTH]; for (index, item) in axis.iter_mut().enumerate() { eeprom_data[index * 6] = item.min as u8; eeprom_data[(index * 6) + 1] = (item.min >> 8) as u8; @@ -579,12 +586,12 @@ fn main() -> ! { eeprom_data[(index * 6) + 4] = item.center as u8; eeprom_data[(index * 6) + 5] = (item.center >> 8) as u8; } - eeprom_data[24] = gimbal_mode; + eeprom_data[EEPROM_DATA_LENGTH - 1] = gimbal_mode; let _ = eeprom.write_page(0x01, &eeprom_data); calibration_active = false; } - // // ON/OFF switch for Throttle hold mode + // ON/OFF switch for Throttle hold mode throttle_hold_enable = axis[GIMBAL_AXIS_LEFT_Y].hold != AXIS_CENTER; // Process axis values @@ -632,7 +639,7 @@ fn main() -> ! { } // Update Virtual RY - let virtual_step: u16 = 2; + let virtual_step: u16 = 5; // Compensate value when changing direction if buttons[BUTTON_FRONT_LEFT_UPPER].pressed && !buttons[BUTTON_FRONT_LEFT_LOWER].pressed @@ -712,7 +719,7 @@ fn main() -> ! { usb_activity = true; } - // Generate led activity when gimbal is moved from idle position + // Indicate activity when gimbal is moved from idle position for item in axis.iter_mut() { if item.value != item.previous_value { usb_activity = true; @@ -720,7 +727,7 @@ fn main() -> ! { item.previous_value = item.value; } - // Generate led activity when a button is pressed + // Indicate activity when a button is pressed for (index, key) in buttons.iter_mut().enumerate() { update_button_press_type(key, (timer.get_counter().ticks() / 1000) as u32); @@ -910,7 +917,7 @@ fn get_joystick_report( let mut slider: i16 = axis_12bit_to_i16(ADC_MIN); let mut hat: u8 = 8; // Hat center position - // Virtual axix control. Disables z and rx axis and using girght gimbal Y axis to control + // Virtual axix control. Disables z and rx axis and using right gimbal Y axis to control // slider axis. Values from center stick to max or min will be recalculated to min to max. if *vt_enable { if axis[GIMBAL_AXIS_RIGHT_X].value >= AXIS_CENTER { @@ -942,8 +949,8 @@ fn get_joystick_report( if key.enable_long_press { if key.active_usb_button != 0 { // Check if key is assigned as hat switch - if key.active_usb_button >= 33 && key.active_usb_button <= 36 { - hat = (key.active_usb_button as u8 - 33) * 2; + if key.active_usb_button >= USB_HAT_UP && key.active_usb_button <= USB_HAT_LEFT { + hat = (key.active_usb_button as u8 - USB_HAT_UP as u8) * 2; } else { buttons |= 1 << (key.active_usb_button - 1); } @@ -951,8 +958,8 @@ fn get_joystick_report( } else { if key.pressed && key.usb_button != 0 { // Check if key is assigned as hat switch - if key.usb_button >= 33 && key.usb_button <= 36 { - hat = (key.usb_button as u8 - 33) * 2; + if key.usb_button >= USB_HAT_UP && key.usb_button <= USB_HAT_LEFT { + hat = (key.usb_button as u8 - USB_HAT_UP as u8) * 2; } else { buttons |= 1 << (key.usb_button - 1); }