Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 9 additions & 2 deletions Firmware/RP2040/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -519,4 +526,4 @@ endif()

set_target_properties(${FW_NAME} PROPERTIES OUTPUT_NAME ${EXE_FILENAME})

pico_add_extra_outputs(${FW_NAME})
pico_add_extra_outputs(${FW_NAME})
10 changes: 8 additions & 2 deletions Firmware/RP2040/src/Board/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"

Expand Down Expand Up @@ -136,4 +142,4 @@
}
#endif // defined(PIO_USB_DP_PIN)

#endif // _BOARD_CONFIG_H_
#endif // _BOARD_CONFIG_H_
4 changes: 2 additions & 2 deletions Firmware/RP2040/src/OGXMini/Board/Standard.cpp
Original file line number Diff line number Diff line change
@@ -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 <pico/multicore.h>
#include <pico/time.h>
Expand Down Expand Up @@ -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
#endif // OGXM_BOARD == PI_PICO || OGXM_BOARD == RP2040_ZERO || OGXM_BOARD == ADAFRUIT_FEATHER
7 changes: 6 additions & 1 deletion Firmware/RP2040/src/OGXMini/OGXMini.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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] = {
Expand All @@ -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] = {
Expand All @@ -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] = {
Expand All @@ -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] = {
Expand All @@ -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() {
Expand Down Expand Up @@ -100,4 +105,4 @@ namespace OGXMini {
}
}

} // namespace OGXMini
} // namespace OGXMini
76 changes: 75 additions & 1 deletion Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand All @@ -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:
Expand Down Expand 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "USBDevice/DeviceDriver/DeviceDriver.h"
#include "Descriptors/XboxOG.h"
#include <pico/time.h>

class XboxOGDevice : public DeviceDriver
{
Expand All @@ -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_
9 changes: 8 additions & 1 deletion Firmware/RP2040/src/USBHost/HostDriver/XInput/Xbox360.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
#include <cstring>

#include "host/usbh.h"
#include "TaskQueue/TaskQueue.h"

#include "USBHost/HostDriver/XInput/tuh_xinput/tuh_xinput.h"
#include "USBHost/HostDriver/XInput/Xbox360.h"

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);
}

Expand Down Expand Up @@ -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);
}
}
3 changes: 2 additions & 1 deletion Firmware/RP2040/src/USBHost/HostDriver/XInput/Xbox360.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Xbox360Host : public HostDriver

private:
XInput::InReport prev_in_report_;
uint32_t tid_keepalive_{0};
};

#endif // _XBOX360_WIRED_HOST_H_
#endif // _XBOX360_WIRED_HOST_H_
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down Expand Up @@ -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```
Expand Down