Firmware

fm1-ctrl

Rising72/100

FM-1 as a hardware controller for web emulators — thin firmware + generic WebMIDI shim + JSON map convention

fm1-ctrl is custom firmware for the M-VAVE FM-1, by @johnskiller. Open source under MPL-2.0.

forM-VAVE FM-1·by@johnskiller

Unclaimed. Is this yours? Sign in with GitHub as @johnskiller to claim it.

Downloads
0
Stars
0
Forks
0
Commits
50
Last commit
Get the source

No release files yet. Build from source.

MPL-2.0· johnskiller/fm1-ctrl·downloads come from the creator’s GitHub releases

50
Commits
0
Releases
50 commits and 0 releases in the last year
Activity · last 12 months

fm1-ctrl

A thin, transparent MIDI controller for the M-VAVE FM-1: every control sends its own event, unchanged, and the mapping lives in the receiving host (a web emulator, a DAW, or a map pushed over SysEx). The firmware has no business logic.

Status: ported to fm1-base (MPL-2.0), which brings Bluetooth MIDI. The app is one file, app/app.c.

The map

controlmessage
27 keysNote On/Off, note 53..79, velocity from the key's own sensing
20 of thempoly aftertouch 0xA0 note pressure — the keys on the analog rows only
7 encodersCC 20..26, relative, 64 + detents (split at ±63)
14 buttonsCC 40..53, 127 down / 0 up, labels FX..OCT+
MASTERCC 27 (7-bit) plus the 14-bit pair CC 7 / CC 39
LEDs / screenecho of this unit's own panel: held keys and buttons, and each held key's pressure

Buttons are sent as plain CC, not 0xFA/0xFC and not MMC. Measured reasons: the FM-1 emulators drop every byte >= 0xF0 (melodee/Felucca index.html:439,472 — if (d[0] >= 0xF0) return;), and no firmware in the ecosystem reads MMC. DriftSeq implements this exact map on purpose (web/emu.js:184), so it is the baseline this firmware must not break.

Build

git submodule update --init --recursive        # the platform
export AC79_SDK=/path/to/AC79_AIoT_SDK         # only for Bluetooth
python3 platform/tools/build.py app            # firmware + package
python3 platform/tools/build.py app --host     # the host simulator
python3 platform/tools/build.py app --web      # the browser emulator

Output: app/build/ctrl.bin, app/build/ctrl.fwsc (identity FM-1_9300 in a dev build).

Verify without the hardware

Two independent ways.

The contract tests — the real app/app.c, on a fake platform, no device and no fm1-base build:

sh app/tests/run.sh          # 15 cases, ~0.2 s

app.c is included rather than linked, so what is tested is the source that ships, and the test can read the state the app keeps (the LED masks). A "frame" there is app_render then app_frame, because the platform drains MIDI in from the audio interrupt.

The host simulator — the whole platform, writing the actual bytes it sent:

python3 platform/tools/build.py app --host
app/build/host/ctrl_host app/tests/map.txt app/build/host
cat app/build/host/midi_out.txt

See platform/README.md for the script language (wait, key, press, turn, shot, expect).

Repository shape

app/                    the firmware: app.c, app.h, fm1.toml, words.tsv, tests/
platform/               fm1-base (submodule): bsp/, device/, include/, tools/, web/
tools/                  this project's own scripts (map generation, checks)
web/                    fm1-shim.js: drives a third-party emulator from the real FM-1
docs/                   specs, maps, research notes

Everything under platform/ is MPL-2.0 and belongs to its authors; our own files are MPL-2.0 too. No GPL-3.0 code is in this tree — see LICENSING.md for how that came about and what it replaced.

What it does not do

  • It does not drive other firmwares' browser emulators by itself. Their worklets accept {type:"buttons"|"keys"|"enc"|"master"} and have no MIDI input; web/fm1-shim.js is the translation layer, and it needs the page to expose its send().
  • It does not make sound. A controller sends events.
  • Its own screen and LEDs echo this unit: the keys and buttons it is holding, and the pressure each analog key reports. What the host sends back is counted (IN n) and otherwise ignored — one FM-1, one source of truth for what the panel shows.
  • It sends aftertouch for the 20 keys on the analog rows (F3..C5) only. The seven keys of the top row (C#5..G5) have no converter of their own — their velocity comes from a probe through the LED line — so they never send 0xA0.

Docs

  • docs/base-and-mcu.md — what fm1-base gives a firmware, and what the AC79 can actually do: the audio path, the second core (and its hardware defect), memory, the analog keys. Read this before designing an engine.
  • docs/research/porting-to-fm1-base.md — what the port took, the ten pitfalls, and what DriftSeq will meet doing the same.
  • docs/research/timer5-isr-trap.md — the TIMER5 wrapper trap of the old tree.
  • docs/research/upstream-audit.md — the old tree's code lineage.

Licence

MPL-2.0. See LICENSING.md.