Manual installation
This is what bootstrap.sh does, step by step, for readers installing by hand or debugging an install that went wrong. Every step states what it does, why it is needed, and how to confirm it worked.
Run everything on the Pi, as root, from the repository root. ZynthianOS is administered as root, and root SSH login is enabled out of the box. There is also a zynthian user in the sudo group, but nothing here uses it — bootstrap.sh refuses to run below uid 0, and the steps below assume root:
ssh root@zynthian.local # or the IP; password: opensynth, older images raspberry
git clone https://github.com/Witzman/Generative-Techno-ZynthianMaschine-MKII
cd Generative-Techno-ZynthianMaschine-MKII
Reaching the Pi
Everything below is typed on the Pi over SSH, so start by getting there. An ethernet cable straight from the Pi to your computer works, and so does a switch.
Plan on a cable or a display for the first contact — WiFi cannot be set up over WiFi. A freshly flashed card holds no WiFi credentials, and every way of giving it some needs the Pi already reachable: the admin menu needs a screen, webconf and VNC need a network. Boot a fresh card with no cable and it joins nothing, with no way in. The hotspot has the same catch — it is switched on from the admin menu, so it is a convenience once you are in, not a way of getting in.
| Way out of it | What you do |
|---|---|
| Display, once | Attach HDMI, set the WiFi up in the UI's admin menu, unplug the display. It never needs to come back |
| Cable, once | Boot with ethernet, take the address, reach webconf or VNC, set the WiFi up there, unplug the cable |
Either one is a one-off. After it the Pi comes up on WiFi by itself and the rig is headless for good.
| Route | How |
|---|---|
| Display attached | The UI comes up on HDMI. Admin → Network Info shows the address. It also appears on the error screen |
| No display | ssh root@zynthian.local, or http://zynthian.local for webconf. zynthian.lan is the same machine under a second name |
| Neither name resolves | Your side is not doing mDNS. Some systems need it enabled; WSL2 never resolves .local. Take the address from your router and use it in place of the name |
| What | Login |
|---|---|
| webconf | password opensynth |
| SSH | user root, password opensynth. Older images use raspberry |
These are factory defaults, identical on every Zynthian. Change them in webconf before the instrument sits on a network you do not control.
None of this is ours — it is stock Zynthian, and the project's own wiki is the place to go when a route here does not fit your setup. No Hardware Build covers flashing, first boot and the network from scratch; Accessing Zynthian from your computer is the fuller list of ways in — VNC, SFTP, WiFi hotspot, and the Windows and macOS notes.
Before you start
Confirm the machine is what the rest of this page assumes.
| Check | Command | Expected |
|---|---|---|
| OS build | head -1 /zynthian/build_info.txt | contains Oram-2601-1 |
| UI branch | git -C /zynthian/zynthian-ui branch --show-current | oram-2601.1 |
| webconf | open http://<pi> | the configuration page loads |
A different branch means another release train. Nothing here has been run on vangelis, which is still beta.
The daemon and the driver
Step 1 — Build the daemon
What. Compiles the Rust HID daemon that speaks to the Maschine. Why. No binary is shipped; the daemon is built where it runs. How to confirm. daemon/target/release/maschine exists and is executable.
apt install -y rustc cargo
cd daemon && cargo build --release && cp picturetest.png target/release/ && cd ..
Minutes on a Pi 4. Do not interrupt it.
Step 2 — Put the daemon config in place
What. Installs pad note offsets, encoder CC numbers, and external_pad_leds. Why. Without "external_pad_leds": true the daemon repaints the pads itself in its own global colour, and the first pad you touch destroys the driver's per-channel picture. How to confirm. The grep prints the flag as true.
cp system/maschine.json daemon/maschine.json
grep external_pad_leds daemon/maschine.json
# → "external_pad_leds": true
Step 3 — Install the udev rule
What. Creates the stable /dev/maschine symlink and restarts the daemon on replug. Why. Otherwise the daemon must be pointed at a /dev/hidrawN number that moves between boots. How to confirm. /dev/maschine exists and points at a hidraw node.
install -m 0644 system/99-maschine.rules /etc/udev/rules.d/99-maschine.rules
udevadm control --reload-rules
udevadm trigger --subsystem-match=hidraw
Step 4 — Install the helper scripts
What. Puts the JACK connect and clock helpers in /usr/local/bin. Why. maschine-jack-connect.sh sets the port alias virtual:maschine.rs/Maschine MK2 Pads. Zynthian derives a control-device id from the part of an alias after the first /, and a2j gives user-client ports no alias at all — without it the driver has no id to bind to and never loads. How to confirm. All three files are present and executable.
install -m 0755 system/maschine-jack-connect.sh /usr/local/bin/
install -m 0755 system/maschine-clock-bridge.py /usr/local/bin/
install -m 0755 system/maschine-clock-connect.sh /usr/local/bin/
Step 5 — Install and enable the systemd units
What. Installs the daemon, its web editor and the clock bridge as services. Why. The daemon must survive reboots and reconnect to JACK by itself. How to confirm. systemctl is-active maschine-mk2 prints active, and the MK2's displays light.
install -m 0644 system/maschine-mk2.service /etc/systemd/system/
install -m 0644 system/maschine-web.service /etc/systemd/system/
install -m 0644 system/maschine-clock.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now maschine-mk2 maschine-web maschine-clock
If you cloned anywhere other than
/root/Generative-Techno-ZynthianMaschine-MKII, editExecStartandWorkingDirectoryinmaschine-mk2.service, and--directoryinmaschine-web.service.install.shrewrites these for you; by hand it is on you.
Step 6 — Let a2jmidid export software clients
What. Makes the daemon's ALSA sequencer port visible in JACK. Why. Without a JACK port, step 4's script has nothing to connect or alias. How to confirm. The port appears.
jack_lsp | grep "Pads MIDI"
# → a2j:maschine rs [N] (playback): Maschine MK2 Pads MIDI
Step 7 — Patch zynautoconnect
What. The only change to a Zynthian core file: adds the Pads port to the hardware-MIDI-source whitelist so it gets a zmip slot, and pins the stable uid. Why. Without it the driver never binds and the rig does nothing, with no error in any log — the most confusing failure in this project. Internally Zynthian gets as far as Found and never reaches Loaded, but both are INFO messages you will not see at the default log level; see step 10. How to confirm. The grep returns 1 or more.
python3 tools/patch-autoconnect-maschine.py
grep -c "maschine rs.*Pads MIDI" /zynthian/zynthian-ui/zynautoconnect/zynthian_autoconnect.py
It ships as a patcher rather than a copy, so it edits your file. It is idempotent. Re-run it after every Zynthian system update — an update replaces zynthian_autoconnect.py and takes the binding with it.
Step 8 — Copy the three driver files
What. Drops the control-surface driver into Zynthian's ctrldev directory. Why. Two traps. These are untracked drop-ins in a checkout that tracks upstream, so never use git in /zynthian/zynthian-ui — a git reset --hard deletes them and the running instrument with them. And every module there needs a dev_ids attribute; without it the whole UI crash-loops every 14 seconds. How to confirm. All three files are present in the target directory.
install -m 0644 ctrldev/zynthian_ctrldev_maschine_mk2.py \
ctrldev/techno_lib.py \
ctrldev/maschine_mk2_lib.py \
/zynthian/zynthian-ui/zyngine/ctrldev/
Step 9 — Restart, daemon first and UI second
What. Brings both halves up in the one order that works. Why. Restarting the daemon alone makes a2j re-register the Pads port onto a new zmip slot while the driver stays bound to the dead one. The rig goes silent, no error appears anywhere, and a stale route is left behind that makes every later pad tap fire twice. How to confirm. Zynthian's UI comes back on the display.
systemctl restart maschine-mk2
sleep 8
systemctl restart zynthian
Step 10 — Confirm the surface is alive
What. Proves the driver bound and the routing is clean. Why. No route means step 7 did not take effect; two routes mean a stale one from an earlier session is still alive, and every pad tap will fire twice. How to confirm. Exactly one ZynMidiRouter:devN_in under the Pads port.
jack_lsp -c | awk '/\(capture\): Pads MIDI/{f=1;next} /^[^ \t]/{f=0} f{print}'
# → ZynMidiRouter:dev2_in ← exactly one line, and nothing else
Do not expect a particular slot number. Patched Zynthian assigns the slot itself, and it varies; this rig runs on dev2_in. Nothing else may connect that port — a second connector is what produced a duplicate route here, and two routes make every pad tap fire twice. See Why there is no "Loaded" line below before hunting through the journal.
Why not grep -A3. The obvious form of this check, jack_lsp -c | grep -A3 "Pads MIDI", reports a healthy rig as a broken one. It matches the Pads port twice — once as a port and once as another port's connection — and the three lines it then prints are unrelated ports sitting at the left margin:
a2j:maschine rs [129] (capture): Pads MIDI
ZynMidiRouter:dev2_in ← the only real route
a2j:maschine rs [129] (playback): MIDI Control
ttymidi:MIDI_in
--
a2j:maschine rs [129] (capture): Pads MIDI
ZynMidiRouter:dev3_in ← these three are ports, not routes
ZynMidiRouter:dev4_in
ZynMidiRouter:dev5_in
Four devN_in lines, one actual route. Read that as a fault and the fix you would reach for — disconnecting the "extras" — breaks a rig that was working. Indentation is the whole distinction: a route is indented under its port, a port starts at the left margin. The awk form above prints only the indented lines belonging to the Pads capture port, so it cannot mislead.
With no snapshot loaded the MK2 draws both tab rows, the Group buttons in channel colours, and the CONTROL button lit. Nothing sounds yet — no chains exist.
Plugins and drum kits
Step 11 — Install the packaged plugins
What. Installs three of the five engines from Debian. Why. obxd-lv2 is channel G, padthv1-lv2 is channel H, and tap-lv2 provides TAP Reverberator and TAP Stereo Echo for all sixteen inserts. How to confirm. Three Status: install ok installed lines.
apt install -y obxd-lv2 padthv1-lv2 tap-lv2
dpkg -s obxd-lv2 padthv1-lv2 tap-lv2 | grep ^Status
Step 12 — Confirm JC303
What. Checks the bass engine, which is not a Debian package. Why. It comes from Zynthian's own plugin set; if missing, install it through webconf so the plugin cache learns about it. How to confirm. The directory exists.
ls -d /zynthian/zynthian-plugins/lv2/JC303.lv2
Step 13 — Enable the plugins and regenerate the LV2 cache
What. In webconf: Engines, enable Obxd, padthv1, JC303, TAP Reverberator and TAP Stereo Echo, then Regenerate LV2 Cache. Why. A plugin that is installed but not enabled is invisible to a snapshot, and loading one that names a disabled plugin gives a chain with no engine. How to confirm. All five appear as available engines. Let the scan finish before loading anything.
Step 14 — Confirm the SFZ drum kits
What. Checks the kits the five drum channels play. Why. Missing kits load silently — the channels exist and make no sound. Use find -type f: one entry is a directory, so ls over-counts. How to confirm. A count greater than zero.
find "/zynthian/zynthian-data/soundfonts/sfz/Drum Machines" -maxdepth 1 -type f -name '*.sfz' | wc -l
# → 40 (on the rig this was written from)
The snapshot
Step 15 — Copy it into a bank
What. Places the factory snapshot in bank 000. Why. The bank subdirectory is not optional — a snapshot at the snapshots root is invisible in the UI. How to confirm. The file is listed under 000/.
install -m 0644 snapshot/017-generative-techno.zss \
/zynthian/zynthian-my-data/snapshots/000/
Step 16 — Make it the default, so it loads by itself
What. Copies the same file to the snapshots root as default.zss. Why. At startup Zynthian restores last_state.zss if there is one, and otherwise falls back to default.zss — which is exactly the fresh-install case, so the instrument comes up without anyone touching a screen. default.zss is only ever written by an explicit save as default, so seeding it destroys nothing, and a Pi with an existing session keeps its own. How to confirm. Restart the UI; it comes up in the mixer, not the main menu.
install -m 0644 snapshot/017-generative-techno.zss \
/zynthian/zynthian-my-data/snapshots/default.zss
systemctl restart zynthian
Step 17 — Or load it by hand
What. Opens Snapshots, goes into bank 000, taps 017-generative-techno. Why. Needed only if this Pi already had a session, whose last_state.zss takes priority over the default. Loading takes about fifteen seconds: eight engines start, sixteen plugin hosts come up, and the driver re-reads its parameters out of the sequencer. How to confirm. Eight mixer strips plus main, both tab rows drawn.
Never save from webconf's Snapshots page. Its Name: field plus the checkmark renames the selected bank — it has destroyed bank
000here once. Loading from webconf is safe; saving from it is not.
Notes
You do not need a touchscreen
The UI is ordinary HDMI: a monitor or TV with a USB mouse and keyboard drives it, and webconf's Snapshots page loads a snapshot from a browser with no screen attached at all. A touchscreen is what the instrument was built around and is far nicer to play — but the rig runs headless, and only saving a snapshot needs the UI itself.
Which does not have to be a physical screen. Zynthian ships a VNC server — webconf, INTERFACE → UI Options → Enable VNC Server — and the whole UI then runs in a browser at http://zynthian.local:6080/vnc.html, with the engines' own desktop on :6081. Confirmed working on this rig. That is the headless answer for anything the UI owns, snapshot saving included: a Pi with no display, no keyboard and no mouse is still fully driveable from the computer you installed from.
Why there is no "Loaded" line
Zynthian logs Found ctrldev driver … and Loaded ctrldev driver … at INFO, while ZYNTHIAN_LOG_LEVEL defaults to WARNING (30). On a stock rig neither line is ever written, whether the driver loaded or not, so their absence proves nothing — the JACK route in step 10 is the check that works as shipped.
To see them, uncomment the line Zynthian ships for it in /zynthian/zynthian-ui/zynthian.sh, set it to 20, and restart the UI:
export ZYNTHIAN_LOG_LEVEL=20 # 10=DEBUG, 20=INFO, 30=WARNING (the default)
systemctl restart zynthian
Measured, not assumed: at level 20 the two lines appear as
INFO:zynthian_ctrldev_manager.update_available_drivers: Found ctrldev driver
'zynthian_ctrldev_maschine_mk2' for devices with ID 'Maschine MK2 Pads'
INFO:zynthian_ctrldev_manager.load_driver: Loaded ctrldev driver
'Maschine MK2 Drum Rig' for 'Maschine MK2 Pads'.
Note what the Loaded line is keyed on. It carries the driver's display name, Maschine MK2 Drum Rig — not the module name. A grep for maschine_mk2 finds the Found line and misses the Loaded one, which is the one that matters. Grep for ctrldev driver instead.
zynthian.sh is a core Zynthian file, so a system update reverts it. There is no setting for this in webconf.
Verify
bash tools/check-prereqs.sh; echo "exit=$?" # → exit=0 once loaded
jack_lsp | grep -c TAP # → 64
cd ctrldev && python3 -m unittest discover -s tests -q # → Ran 271 tests … OK
The unit tests run anywhere — no Pi, no Maschine. They are the only automated proof this project has: the driver cannot be imported off the Pi, so everything generative and presentational lives in techno_lib.py, which has no Zynthian imports and is tested.
When it does not work
| Symptom | Cause | Fix |
|---|---|---|
| Displays blank, buttons dark, Zynthian fine | daemon not running | systemctl status maschine-mk2, then journalctl -u maschine-mk2 -n 50 |
Surface dead, no devN_in under the Pads port | no zmip slot — the driver never bound | Step 7, then restart daemon and UI |
No Pads MIDI in jack_lsp | a2jmidid not exporting software clients | Step 6 |
| Every pad tap fires twice | a stale JACK route | Step 10, using the awk form — only an indented second devN_in is a real duplicate. Repair by restarting the daemon, then the UI |
| Whole UI restarts every ~14 s | a module in ctrldev/ without dev_ids | Step 8 |
| First pad touch destroys the pad colours | external_pad_leds missing | Step 2, then restart the daemon and the UI |
| Input dies after seconds, then recovers | kernel hidraw fault; the daemon reopens the device | Nothing to do. watchdog: input stalled, reopened about every 8 s is healthy |
| A channel is silent, tab dashed | muted, or its generator is silent — HITS 0, or play chance 0 | Not a fault. Raise HITS or CHANCE, or unmute with its F button |
| A channel is silent, tab normal | engines did not start | jack_lsp | grep -c TAP → 64. Fewer means plugin instances failed |
| Every channel silent | something is soloed | Tap SOLO to leave solo mode |