From 13b25fef6fe92cf7bf2fbb8ef497898134ef66d6 Mon Sep 17 00:00:00 2001 From: Jonathan Haylett Date: Sun, 21 May 2023 00:28:40 +0100 Subject: [PATCH] Fix various issues with NintendoSwitchBackend NintendoSwitchBackend had several issues. Firstly, I messed up the descriptor, which was why this comms backend didn't work on PC. Secondly, because the Nintendo Switch apparently applies a deadzone and scaling to GameCube controller analog stick inputs, it was necessary to reverse engineer this scaling and replicate it in NintendoSwitchBackend so that we scale the inputs before sending them to the console, in the same way that the Switch would scale them after receiving them. Until now I have been using a naive multiplication which was not close enough to the actual formula to prevent input issues manifesting when using the Ultimate mode. Thanks to Zeronia, we have much more data now and I have been able to reverse engineer the scaling formula to get one that matches almost exactly. The only remaining considerations are that the GameCube controller C-Stick is apparently scaled differently to the left analog stick, and that I have only implemented a basic square deadzone rather than a circular deadzone. It is yet to be seen whether these things will cause any input inconsistencies in existing controller modes. --- .github/workflows/build-device-config.yml | 2 +- .../include/comms/NintendoSwitchBackend.hpp | 2 +- HAL/pico/src/comms/NintendoSwitchBackend.cpp | 14 +++++---- include/util/analog_filters.hpp | 9 ++++++ lib/TUCompositeHID/include/TUCompositeHID.hpp | 2 +- lib/TUCompositeHID/include/TUGamepad.hpp | 2 +- lib/TUCompositeHID/include/TUKeyboard.hpp | 2 +- lib/TUCompositeHID/src/TUCompositeHID.cpp | 2 +- lib/TUCompositeHID/src/TUGamepad.cpp | 2 +- lib/TUCompositeHID/src/TUKeyboard.cpp | 4 ++- src/util/analog_filters.cpp | 29 +++++++++++++++++++ 11 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 include/util/analog_filters.hpp create mode 100644 src/util/analog_filters.cpp diff --git a/.github/workflows/build-device-config.yml b/.github/workflows/build-device-config.yml index 94afa079..541be7b5 100644 --- a/.github/workflows/build-device-config.yml +++ b/.github/workflows/build-device-config.yml @@ -34,7 +34,7 @@ jobs: BIN_EXT: ${{ matrix.bin_ext }} strategy: fail-fast: false - matrix: + matrix: include: ${{ fromJson(needs.metadata.outputs.meta_json).build }} steps: diff --git a/HAL/pico/include/comms/NintendoSwitchBackend.hpp b/HAL/pico/include/comms/NintendoSwitchBackend.hpp index 0d16ac39..1f5c76f0 100644 --- a/HAL/pico/include/comms/NintendoSwitchBackend.hpp +++ b/HAL/pico/include/comms/NintendoSwitchBackend.hpp @@ -55,7 +55,7 @@ class NintendoSwitchBackend : public CommunicationBackend { protected: static const uint8_t _report_id = 0; - static uint8_t _descriptor[]; + static const uint8_t _descriptor[]; switch_gamepad_report_t _report; diff --git a/HAL/pico/src/comms/NintendoSwitchBackend.cpp b/HAL/pico/src/comms/NintendoSwitchBackend.cpp index c4aab389..d480424d 100644 --- a/HAL/pico/src/comms/NintendoSwitchBackend.cpp +++ b/HAL/pico/src/comms/NintendoSwitchBackend.cpp @@ -2,6 +2,7 @@ #include "core/CommunicationBackend.hpp" #include "core/state.hpp" +#include "util/analog_filters.hpp" #include #include @@ -61,7 +62,10 @@ // clang-format on -uint8_t NintendoSwitchBackend::_descriptor[] = { HID_REPORT_DESC() }; +const uint8_t NintendoSwitchBackend::_descriptor[] = { HID_REPORT_DESC() }; + +const uint8_t DEADZONE = 11; +const int RADIUS = 256; NintendoSwitchBackend::NintendoSwitchBackend( InputState &inputs, @@ -139,10 +143,10 @@ void NintendoSwitchBackend::SendReport() { _report.home = _outputs.home; // Analog outputs - _report.lx = (_outputs.leftStickX - 128) * 1.25 + 128; - _report.ly = 255 - ((_outputs.leftStickY - 128) * 1.25 + 128); - _report.rx = (_outputs.rightStickX - 128) * 1.25 + 128; - _report.ry = 255 - ((_outputs.rightStickY - 128) * 1.25 + 128); + _report.lx = apply_radius(apply_deadzone(_outputs.leftStickX, DEADZONE, true), RADIUS); + _report.ly = 255 - apply_radius(apply_deadzone(_outputs.leftStickY, DEADZONE, true), RADIUS); + _report.rx = apply_radius(apply_deadzone(_outputs.rightStickX, DEADZONE, true), RADIUS); + _report.ry = 255 - apply_radius(apply_deadzone(_outputs.rightStickY, DEADZONE, true), RADIUS); // D-pad Hat Switch _report.hat = diff --git a/include/util/analog_filters.hpp b/include/util/analog_filters.hpp new file mode 100644 index 00000000..55420a0e --- /dev/null +++ b/include/util/analog_filters.hpp @@ -0,0 +1,9 @@ +#ifndef _UTIL_ANALOG_FILTERS_HPP +#define _UTIL_ANALOG_FILTERS_HPP + +#include "stdlib.hpp" + +uint8_t apply_deadzone(uint8_t value, uint8_t deadzone, bool scale); +uint8_t apply_radius(uint8_t value, int radius); + +#endif \ No newline at end of file diff --git a/lib/TUCompositeHID/include/TUCompositeHID.hpp b/lib/TUCompositeHID/include/TUCompositeHID.hpp index dce350df..e644e48b 100644 --- a/lib/TUCompositeHID/include/TUCompositeHID.hpp +++ b/lib/TUCompositeHID/include/TUCompositeHID.hpp @@ -7,7 +7,7 @@ namespace TUCompositeHID { extern Adafruit_USBD_HID _usb_hid; - bool addDescriptor(uint8_t *descriptor, size_t descriptor_len); + bool addDescriptor(const uint8_t *descriptor, size_t descriptor_len); } #endif \ No newline at end of file diff --git a/lib/TUCompositeHID/include/TUGamepad.hpp b/lib/TUCompositeHID/include/TUGamepad.hpp index 2e4c33a2..447cd689 100644 --- a/lib/TUCompositeHID/include/TUGamepad.hpp +++ b/lib/TUCompositeHID/include/TUGamepad.hpp @@ -67,7 +67,7 @@ class TUGamepad { protected: static const uint8_t _report_id = 1; - static uint8_t _descriptor[]; + static const uint8_t _descriptor[]; gamepad_report_t _report; diff --git a/lib/TUCompositeHID/include/TUKeyboard.hpp b/lib/TUCompositeHID/include/TUKeyboard.hpp index 7e430586..c4b9fc5f 100644 --- a/lib/TUCompositeHID/include/TUKeyboard.hpp +++ b/lib/TUCompositeHID/include/TUKeyboard.hpp @@ -20,7 +20,7 @@ class TUKeyboard { private: static const uint8_t _report_id = 2; - static uint8_t _descriptor[]; + static const uint8_t _descriptor[]; hid_keyboard_report_t _report; }; diff --git a/lib/TUCompositeHID/src/TUCompositeHID.cpp b/lib/TUCompositeHID/src/TUCompositeHID.cpp index acaa6b8c..e3fac0e5 100644 --- a/lib/TUCompositeHID/src/TUCompositeHID.cpp +++ b/lib/TUCompositeHID/src/TUCompositeHID.cpp @@ -16,7 +16,7 @@ namespace TUCompositeHID { false ); - bool addDescriptor(uint8_t *descriptor, size_t descriptor_len) { + bool addDescriptor(const uint8_t *descriptor, size_t descriptor_len) { if (_current_descriptor_len + descriptor_len > HID_DESCRIPTOR_BUFSIZE) { return false; } diff --git a/lib/TUCompositeHID/src/TUGamepad.cpp b/lib/TUCompositeHID/src/TUGamepad.cpp index aaefa094..f8b05af9 100644 --- a/lib/TUCompositeHID/src/TUGamepad.cpp +++ b/lib/TUCompositeHID/src/TUGamepad.cpp @@ -71,7 +71,7 @@ SOFTWARE. // clang-format on -uint8_t TUGamepad::_descriptor[] = { HID_REPORT_DESC(HID_REPORT_ID(_report_id)) }; +const uint8_t TUGamepad::_descriptor[] = { HID_REPORT_DESC(HID_REPORT_ID(_report_id)) }; TUGamepad::TUGamepad() {} diff --git a/lib/TUCompositeHID/src/TUKeyboard.cpp b/lib/TUCompositeHID/src/TUKeyboard.cpp index d3ac6cbc..b93887db 100644 --- a/lib/TUCompositeHID/src/TUKeyboard.cpp +++ b/lib/TUCompositeHID/src/TUKeyboard.cpp @@ -3,7 +3,9 @@ #include #include -uint8_t TUKeyboard::_descriptor[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(_report_id)) }; +// clang-format off +const uint8_t TUKeyboard::_descriptor[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(_report_id)) }; +// clang-format on #define MODIFIER_MASK(mod_kc) (1 << (mod_kc & 0x0F)) diff --git a/src/util/analog_filters.cpp b/src/util/analog_filters.cpp new file mode 100644 index 00000000..74e619df --- /dev/null +++ b/src/util/analog_filters.cpp @@ -0,0 +1,29 @@ +#include "util/analog_filters.hpp" + +#include + +#define SIGNUM(x) ((x > 0) - (x < 0)) + +uint8_t apply_deadzone(uint8_t value, uint8_t deadzone, bool scale) { + int8_t value_signed = value - 128; + if (abs(value_signed) > deadzone) { + // If outside deadzone, must subtract deadzone from result so that axis values start from 1 + // instead of having lower values cut off. + int8_t post_deadzone = value_signed - deadzone * SIGNUM(value_signed); + // If a radius value is passed in, scale up the values linearly so that the same effective + // value is given on the rim. + if (scale) { + int8_t sign = SIGNUM(post_deadzone); + int8_t post_scaling = min(127, abs(post_deadzone) * 128.0 / (128 - deadzone)) * sign; + return post_scaling + 128; + } + return post_deadzone + 128; + } + return 128; +} + +uint8_t apply_radius(uint8_t value, int radius) { + int8_t value_signed = value - 128; + int8_t sign = SIGNUM(value_signed); + return min(127, (int)(abs(value_signed) * radius / 128.0)) * sign + 128; +} \ No newline at end of file