Open-source Linux configurator for the 8BitDo Ultimate 2 Wireless controller (CLI + GUI): remapping, sticks, triggers, gyro, LED rings, profiles https://miguelsanzbr.github.io/8bitdo-ultimate2-linux/
  • Python 64.7%
  • TypeScript 29.5%
  • CSS 4.4%
  • Shell 1.1%
  • JavaScript 0.2%
Find a file
Miguel Sanz Boragan 03957a0c16
Merge pull request #19 from MiguelSanzBr/packaging/pkgbuild-0.1.0-beta.8
Packaging: PKGBUILD for 0.1.0-beta.8
2026-10-22 17:09:55 -03:00
.github/workflows Bring the web configurator into the repo (web/) 2026-10-20 23:09:17 -03:00
docs Never re-knock a 2dc8:3106 pad that did not switch to the 2.4G config mode 2026-10-22 16:26:16 -03:00
packaging Packaging: PKGBUILD for 0.1.0-beta.8 2026-10-22 17:05:47 -03:00
src/ultimate2 Release 0.1.0-beta.8 2026-10-22 16:57:33 -03:00
tests Merge #17 (Ultimate 2.4G) into the jscal remap 2026-10-22 16:38:00 -03:00
tools GUI: drawing traced from the controller's real silhouette 2026-10-12 21:46:27 -03:00
udev Support the 8BitDo Ultimate 2.4G as a Model 2026-10-21 19:15:12 -03:00
web Bring the web configurator into the repo (web/) 2026-10-20 23:09:17 -03:00
.coveragerc Initial public beta: Linux configurator for the 8BitDo Ultimate 2 Wireless 2026-10-12 02:37:58 -03:00
.gitignore Flatpak: manifest for the bundle attached to the releases 2026-10-15 17:08:44 -03:00
CHANGELOG.md Release 0.1.0-beta.8 2026-10-22 16:57:33 -03:00
CONTRIBUTING.md Initial public beta: Linux configurator for the 8BitDo Ultimate 2 Wireless 2026-10-12 02:37:58 -03:00
LICENSE Initial public beta: Linux configurator for the 8BitDo Ultimate 2 Wireless 2026-10-12 02:37:58 -03:00
pyproject.toml Release 0.1.0-beta.8 2026-10-22 16:57:33 -03:00
README.md Packaging: PKGBUILD for 0.1.0-beta.8 2026-10-22 17:05:47 -03:00
u2ctl.py Initial public beta: Linux configurator for the 8BitDo Ultimate 2 Wireless 2026-10-12 02:37:58 -03:00
u2gui.py Initial public beta: Linux configurator for the 8BitDo Ultimate 2 Wireless 2026-10-12 02:37:58 -03:00

ultimate2 — 8BitDo Ultimate 2 Wireless configurator for Linux

Open-source replacement for the configuration part of 8BitDo Ultimate Software V2, which only exists for Windows/macOS. It talks to the controller directly over /dev/hidraw. You get a command-line tool, u2ctl, and a graphical interface, u2gui, styled after the official app.

Português: veja o resumo em português.

u2gui home screen: live controller view with callouts, LED rings and section tiles

Button remapping LED rings and colour wheel
Buttons page LEDs page

Not affiliated with 8BitDo. "8BitDo" and "Ultimate 2" are trademarks of their owner. The protocol was reverse-engineered for interoperability; no code or assets from the official software are included.

Features

u2ctl u2gui
Read firmware version, active profile, controller edition ✓ ✓
3 custom profiles: create, rename, enable/disable ✓ ✓
Button remapping (incl. L4/R4 and the PL/PR back buttons) ✓ ✓
Stick dead zones/range, invert axes, swap sticks/d-pad ✓ ✓
Trigger ranges, swap triggers, trigger rumble ✓ ✓
Rumble strength, gyro (6-axis) mapping ✓ ✓
LED rings: active theme (dynamic / hotwheel / custom / off), colours, speed, per-LED colours ✓ ✓
Backup / restore of the whole configuration ✓ ✓
Live view of the buttons you press, sticks and triggers ✓
Auto-save (changes are written ~0.4 s later and verified) ✓
Interface in English or Brazilian Portuguese (system language; changeable in u2gui) ✓ ✓

Tested with an Ultimate 2 Wireless (firmware 1.10) over USB (2dc8:310b). The 2.4G dongle (2dc8:6013) speaks the same protocol once the controller is paired, but this has not been tested yet.

1st-gen Ultimate Bluetooth Controller (the one with the charging dock and Nintendo layout, firmware 1.02): with the pad on a USB cable straight to the PC (not through the dock). u2ctl switches it to its config mode and back on every command; u2gui keeps it in config mode while the window is open (it is not an XInput gamepad meanwhile) and gives it back when you close it. Button remapping incl. P1/P2, sticks, triggers, rumble, live view; no LEDs, no gyro. Now and then the pad turns off while switching modes: press Home and it is picked up again. Details in docs/ULTIMATE_BT.md.

Ultimate 2.4G Wireless Controller (Xbox layout, X / D switch on the back; firmware 2.04, receiver 1.10): over a USB cable or its 2.4G receiver. It keeps one configuration per mode: switch on D for the cable (and Bluetooth, which has no configuration channel of its own), X for the receiver. On X, u2ctl and u2gui switch it to its config mode the way the Windows app does and back afterwards (the udev rule gives access to its USB node for that). Button remapping incl. P1/P2, sticks, triggers, rumble, live view; no LEDs, no gyro, no trigger rumble. On X with no game open, its receiver switches the controller off after a profile change unless something reads the pad: u2ctl keepalive does (the .deb runs it as the ultimate2-keepalive user service; with the Arch package, enable it with systemctl --user enable --now ultimate2-keepalive). Over Bluetooth, u2ctl battery and the u2gui badge show its battery, and u2gui shows read-only the D configuration it kept from the last cable read. Details in docs/ULTIMATE_24G.md.

Web version

The same configurator also runs in the browser, with nothing to install: https://miguelsanzbr.github.io/8bitdo-ultimate2-linux/ (Chrome, Edge or another Chromium browser, because it talks to the device through WebHID). From the terminal, u2gui web opens it. Its source is in web/ (React + TypeScript); its tests run in .github/workflows/web.yml and the deploy in .github/workflows/pages.yml.

Install

Download ultimate2_<version>_all.deb from the Releases page, then:

sudo apt install ./ultimate2_0.1.0-beta5_all.deb

This installs the u2ctl and u2gui commands, the menu entry "8BitDo Ultimate 2" and the udev rule. Unplug and replug the controller afterwards.

Arch Linux (.pkg.tar.zst)

Download 8bitdo-ultimate2-<version>-any.pkg.tar.zst from the Releases page, then:

sudo pacman -U ./8bitdo-ultimate2-0.1.0beta5-1-any.pkg.tar.zst

Or build it yourself from the PKGBUILD (makepkg -si in packaging/aur). Replug the controller afterwards.

AppImage (any distribution)

Download ultimate2-appimage.zip from the Releases page and extract it. It contains the AppImage, install-udev.sh and the udev rule. The first time only, install the rule, because an AppImage cannot do it by itself:

chmod +x install-udev.sh 8BitDo-Ultimate-2-*.AppImage   # a zip download loses the executable bit
./install-udev.sh                                       # asks for your password

Then unplug and replug the controller (or its 2.4G adapter) and start the AppImage. Without the rule the app cannot open the controller. If it refuses to start because FUSE is missing, run it with APPIMAGE_EXTRACT_AND_RUN=1 ./8BitDo-Ultimate-2-*.AppImage.

Flatpak

From the signed repository at https://miguelsanzbr.github.io/flatpak/ (updates arrive with flatpak update):

flatpak remote-add --user --if-not-exists miguelsanz https://miguelsanzbr.github.io/flatpak/miguelsanz.flatpakrepo
flatpak install --user miguelsanz io.github.miguelsanzbr._8bitdo-ultimate2-linux

Each release also has a .flatpak file (flatpak install --user ./file.flatpak). The app is not on Flathub. Inside the sandbox the app reaches the device through its hidraw node, so the udev rule is still installed once on the host, from a clone of this repository or with install-udev.sh from the AppImage zip:

sudo install -m644 udev/70-8bitdo-u2.rules /etc/udev/rules.d/ && sudo udevadm control --reload && sudo udevadm trigger --action=add --subsystem-match=hidraw

The AppStream metadata, desktop entry and icon for Flatpak are in packaging/flatpak/.

From source

Requirements: Python ≥ 3.8 and Tk (python3-tk on Debian/Ubuntu) for the GUI. There are no other dependencies.

git clone https://github.com/MiguelSanzBr/8bitdo-ultimate2-linux.git && cd 8bitdo-ultimate2-linux
sudo cp udev/70-8bitdo-u2.rules /etc/udev/rules.d/ && sudo udevadm control --reload && sudo udevadm trigger --action=add --subsystem-match=hidraw
pip install .            # installs the u2ctl and u2gui commands

You can also skip installing and run ./u2ctl.py and ./u2gui.py straight from the checkout. Build the .deb yourself with ./packaging/build-deb.sh (output in dist/).

Beta. Make a backup first (u2ctl backup orig.bin, or the Backup button) so you can restore your controller's original configuration.

Usage

Graphical interface

u2gui
  • Home: a drawing of your controller in its own colours (the edition is detected). Buttons light up when you press them, and the LED rings preview the current effect. The tiles open each section.
  • Profiles 1–3 are at the top. ● marks the profile the controller is currently using; switch profiles with the controller's profile button. A banner warns you when you edit a profile that is not active.
  • Buttons: click a button on the drawing, then choose its new function.
  • LEDs: pick the active theme, then click what to colour (theme colours, all custom LEDs, or a single LED on the drawing) and use the colour wheel.
  • Profile page: besides the profile settings, it holds the interface language (English or Brazilian Portuguese). The first run follows the system language; picking another one (here or in the selector next to Backup) restarts the window in it.
  • Changes are saved automatically. Backup / Restaurar backup save and restore the whole configuration.

Command line

u2ctl list                       # detected devices
u2ctl info                       # firmware, mode, active slot
u2ctl backup orig.bin            # raw backup (do this first)
u2ctl new-slot 0 --name Meu      # make slot 0 a custom profile with the app's defaults
u2ctl dump -o cfg.json           # configuration as JSON
#  … edit cfg.json …
u2ctl apply cfg.json -n          # show what would change
u2ctl apply cfg.json             # write, save to flash, verify
u2ctl theme hotwheel             # active LED theme: dynamic | hotwheel | custom | off
u2ctl restore orig.bin           # undo everything

apply only writes the fields you changed; every other byte is kept exactly as read from the controller.

JSON fields

There are three slots, slots[0..2]. A slot or section is only used when its flag is "enabled"; on a fresh controller everything reads 0xffffffff (erased flash). u2ctl new-slot fills a slot with sane defaults.

Field Meaning
gamepad_mode 0 Switch, 1 Android, 3 Windows/XInput
slots[n].flag "enabled" turns the slot into a custom profile
name up to 16 characters
mapping.<button> function of a physical button: A B X Y LB RB LT RT L3 R3 Select Start Home Menu Screenshot Up Down Left Right P1..P5 TurboSingle TurboAuto LS_Up.. RS_Right None (P1 = PR and P2 = PL, the back buttons; P3 = L4 and P4 = R4, the small shoulder buttons)
sticks.left_start / left_end (right_*) inner dead zone / outer range, 0–128
triggers.left_start / left_end (right_*) LT/RT activation / full-press point, 0–255
special.features invert_LS_X invert_LS_Y invert_RS_X invert_RS_Y swap_sticks swap_triggers swap_LS_dpad trigger_rumble
rumble.left / right motor strength 0.0–1.0
sixaxis mapping_type 0 off / 1 right stick / 2 left stick; trigger_mode 1 hold / 2 toggle; sensitivity 1–10; dead_compensate 0–100
led_dynamic, led_hotwheel, led_single colours #rrggbb; hotwheel speed 1–15; led_single.colors = 24 LEDs

Troubleshooting

  • permission denied on /dev/hidrawN: install the udev rule (see Install), then unplug and replug the controller. The rule must sort before 73-seat-late.rules, which is why it is called 70-…. If you installed 0.1.0-beta.1 from source, delete the old /etc/udev/rules.d/99-8bitdo-u2.rules.
  • Writes hang or time out: the firmware only answers while the host keeps reading the controller's XInput reports. The tools keep the joystick node (/dev/input/js*) open for this. If it was stuck before, unplug and replug the controller.
  • Colours do not change on the controller: colours belong to a profile. Activate that profile with the controller's profile button; the GUI's ● shows which one is active.

Development

python3 -m venv --system-site-packages .venv && .venv/bin/pip install -e ".[test]"
.venv/bin/python -m coverage run -m pytest && .venv/bin/python -m coverage report   # needs an X display for the GUI tests

The suite uses an in-process emulator of the controller firmware (tests/fake_device.py), so no hardware is needed. Coverage is enforced at 100% (lines and branches).

Further reading:

Not implemented yet

Macro step data, firmware updates, dongle pairing, and confirming which edition numbers map to which colourways (only edition 3, the lilac model, is known).

License

MIT


Português

Configurador open source do 8BitDo Ultimate 2 Wireless para Linux, com linha de comando (u2ctl) e interface gráfica (u2gui) no estilo do app oficial.

As imagens no topo desta página mostram a interface: os botões apertados acendem ao vivo, e há páginas de botões e de LEDs com roda de cores.

  • Instalar: baixe o .deb em Releases e rode sudo apt install ./ultimate2_*_all.deb. Depois reconecte o controle. O app aparece no menu como "8BitDo Ultimate 2". Com o AppImage (qualquer distribuição), rode uma vez o install-udev.sh que vem no zip (veja AppImage acima) e reconecte o controle.
  • Usar: u2gui. As mudanças são salvas sozinhas. As cores ficam por perfil, então ative o perfil no botão de perfil do controle (o ● mostra qual está ativo).
  • Idioma: a interface está disponível em inglês e português do Brasil. Ela segue o idioma do sistema e dá para trocar no seletor ao lado do Backup ou na página Perfil (a janela reinicia no novo idioma).
  • Backup: u2ctl backup arquivo.bin / u2ctl restore arquivo.bin.
  • Sem vínculo com a 8BitDo. O protocolo foi obtido por engenharia reversa para interoperabilidade.