diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 34aa5da1..8df09b2d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,9 +19,8 @@ jobs: - PI_PICOW - PI_PICO2W - RP2040_ZERO + - RP2350_USB_A - ADAFRUIT_FEATHER - - ESP32_BLUEPAD32_I2C - - ESP32_BLUERETRO_I2C #- EXTERNAL_4CH_I2C steps: diff --git a/Firmware/RP2040/CMakeLists.txt b/Firmware/RP2040/CMakeLists.txt index 839577b3..330592ba 100644 --- a/Firmware/RP2040/CMakeLists.txt +++ b/Firmware/RP2040/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(SRC ${CMAKE_CURRENT_LIST_DIR}/src) set(EXTERNAL_DIR ${CMAKE_CURRENT_LIST_DIR}/../external) -set(PICOSDK_VERSION_TAG "2.1.0") +set(PICOSDK_VERSION_TAG "2.1.1") # Default Pico SDK path when not set (e.g. by CMake Tools or command line) if(NOT PICO_SDK_PATH) @@ -183,6 +183,13 @@ elseif(OGXM_BOARD STREQUAL "ESP32_BLUERETRO_I2C") add_compile_definitions(OGXM_RETAIL=1) endif() +elseif(OGXM_BOARD STREQUAL "RP2350_USB_A") + add_compile_definitions(CONFIG_OGXM_BOARD_RP2350_USB_A=1) + set(EN_USB_HOST TRUE) + set(PICO_PLATFORM rp2350) + set(EN_RGB TRUE) + set(FLASH_SIZE_MB 2) + else() message(FATAL_ERROR "Invalid OGXM_BOARD value. See options in src/board_config.h") @@ -519,4 +526,4 @@ endif() set_target_properties(${FW_NAME} PROPERTIES OUTPUT_NAME ${EXE_FILENAME}) -pico_add_extra_outputs(${FW_NAME}) \ No newline at end of file +pico_add_extra_outputs(${FW_NAME}) diff --git a/Firmware/RP2040/src/Board/Config.h b/Firmware/RP2040/src/Board/Config.h index a40ecf60..d73001a0 100644 --- a/Firmware/RP2040/src/Board/Config.h +++ b/Firmware/RP2040/src/Board/Config.h @@ -9,7 +9,8 @@ #define ESP32_BLUERETRO_I2C 5 #define EXTERNAL_4CH_I2C 6 #define INTERNAL_4CH_I2C 7 -#define BOARDS_COUNT 8 +#define RP2350_USB_A 8 +#define BOARDS_COUNT 9 #define SYSCLOCK_KHZ 240000 @@ -99,6 +100,11 @@ #define MAX_GAMEPADS 1 #endif +#elif defined(CONFIG_OGXM_BOARD_RP2350_USB_A) + #define OGXM_BOARD RP2350_USB_A + #define PIO_USB_DP_PIN 12 + #define RGB_PXL_PIN 16 + #else #error "Invalid OGXMini board selected" @@ -136,4 +142,4 @@ } #endif // defined(PIO_USB_DP_PIN) -#endif // _BOARD_CONFIG_H_ \ No newline at end of file +#endif // _BOARD_CONFIG_H_ diff --git a/Firmware/RP2040/src/OGXMini/Board/Standard.cpp b/Firmware/RP2040/src/OGXMini/Board/Standard.cpp index 575b4e0c..979d0a12 100644 --- a/Firmware/RP2040/src/OGXMini/Board/Standard.cpp +++ b/Firmware/RP2040/src/OGXMini/Board/Standard.cpp @@ -1,6 +1,6 @@ #include "Board/Config.h" #include "OGXMini/Board/Standard.h" -#if ((OGXM_BOARD == PI_PICO) || (OGXM_BOARD == RP2040_ZERO) || (OGXM_BOARD == ADAFRUIT_FEATHER)) +#if ((OGXM_BOARD == PI_PICO) || (OGXM_BOARD == RP2040_ZERO) || (OGXM_BOARD == ADAFRUIT_FEATHER) || (OGXM_BOARD == RP2350_USB_A)) #include #include @@ -138,4 +138,4 @@ void standard::run() { // void standard::initialize() {} // void standard::run() {} -#endif // OGXM_BOARD == PI_PICO || OGXM_BOARD == RP2040_ZERO || OGXM_BOARD == ADAFRUIT_FEATHER \ No newline at end of file +#endif // OGXM_BOARD == PI_PICO || OGXM_BOARD == RP2040_ZERO || OGXM_BOARD == ADAFRUIT_FEATHER diff --git a/Firmware/RP2040/src/OGXMini/OGXMini.cpp b/Firmware/RP2040/src/OGXMini/OGXMini.cpp index a754c630..a676e1e5 100644 --- a/Firmware/RP2040/src/OGXMini/OGXMini.cpp +++ b/Firmware/RP2040/src/OGXMini/OGXMini.cpp @@ -22,6 +22,7 @@ namespace OGXMini { esp32_br_i2c::initialize, // ESP32_BLUERETRO_I2C four_ch_i2c::initialize, // EXTERNAL_4CH_I2C four_ch_i2c::initialize, // INTERNAL_4CH_I2C + standard::initialize, // RP2350_USB_A }; static constexpr RunFunc run_func[BOARDS_COUNT] = { @@ -33,6 +34,7 @@ namespace OGXMini { esp32_br_i2c::run, // ESP32_BLUERETRO_I2C four_ch_i2c::run, // EXTERNAL_4CH_I2C four_ch_i2c::run, // INTERNAL_4CH_I2C + standard::run, // RP2350_USB_A }; static constexpr HostMountedFunc host_mount_func[BOARDS_COUNT] = { @@ -44,6 +46,7 @@ namespace OGXMini { nullptr, // ESP32_BLUERETRO_I2C four_ch_i2c::host_mounted, // EXTERNAL_4CH_I2C four_ch_i2c::host_mounted, // INTERNAL_4CH_I2C + standard::host_mounted, // RP2350_USB_A }; static constexpr HostMountedWTypeFunc host_mount_w_type_func[BOARDS_COUNT] = { @@ -55,6 +58,7 @@ namespace OGXMini { nullptr, // ESP32_BLUERETRO_I2C four_ch_i2c::host_mounted_w_type, // EXTERNAL_4CH_I2C four_ch_i2c::host_mounted_w_type, // INTERNAL_4CH_I2C + nullptr, // RP2350_USB_A }; static constexpr WirelessConnectedFunc wl_conn_func[BOARDS_COUNT] = { @@ -66,6 +70,7 @@ namespace OGXMini { nullptr, // ESP32_BLUERETRO_I2C four_ch_i2c::wireless_connected, // EXTERNAL_4CH_I2C four_ch_i2c::wireless_connected, // INTERNAL_4CH_I2C + nullptr, // RP2350_USB_A }; void initialize() { @@ -100,4 +105,4 @@ namespace OGXMini { } } -} // namespace OGXMini \ No newline at end of file +} // namespace OGXMini diff --git a/Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.cpp b/Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.cpp index eeda9c0c..e5671868 100644 --- a/Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.cpp +++ b/Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.cpp @@ -3,6 +3,7 @@ #include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h" #include "USBDevice/DeviceDriver/XboxOG/XboxOG_GP.h" +#include "Board/ogxm_log.h" void XboxOGDevice::initialize() { @@ -20,6 +21,79 @@ void XboxOGDevice::process(const uint8_t idx, Gamepad& gamepad) std::memset(&in_report_.buttons, 0, 8); Gamepad::PadIn gp_in = gamepad.get_pad_in(); + // Check for SYS button long-press + bool sys_pressed = (gp_in.buttons & Gamepad::BUTTON_SYS) != 0; + + if (sys_pressed && !sys_button_pressed_) + { + // SYS button just pressed + sys_button_pressed_ = true; + sys_button_press_time_ = get_absolute_time(); + sys_button_combo_sent_ = false; + } + else if (!sys_pressed && sys_button_pressed_) + { + // SYS button released + sys_button_pressed_ = false; + + // Calculate how long it was held + uint64_t hold_time_ms = to_ms_since_boot(get_absolute_time()) - to_ms_since_boot(sys_button_press_time_); + + if (hold_time_ms >= 3000) + { + // 3s combo: LT + RT + Up + Back - Shutdown + gp_in.trigger_l = gamepad.scale_trigger_l(0xFF); + gp_in.trigger_r = gamepad.scale_trigger_r(0xFF); + gp_in.dpad |= Gamepad::DPAD_UP; + gp_in.buttons |= Gamepad::BUTTON_BACK; + sys_button_combo_sent_ = true; + } + else if (hold_time_ms >= 1000) + { + // 1s combo: LT + RT + Start + SELECT - Soft IGR + gp_in.trigger_l = gamepad.scale_trigger_l(0xFF); + gp_in.trigger_r = gamepad.scale_trigger_r(0xFF); + gp_in.buttons |= Gamepad::BUTTON_START; + gp_in.buttons |= Gamepad::BUTTON_BACK; + sys_button_combo_sent_ = true; + } + else + { + // Under 1s: treat as normal START button + gp_in.buttons |= Gamepad::BUTTON_START; + sys_button_combo_sent_ = true; + } + } + else if (sys_pressed && !sys_button_combo_sent_) + { + // Button still held - check if we should send a combo + uint64_t hold_time_ms = to_ms_since_boot(get_absolute_time()) - to_ms_since_boot(sys_button_press_time_); + + if (hold_time_ms >= 3000) + { + // 3s combo: LT + RT + Up + Back - Shutdown + gp_in.trigger_l = gamepad.scale_trigger_l(0xFF); + gp_in.trigger_r = gamepad.scale_trigger_r(0xFF); + gp_in.dpad |= Gamepad::DPAD_UP; + gp_in.buttons |= Gamepad::BUTTON_BACK; + sys_button_combo_sent_ = true; + } + else if (hold_time_ms >= 1000) + { + // 1s combo: LT + RT + Start + Back - Soft IGR + gp_in.trigger_l = gamepad.scale_trigger_l(0xFF); + gp_in.trigger_r = gamepad.scale_trigger_r(0xFF); + gp_in.buttons |= Gamepad::BUTTON_START; + gp_in.buttons |= Gamepad::BUTTON_BACK; + sys_button_combo_sent_ = true; + } + else + { + // Under 1s: treat as START button + gp_in.buttons |= Gamepad::BUTTON_START; + } + } + switch (gp_in.dpad) { case Gamepad::DPAD_UP: @@ -138,4 +212,4 @@ const uint8_t* XboxOGDevice::get_descriptor_configuration_cb(uint8_t index) const uint8_t* XboxOGDevice::get_descriptor_device_qualifier_cb() { return nullptr; -} \ No newline at end of file +} diff --git a/Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.h b/Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.h index cdd63c3f..832dd285 100644 --- a/Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.h +++ b/Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.h @@ -5,6 +5,7 @@ #include "USBDevice/DeviceDriver/DeviceDriver.h" #include "Descriptors/XboxOG.h" +#include class XboxOGDevice : public DeviceDriver { @@ -23,6 +24,10 @@ class XboxOGDevice : public DeviceDriver private: XboxOG::GP::InReport in_report_; XboxOG::GP::OutReport out_report_; + + bool sys_button_pressed_{false}; + bool sys_button_combo_sent_{false}; + absolute_time_t sys_button_press_time_; }; -#endif // _XBOXGOG_DEVICE_H_ +#endif // _XBOXGOG_DEVICE_H_ \ No newline at end of file diff --git a/Firmware/RP2040/src/USBHost/HostDriver/XInput/Xbox360.cpp b/Firmware/RP2040/src/USBHost/HostDriver/XInput/Xbox360.cpp index 58e75051..266a3489 100644 --- a/Firmware/RP2040/src/USBHost/HostDriver/XInput/Xbox360.cpp +++ b/Firmware/RP2040/src/USBHost/HostDriver/XInput/Xbox360.cpp @@ -1,6 +1,7 @@ #include #include "host/usbh.h" +#include "TaskQueue/TaskQueue.h" #include "USBHost/HostDriver/XInput/tuh_xinput/tuh_xinput.h" #include "USBHost/HostDriver/XInput/Xbox360.h" @@ -8,6 +9,12 @@ void Xbox360Host::initialize(Gamepad& gamepad, uint8_t address, uint8_t instance, const uint8_t* report_desc, uint16_t desc_len) { tuh_xinput::set_led(address, instance, idx_ + 1, true); + // Sets LED every 2 seconds to keep 8BitDo Ultimate 1C controllers alive. May cause LED weirdness or other issues on wired 360 controllers. + tid_keepalive_ = TaskQueue::Core1::get_new_task_id(); + TaskQueue::Core1::queue_delayed_task(tid_keepalive_, 2000, true, [address, instance, this] { + tuh_xinput::set_led(address, instance, idx_ + 1, false); + }); + tuh_xinput::receive_report(address, instance); } @@ -55,4 +62,4 @@ bool Xbox360Host::send_feedback(Gamepad& gamepad, uint8_t address, uint8_t insta { Gamepad::PadOut gp_out = gamepad.get_pad_out(); return tuh_xinput::set_rumble(address, 0, gp_out.rumble_l, gp_out.rumble_r, false); -} \ No newline at end of file +} diff --git a/Firmware/RP2040/src/USBHost/HostDriver/XInput/Xbox360.h b/Firmware/RP2040/src/USBHost/HostDriver/XInput/Xbox360.h index 63511e5d..110e2ef9 100644 --- a/Firmware/RP2040/src/USBHost/HostDriver/XInput/Xbox360.h +++ b/Firmware/RP2040/src/USBHost/HostDriver/XInput/Xbox360.h @@ -18,6 +18,7 @@ class Xbox360Host : public HostDriver private: XInput::InReport prev_in_report_; + uint32_t tid_keepalive_{0}; }; -#endif // _XBOX360_WIRED_HOST_H_ \ No newline at end of file +#endif // _XBOX360_WIRED_HOST_H_ diff --git a/README.md b/README.md index e14adc8f..607ec9ba 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # OGX-Mini 2026 ![OGX-Mini Boards](images/OGX-Mini-github.jpg "OGX-Mini Boards") -Firmware for the RP2040, capable of emulating gamepads for several game consoles. The firmware comes in many flavors, supported on the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723), Pi Pico, Pi Pico 2, Pi Pico W, Pi Pico 2 W, Waveshare RP2040-Zero, Pico/ESP32 hybrid, and a 4-Channel RP2040-Zero setup. +Firmware for the RP2040, capable of emulating gamepads for several game consoles. The firmware comes in many flavors, supported on the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723), Pi Pico, Pi Pico 2, Pi Pico W, Pi Pico 2 W, Waveshare RP2040-Zero, Waveshare/SpotPear RP2350-A, Pico/ESP32 hybrid, and a 4-Channel RP2040-Zero setup. [**Visit the web app here**](https://wiredopposite.github.io/OGX-Mini-WebApp/) to change your mappings and deadzone settings. To pair the OGX-Mini with the web app via USB, plug your controller in, then connect it to your PC, hold **Start + Left Bumper + Right Bumper** to enter web app mode. Click "Connect via USB" in the web app and select the OGX-Mini. You can also pair via Bluetooth, no extra steps are needed in that case. @@ -163,7 +163,8 @@ Build with **CMake** from the `Firmware/RP2040` directory. You can compile for d - ```PI_PICO2``` - ```PI_PICOW``` - ```PI_PICO2W``` -- ```RP2040_ZERO``` +- ```RP2040_ZERO``` +- ```RP2350_USB_A``` - ```ADAFRUIT_FEATHER``` - ```ESP32_BLUEPAD32_I2C``` - ```ESP32_BLUERETRO_I2C```