Wii Guitar MIDI Bridge


A Guitar Hero controller, a Raspberry Pi Zero 2 W, and a short cable turn into a real MIDI instrument. Fret combinations pick chords, the strum bar plays them, the touch bar picks individual strings, and the whammy bends or drives intensity. Four instruments — guitar, bass, synth and drums — each on its own MIDI channel.

The Pi appears to a phone or computer as a class-compliant USB MIDI device, so GarageBand, Loopy, a DAW, or anything else that takes MIDI sees an instrument with no drivers and no configuration.

Latency is roughly 25–35 ms end to end. The Wii Remote’s own Bluetooth reporting is ~10–20 ms of that and is unavoidable; USB MIDI adds about 2 ms.

What you need

Two hardware notes that cost real time to discover. The Zero 2 W has two micro-USB jacks and only the outer one carries data. And a plain Lightning Camera Connection Kit supplies about 100 mA, which browns the Pi out — use a powered CCK, or an iPhone 15+ USB-C port.

Install

On a fresh Raspberry Pi OS or DietPi install:

curl -fsSLO https://jacksonmicro.com/wiiguitar/wiiguitar.tar.gz
tar xzf wiiguitar.tar.gz
cd wiiguitar
less install.sh          # it wants root, so read it first
sudo ./install.sh

It installs packages, enables SPI and USB gadget mode, applies the Bluetooth settings a Wii Remote needs, and sets up three systemd services. Re-running it is safe — every step checks before changing anything, and --dry-run shows exactly what it would do without touching the system.

Then reboot, pair, and plug in:

sudo systemctl start reboot.target   # DietPi masks logind
cd ~/wiiguitar && sudo ./pair.sh     # press 1+2 on the Wii Remote

Download wiiguitar.tar.gz View install.sh SHA256SUMS

How playing works

Hold a combination of the five fret buttons to select a chord; hit the strum bar to play it. That much is the guitar. On top:

Instrument modes

KEY1 on the HAT cycles modes. Switching sends CC123 (all notes off) to the channel you are leaving, so the previous instrument is never left holding notes.

ModeMIDI chFretsWhammyTouch barDefault voice
Guitar1chordbendpluckclean
Bass2notebendnotesfinger
Synth3chordintensitypluckpiano
Drums10drumintensitycymbalskit

Drums sit on channel 10 because that is where General MIDI puts percussion. No program change is ever sent there — one would swap the entire kit.

Configuring the notes

Everything below is plain Python or JSON in the install directory. Edit, restart with sudo systemctl restart wiiguitar, and it takes effect.

Chords — guitar and synth

All 31 fret combinations are mapped in chords.py. Each entry is bitmask: (semitones above the key root, quality, label), with GREEN=1 RED=2 YELLOW=4 BLUE=8 ORANGE=16:

CHORD_MAP = {
    GREEN:        (0,  "maj",   "I"),      # C  in the key of C
    RED:          (7,  "maj",   "V"),      # G
    YELLOW:       (9,  "min",   "vi"),     # Am
    BLUE:         (5,  "maj",   "IV"),     # F
    ORANGE:       (2,  "min",   "ii"),     # Dm
    GREEN | RED:  (4,  "min",   "iii"),    # Em
    ...
}

The four singles run I–V–vi–IV left to right, which is the most common progression in popular music — so you play it as a plain sweep across the neck.

One fret — the workhorse chords

FretsDegreeKey of CKey of G
GICG
RVGD
YviAmEm
BIVFC
OiiDmAm

Two frets — 7ths, colour, the remaining degrees

FretsDegreeKey of CKey of G
G+RiiiEmBm
G+YIsus4Csus4Gsus4
R+YV7G7D7
G+BI7C7G7
R+Bii7Dm7Am7
Y+Bvi7Am7Em7
G+OImaj7Cmaj7Gmaj7
R+OVsus4Gsus4Dsus4
Y+OIVmaj7Fmaj7Cmaj7
B+ObVIIA#F

Three frets — power chords

FretsDegreeKey of CKey of G
G+R+YI5C5G5
G+R+Bvi5A5E5
G+Y+Bii5D5A5
R+Y+BV5G5D5
G+R+ObVII5A#5F5
G+Y+Oiii5E5B5
R+Y+ObVIG#D#
G+B+ObIIID#A#
R+B+OviiBdimF#dim
Y+B+OIV5F5C5

Four frets — suspensions and extensions

FretsDegreeKey of CKey of G
G+R+Y+BIadd9Cadd9Gadd9
G+R+Y+OIVsus2Fsus2Csus2
G+R+B+OVadd9Gadd9Dadd9
G+Y+B+Oiisus4Dsus4Asus4
R+Y+B+Ovi7Am7Em7

All five

FretsDegreeKey of CKey of G
G+R+Y+B+OImaj7*Cmaj7Gmaj7

Chord qualities

Semitone intervals above the root, from QUALITIES. Add your own by putting a new tuple in that dictionary.

NameIntervals
maj0, 4, 7
min0, 3, 7
dom70, 4, 7, 10
min70, 3, 7, 10
maj70, 4, 7, 11
sus40, 5, 7
sus20, 2, 7
power0, 7
dim0, 3, 6
add90, 4, 7, 14

Songs — naming real chords

setlist.json holds a setlist. A song names actual chords per fret, so it reads the way songs are written down rather than in roman numerals:

{"name": "Wonderwall", "key": "Em", "patch": "acoustic",
 "frets": {"G": "Em7", "R": "G", "Y": "Dsus4",
           "B": "A7sus4", "O": "Cadd9"}}

Keys use combination names like "G+R" for two frets. Anything a song does not define falls back to the diatonic chord for its key, so no fret is ever dead. Minor keys correctly borrow their relative major’s chord set — Em gets G major’s chords, not E major’s.

You can also edit chords on the device: KEY2 opens an editor where the joystick picks the fret slot and the root, KEY1 cycles the quality, and KEY2 saves. Chords preview audibly as you dial them, and saves are atomic, so pulling the power mid-write cannot corrupt the setlist.

SongKeyFrets
Key of CCdiatonic default for the key
Key of GGdiatonic default for the key
WonderwallEmEm7  G  Dsus4  A7sus4  Cadd9
Let It BeCC  G  Am  F  Em
ZombieEmEm  C  G  D  Am
Knockin on HeavenGG  D  Am  C  Em
House of Rising SunAmAm  C  D  F  E
Blues in EEE7  A7  B7  E5  A5
Sweet Home AlabamaDD  C  G  Em  Bm

Bass — ten single notes

The five frets are scale degrees 1–5 and the five touch-bar zones continue 6–10, giving just over an octave without leaving the neck. Minor keys use the natural minor scale. The root is placed once for the whole layout so the top notes cannot wrap around and collide with the bottom.

Key Frets — G R Y B OTouch zones 1–5
CC2D2E2F2G2A2B2C3D3E3
AmA1B1C2D2E2F2G2A2B2C3
EmE1F#1G1A1B1C2D2E2F#2G2

Change the scales or the register in modes.py:

MAJOR = (0, 2, 4, 5, 7, 9, 11)
MINOR = (0, 2, 3, 5, 7, 8, 10)
BASS_LOW, BASS_HIGH = 28, 55      # keep inside a bass guitar's range

Drums — General MIDI kit

Frets fire the instant they are pressed; waiting for a strum makes no sense for percussion. The strum bar becomes the kick and open hat instead.

ControlSoundGM note
Strum downKick36
Strum upOpen hat46
GSnare38
RHi-hat42
YHi tom50
BLow tom45
OCrash49
zone 1Ride51
zone 2Tambourine54
zone 3Cowbell56
zone 4Clap39
zone 5Splash55

Remap in modes.py by editing DRUM_FRETS, DRUM_STRUM_DOWN, DRUM_STRUM_UP and DRUM_TOUCH — each is a GM note number and a label.

Voices

Each mode cycles its own instrument list, so a bass line is never played on a guitar patch. Open the setlist menu with the joystick and press KEY1 to cycle the voice for the mode you are in. Numbers are GM programs.

ModeVoices
Guitarclean 27, overdrive 29, distortion 30, acoustic 25, nylon 24, muted 28, jazz 26, piano 0, organ 16, strings 48
Bassfinger 33, pick 34, fretless 35, slap 36, synth bs 38, upright 32
Synthpiano 0, e.piano 4, saw 81, square 80, warm pad 89, organ 16, strings 48, bell 11
Drumskit 0

A song’s patch names a guitar voice, so it applies to guitar mode only. To move a mode’s register, give it an octave: Mode("Synth", ..., octave=1) raises it twelve semitones across chords, plucks and bass notes alike.

Whammy and touch bar

On a World Tour neck these two axes are the opposite way round from what their names suggest. Measured, not assumed:

ControlAxisRange
Whammy barABS_HAT1X0–15, rests at 0
Touch barABS_HAT0X4–31, and 15 means not touched

If your controller differs, run the bundled test rather than guessing — it prompts on the LCD and reports which axis each control actually moved:

sudo systemctl stop wiiguitar
python3 axistest.py
sudo systemctl start wiiguitar

Intensity is floored so releasing the whammy returns to normal, never to silence: velocity runs from your configured value to 127, CC11 expression from 100 to 127, and CC74 brightness from 64 (neutral) to 127. CC11 is deliberately floored — it is MIDI Expression, where 0 mutes the channel outright.

Controls

ControlPlay viewSetlist menuChord editor
Joystick up / downPrevious / next songScrollSelect fret slot
Joystick left / rightTranspose a semitone—Change chord root
Joystick pressOpen setlistPick songCycle quality
KEY1Cycle instrument modeCycle voiceCycle quality
KEY2Open chord editorPick songSave
KEY3Panic — all notes offBackRevert / back

On the guitar itself, + and − transpose while you play.

Things that will bite you

Three Bluetooth problems each independently stop a Wii Remote from working, and none of them give an obvious error. The installer handles all three; they are listed here because they are miserable to rediscover.

Also worth knowing: hid-wiimote exposes five input devices for one controller, and the base “Nintendo Wii Remote” also has five or more buttons. Matching the wrong one gives you a bridge that looks connected and does nothing.

How it fits together

bridge.py     the application: reads the guitar, drives the screen, sends MIDI
modes.py      instrument modes, drum map, bass scales, voices
chords.py     chord table, qualities, voicings
songs.py      setlist loading and per-song fret assignments
ui.py         the 128x128 screen
hat.py        ST7735S display driver and joystick/button input
backends.py   output: raw USB MIDI, a local FluidSynth, or silent
axistest.py   identifies whammy vs touch bar on your controller
pair.sh       Bluetooth pairing, including the fixes above

The bridge idles at about 0.3% CPU on a Zero 2 W. Reading the HAT’s eight buttons through libgpiod edge events rather than a polling library is most of why — the obvious alternatives cost 7–8% forever.