Welcome to my annotated fork of the Bastl Instruments microGranny 2.0 firmware. The goal here isn't to rewrite Vaclav's code (yet) – for now, it's to document the living daylights out of it so more people can mod, repair, and teach with this tiny granular beast. Think of this repo as half studio notebook, half roadie handbook.
- The original firmware is legendary, but the documentation lives in forum posts, PDF scans, and memories of modular nerds. This fork collects the most useful breadcrumbs in one place.
- Most changes so far are documentation + build tooling. The only behavior change in this fork is a small MIDI tweak: randomize-by-CC now triggers the same UI feedback and knob resync you get from the front panel.
- Every note below comes from reading the code that ships in this repository. If it isn't in these
.inofiles, it isn't promised here.
Completed in this fork
- PlatformIO scaffolding +
src/reorg so the firmware builds outside the Arduino IDE. - Documentation additions: parameter packing notes, PlatformIO walkthrough, and this annotated README.
mg2HWshim README to document the missing hardware layer dependency.- MIDI randomize CC feedback (see
src/MIDI.inoinproceedCC()).
Open work / next steps
- Bring the official
mg2HWlibrary intolib/mg2HW/(or wirelib_depsto a fork) so PlatformIO builds without manual copy steps. - Document the LED/color language and UI message patterns once
mg2HWreferences are in hand. - Cross-check the MIDI randomize-by-CC feedback on real hardware and document any quirks or regressions vs upstream.
- Decide whether to pin exact SdFat/WaveRP versions in
platformio.ini(or document known-good versions) to avoid build drift.
You'll want the same setup Bastl used:
- Controller board: Bastl microGranny 2.0 (ATmega328P at its heart, Arduino Uno-compatible toolchain).
- Audio + UI shield:
mg2HWlibrary (not bundled here; grab it from the original Bastl release). - Storage: FAT-formatted microSD card.
- Libraries:
- Tooling: Arduino IDE (1.8.x era) or Arduino CLI with the AVR toolchain.
Flash routine:
- PlatformIO heads-up: the repo now ships with a ready-to-run
platformio.ini. Install PlatformIO, clone the repo, and runplatformio runto build orplatformio run --target uploadto flash. Drop Bastl'smg2HWsources underlib/mg2HW/(or tweaklib_deps) so the hardware layer resolves. - Arduino IDE traditionalists: install the SdFat and WaveRP libraries, park
mg2HWin your libraries folder, then opensrc/microGranny2.inoand upload as usual. The IDE still slurps in the companion.inotabs because they're sitting right next to the main sketch insidesrc/.
| File | What it holds |
|---|---|
src/microGranny2.ino |
Bootstraps the hardware, SD card, MIDI, and memory state. Think of it as the conductor orchestrating the other modules. |
src/SOUND.ino |
Playback engine: loop logic, grain triggering, envelope shaping, and keeping the WaveRP stream on leash. |
src/MEM.ino |
Compact bit-packed parameter storage for six sounds × multiple presets and banks. Also contains the parameter metadata tables documented below. |
src/MIDI.ino |
MIDI parser + note buffer. Handles legato, sustain, clock sync, and sample-rate tweaks via a 49-entry lookup table. |
src/SD.ino |
Disk ops: SD initialisation, file indexing, sample playback, and the record-to-WAV routine. |
src/UI.ino |
Front-panel choreography – LEDs, RGB feedback, display messages, long-press/shift combos, and record mode UI. |
src/fileNames.ino |
Utility helpers for naming and listing samples on the card. |
The firmware stores eleven variables per sound, bit-packed inside variable[NUMBER_OF_SOUNDS][NUMBER_OF_BYTES]. Here's the layout pulled directly from the tables in MEM.ino:
| Index | Label (labels) |
Meaning | Range (maxValue) |
Notes |
|---|---|---|---|---|
| 0 | r |
Sample rate | 0–1023 |
Higher is faster; tuned playback uses the note table in MIDI.ino. |
| 1 | c |
Bit-crush depth | 0–127 |
Drives WaveRP's setCrush controls. |
| 2 | a |
Attack | 0–127 |
Envelope generator attack time. |
| 3 | r |
Release | 0–127 |
Envelope generator release time. |
| 4 | g |
Loop length | 0–127 |
Tied to loopLength in SOUND.ino. |
| 5 | m |
Shift speed | 0–255 |
Signed rate for granular shifting (see shiftSpeed). |
| 6 | s |
Start position | 0–1023 |
Sample start index. |
| 7 | e |
End position | 0–1023 |
Sample end index. |
| 8 | (none) | Mode/flags (SETTING) |
0–63 |
Bitfield for tuned/legato/repeat/sync/random shift toggles. |
| 9 | (none) | Sample name char 1 | ASCII 0–127 |
First stored character of the SD filename. |
| 10 | (none) | Sample name char 2 | ASCII 0–127 |
Second stored character of the SD filename. |
Those values load and save through loadPreset, savePreset, getVar, and setVar. Want to add a new parameter? Update NUMBER_OF_VARIABLES, extend labels, maxValue, variableDepth, and the byte/bit coordinate tables, then teach the UI and storage code about it.
- Buffering:
MIDI.inokeeps a 16-slot FIFO (midiBuffer). Duplicate notes get deduped before insertion. - Legato: If
legatois true and the incoming MIDI note is between 23 and 65, the code reuses the active voice and simply retunes playback via thenoteSampleRateTable. - Clock sync:
clockCounterincrements with MIDI clock messages;instantLooproutines inSOUND.inouse it to align stutter loops. - Sustain:
sustaingates note-off handling so you can latch grains with a pedal.
initSdCardAndReport()is strict: it wants a card that passesSd2Card::initand exposes a FAT16/32 root.- The playback path caches directory positions per sound inside the
index[]array. Once a sound slot has touched a file, subsequent plays re-open by index instead of name – faster, but it means renaming files on the card without rebooting can confuse things. - Recording drops 16-bit WAVs named
<bank><preset>.WAV(e.g.30.WAV). The naming math lives intrackRecord(); study it before changing your folder structure.
- Audit parameters – start in
MEM.inoso you know what space is left invariable[]and the EEPROM layout (E_BANK,E_PRESET). - Touch UI intentionally –
UI.inomanages long presses, combo buttons, and the 7-seg display text. Update the helper strings and knob mappings in the same breath. - Mind the wave player – anything touching
wave(WaveRP) should pause/resume carefully; seerenderLooping()andrenderGranular()inSOUND.inofor the existing handshake. - Keep MIDI light – the parser runs in the main loop; avoid blocking operations there or you'll miss clocks.
- ✅ EEPROM boot guard: on first boot
EEPROM.read(1000)forces playback ofZZ.WAV, then resets the init flag. Handy for smoke-testing new builds. - ✅
RECORD_RATEis hard-coded to 22,050 Hz. Raise it if you have the bandwidth, but watchMAX_FILE_SIZE(currently 100 MB) and SD card performance. - ✅
instantLoopmode2is the synced stutter.instantClockCounteris set elsewhere to quantise the loop restarts. - ❓ TODO: Document the full color/LED language once the
mg2HWlibrary is in hand.
Pull requests are welcome if you discover quirks or want to teach the next hacker something new. Keep it punk, but keep it accurate.
docs/platformio.mdwalks through the PlatformIO setup, dependency dance, and the new folder layout.docs/status.mdkeeps a short ledger of what this fork has changed and what is still pending.
- 2026-01-28 — Documentation refresh, including fork status ledger and clearer orientation. (
077febb) - 2025-11-09 — MIDI randomize-by-CC now triggers UI feedback + knob resync. (
25eade7) - 2025-10-28 — PlatformIO scaffolding,
src/reorg, and initial documentation drop. (9ba1bcb,6f49849)