Native Linux control for the NUX Mighty Lite BT MkII (NGA-3BT) guitar amp
over USB-MIDI SysEx — no Wine, no Bluetooth, no vendor app.
The official Mighty app is mobile-only and talks BLE. The amp also has a USB
port that shows up as a class-compliant USB-MIDI device (1fc9:8260, NXP), so
everything the app does can be done from a shell with ALSA's amidi. This repo
is the result of reverse-engineering that path and verifying it against real
hardware.
Python 3 and amidi (package alsa-utils on Arch/Debian). No pip installs.
$ aplay -l # nothing needed from PipeWire/JACK — raw ALSA MIDI
$ python nuxctl.py ports
NUX amp on hw:2,0,0| Command | What it does |
|---|---|
ports |
Find the amp's raw-MIDI port |
dump [slot] |
Decode and print presets (all 7 slots, or one) |
backup |
Dump all 7 presets to presets-backup.json |
select <1-7> |
Switch the active preset slot |
apply <song.json> <tone> |
Push a tone spec into the live buffer (audition only) |
commit <1-7> |
Write the live buffer to flash — permanent, no undo |
tuner [seconds] |
Run the amp's tuner with terminal readout |
apply / commit are deliberately two steps: a tone is always auditioned
before it becomes permanent, because the amp has no per-slot factory
restore. (For exactly that reason, presets-factory-20260729.json is a dump
of the factory presets — the restore point NUX doesn't give you.)
$ python nuxctl.py apply songs/deftones-change.json verse_dark
Applied 'verse_dark' from Deftones - Change (In the House of Flies)
-> LIVE buffer only. Nothing written to flash.
-> Switch slots and back to discard it.
songs/ holds annotated tone recipes (Deftones ×4, Nirvana). They're not just
parameter dumps — each tone documents why every setting is what it is
(amp-model choice per era, when the chorus effect is identity vs. atmosphere,
tuning notes across a setlist so nothing needs a retune between songs):
"verse_dark": {
"_comment": "Treble at 40 is the point - the darkest clean in any of the song files. ...",
"amp": 2,
"amp_params": [32, 60, 52, 46, 40],
"_amp_params_are": "Deluxe Rvb: Gain, Master, Bass, Middle, Treble",
...
}Derived from tuntorius/mightier_amp (the excellent open-source replacement for the Mighty app) and verified against hardware. Messages are plain MIDI SysEx:
| Constant | Value | Meaning |
|---|---|---|
| header | F0 43 58 |
SysEx start + C X (Cherub/NUX vendor bytes) |
| direction | 00 / 01 / 02 |
GET / SET / REQ |
MSG_PRESET |
0x0B |
Read/write a preset slot |
MSG_CURPRESET |
0x0C |
Current-slot operations |
MSG_SPEC_CMD |
0x75 |
Special commands (e.g. 48 = save live buffer) |
MSG_TUNER |
0x6F |
Tuner stream on/off |
Hard-won gotchas, so you don't lose the hours I did:
- USB is not BLE. The mobile app wraps every message in BLE-MIDI packet
framing —
80 80beforeF0,80beforeF7. Over USB-MIDI those bytes are not part of the message; ALSA handles packetisation. Send them raw and the amp silently ignores you. Emit bareF0 .. F7. - Saves that lie. The save command lives on
0x75(MSG_SPEC_CMD), not0x15(kSYX_CURSTATE). The amp ignores0x15without erroring, so saves appear to succeed and nothing is written. - Live buffer vs flash. Edits are CCs into a volatile live buffer; flash is only touched by the explicit save command. That's what makes the audition-then-commit workflow possible (and safe).
- Tuner modes lie about non-standard tunings.
guitarStandardsnaps readings to E standard and will misreport a Drop C# low string — chromatic is the only trustworthy mode if you don't play in standard. Thetunercommand defaults to a Drop C# target; editDROP_CSHARPfor yours.
"It doesn't sound the same" as a measurement instead of an argument: records the amp (or takes two WAV files) and compares octave-band energy balance, normalised for overall level, against a reference recording.
python compare-tone.py record [seconds] # capture from the amp, then compare
python compare-tone.py compare A.wav B.wav # compare two existing filesMIT. Protocol knowledge builds on tuntorius/mightier_amp — if you want full-featured control of a Mighty-series amp on mobile, use that project.