Compare commits
4 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae65251cb4 | |||
|
b04a6d29ca |
|||
|
e41a1746ba |
|||
|
22cfe62c41 |
3 changed files with 39 additions and 14 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 Joey Yakimowich-Payne
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -15,7 +15,7 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a
|
|||
5. Connect the Pico to the Switch (dock USB-A or USB-C OTG); the Switch should see it as a wired Pro Controller.
|
||||
|
||||
## Planned features
|
||||
- IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch).
|
||||
- IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch) (WIP branch on `gyrov3`).
|
||||
|
||||
## Limitations
|
||||
- No motion controls/IMU passthrough yet (planned).
|
||||
|
|
@ -228,6 +228,12 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client:
|
|||
### Linux tips
|
||||
- You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules).
|
||||
|
||||
## References
|
||||
- GP2040-CE (controller firmware ecosystem): https://github.com/OpenStickCommunity/GP2040-CE
|
||||
- nxbt (Switch controller research/tools): https://github.com/Brikwerk/nxbt
|
||||
- Nintendo Switch Reverse Engineering notes: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
|
||||
- `hid-nintendo` driver reference: https://github.com/DanielOgorchock/linux/blob/ogorchock/drivers/hid/hid-nintendo.c
|
||||
|
||||
## Troubleshooting
|
||||
- **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, and `Pro Controller Wired Communication` is enabled on the Switch.
|
||||
- **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `switch_pico_bridge.controller_uart_bridge` if needed.
|
||||
|
|
|
|||
|
|
@ -214,6 +214,7 @@ class ControllerContext:
|
|||
last_rumble_energy: float = 0.0
|
||||
rumble_active: bool = False
|
||||
axis_offsets: Dict[int, int] = field(default_factory=dict)
|
||||
swap_abxy: bool = False
|
||||
|
||||
|
||||
def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int]:
|
||||
|
|
@ -395,12 +396,9 @@ def toggle_abxy_for_context(ctx: ControllerContext, config: BridgeConfig, consol
|
|||
if config.swap_abxy_global:
|
||||
console.print("[yellow]Global --swap-abxy is enabled; disable it to use per-controller toggles.[/yellow]")
|
||||
return
|
||||
swapped = ctx.stable_id in config.swap_abxy_ids
|
||||
swapped = ctx.swap_abxy
|
||||
action = "standard" if swapped else "swapped"
|
||||
if swapped:
|
||||
config.swap_abxy_ids.discard(ctx.stable_id)
|
||||
else:
|
||||
config.swap_abxy_ids.add(ctx.stable_id)
|
||||
ctx.swap_abxy = not swapped
|
||||
console.print(
|
||||
f"[cyan]Controller {ctx.controller_index} ({controller_display_name(ctx)}, inst {ctx.instance_id}) now using {action} ABXY layout.[/cyan]"
|
||||
)
|
||||
|
|
@ -425,7 +423,7 @@ def prompt_swap_abxy_controller(
|
|||
table.add_column("GUID")
|
||||
table.add_column("Layout", justify="center")
|
||||
for idx, ctx in enumerate(controllers):
|
||||
swapped = config.swap_abxy_global or (ctx.stable_id in config.swap_abxy_ids)
|
||||
swapped = config.swap_abxy_global or ctx.swap_abxy
|
||||
state = "Swapped" if swapped else "Standard"
|
||||
if config.swap_abxy_global:
|
||||
state += " (global)"
|
||||
|
|
@ -988,8 +986,7 @@ def open_initial_contexts(
|
|||
console.print(f"[red]Failed to open controller {index}: {exc}[/red]")
|
||||
continue
|
||||
stable_id = guid
|
||||
if index in config.swap_abxy_indices:
|
||||
config.swap_abxy_ids.add(stable_id)
|
||||
should_swap = index in config.swap_abxy_indices or stable_id in config.swap_abxy_ids
|
||||
uart = open_uart_or_warn(port, args.baud, console) if port else None
|
||||
if uart:
|
||||
uarts.append(uart)
|
||||
|
|
@ -1007,6 +1004,7 @@ def open_initial_contexts(
|
|||
stable_id=stable_id,
|
||||
port=port,
|
||||
uart=uart,
|
||||
swap_abxy=should_swap,
|
||||
)
|
||||
if config.zero_sticks:
|
||||
zero_context_sticks(ctx, console)
|
||||
|
|
@ -1058,7 +1056,7 @@ def handle_button_event(
|
|||
return
|
||||
current_button_map = (
|
||||
config.button_map_swapped
|
||||
if (config.swap_abxy_global or ctx.stable_id in config.swap_abxy_ids)
|
||||
if (config.swap_abxy_global or ctx.swap_abxy)
|
||||
else config.button_map_default
|
||||
)
|
||||
button = event.cbutton.button
|
||||
|
|
@ -1103,9 +1101,8 @@ def handle_device_added(
|
|||
console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]")
|
||||
return
|
||||
stable_id = guid
|
||||
# Promote any index-based swap flags to stable IDs on first sight.
|
||||
if idx in config.swap_abxy_indices:
|
||||
config.swap_abxy_ids.add(stable_id)
|
||||
# Determine if this controller should have swapped ABXY based on index or GUID
|
||||
should_swap = idx in config.swap_abxy_indices or stable_id in config.swap_abxy_ids
|
||||
uart = open_uart_or_warn(port, args.baud, console) if port else None
|
||||
if uart:
|
||||
uarts.append(uart)
|
||||
|
|
@ -1123,6 +1120,7 @@ def handle_device_added(
|
|||
stable_id=stable_id,
|
||||
port=port,
|
||||
uart=uart,
|
||||
swap_abxy=should_swap,
|
||||
)
|
||||
if config.zero_sticks:
|
||||
zero_context_sticks(ctx, console)
|
||||
|
|
@ -1163,7 +1161,7 @@ def service_contexts(
|
|||
for ctx in list(contexts.values()):
|
||||
current_button_map = (
|
||||
config.button_map_swapped
|
||||
if (config.swap_abxy_global or ctx.stable_id in config.swap_abxy_ids)
|
||||
if (config.swap_abxy_global or ctx.swap_abxy)
|
||||
else config.button_map_default
|
||||
)
|
||||
poll_controller_buttons(ctx, current_button_map)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue