diff --git a/.agents/README.md b/.agents/README.md deleted file mode 100644 index 271f85b2..00000000 --- a/.agents/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# `.agents/` — AI Agent Harness - -This directory is the Antigravity harness folder for Patternflow. It is version-controlled and public — part of the open-source release. - -## What is here - -Nothing an agent needs to read first. The root `AGENTS.md` is the single context file, loaded in every session by Antigravity, Cursor and Claude Code alike; it carries the repository map, the hard rules, the build commands and the versioning conventions. - -The skills and workflows that used to live here (`add-pattern`, `update-bom`, `release-version`, `firmware-cleanup`, `/release`, `/update-build-doc`) were retired on 2026-09-03. They were written against the v1/v2 firmware and board — patterns as functions inside `patternflow.ino`, an SMD BOM, version numbers in folder names — and an agent following them today would edit the firmware core (which `AGENTS.md` forbids) or "correct" the v3 BOM back to v2. The procedures they described now live where they are kept current: - -- releasing → `docs/RELEASING.md` and `.github/workflows/firmware-release.yml` -- writing a pattern → `firmware/CUSTOM_PATTERNS.md`, `PATTERN_GUIDE.md` -- writing a feature or an edition → `FEATURE_GUIDE.md`, `docs/EDITIONS.md` -- the BOM → `hardware/bom/README.md` (the CSV is the source of truth) - -## Contributing to the harness - -If you find yourself repeatedly explaining the same thing to your agent while working on Patternflow, that is a candidate for a new skill under `skills//SKILL.md`. Write it against the current tree, and name the file it derives its facts from, so the next reader can tell when it has gone stale. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..c86d5ffd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# Keeps line endings and indentation consistent across editors. Not enforced by CI. +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{ino,h,cpp,c,ts,tsx,js,mjs,json,yml,yaml,css}] +indent_style = space +indent_size = 2 + +[*.py] +indent_style = space +indent_size = 4 diff --git a/.github/DISCUSSION_TEMPLATE/show-and-tell.yml b/.github/DISCUSSION_TEMPLATE/show-and-tell.yml deleted file mode 100644 index 4f686b0e..00000000 --- a/.github/DISCUSSION_TEMPLATE/show-and-tell.yml +++ /dev/null @@ -1,29 +0,0 @@ -title: "Share your build" -labels: ["area:community"] -body: - - type: input - attributes: - label: Project / Maker name - description: How you want to be credited on the globe - validations: - required: true - - type: textarea - attributes: - label: Short intro - description: About 3 sentences is enough - validations: - required: true - - type: textarea - attributes: - label: Links - description: Your app, project page, video — anything - - type: textarea - attributes: - label: Images - description: Drag & drop ~3 screenshots or photos here - - type: input - attributes: - label: Location - description: City or country, so we can place you on the globe - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index aca53363..e67ad620 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -8,10 +8,13 @@ contact_links: about: Stuck building or flashing? Ask in the hardware-help channel — photos and quick back-and-forth. - name: Share a pattern url: https://discord.com/channels/1497757947827327067/1499908302962819236 - about: Show a custom pattern you made. Develop and test it first in the Pattern Lab. + about: Publish it from the Pattern Lab to the Community, then show it off in the patterns channel. - name: Discord community url: https://discord.gg/Vr9QtsxeTk about: New here? Join the server, then jump into the channels above. + - name: Getting help — every venue in one table + url: https://github.com/engmung/Patternflow/blob/main/SUPPORT.md + about: Where to ask what, including the venue that works where Discord is blocked. - name: Contributing guide url: https://github.com/engmung/Patternflow/blob/main/CONTRIBUTING.md about: How to contribute docs, firmware, hardware, or web changes. diff --git a/.github/ISSUE_TEMPLATE/share_build.yml b/.github/ISSUE_TEMPLATE/share_build.yml new file mode 100644 index 00000000..39e6ba83 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/share_build.yml @@ -0,0 +1,63 @@ +name: Share your build +description: Put your finished Patternflow on the build map. +title: "Build: " +labels: ["area:community"] +body: + - type: markdown + attributes: + value: | + Every pin on the [build map](https://patternflow.work/inside) is someone who built one from these files. Fill this in and yours goes up. + Comfortable with git? The map is a data file plus a photo folder — see [CONTRIBUTING.md](https://github.com/engmung/Patternflow/blob/main/CONTRIBUTING.md#your-build) and send it as a pull request instead. + - type: input + id: maker + attributes: + label: Maker name + description: How you want to be credited on the map. + validations: + required: true + - type: input + id: location + attributes: + label: Location + description: City or country — the pin goes where you say, at that precision. + validations: + required: true + - type: dropdown + id: board + attributes: + label: Board + options: + - v3.9 PCB + - v3.0 PCB + - v2.x PCB + - breadboard + - something else (say what below) + validations: + required: true + - type: dropdown + id: enclosure + attributes: + label: Enclosure + options: + - official 3D-printed case + - my own remix of the case + - something else entirely + - type: textarea + id: intro + attributes: + label: Short intro + description: About three sentences — what you changed, what it is for, where it lives. + validations: + required: true + - type: textarea + id: images + attributes: + label: Photos + description: Drag and drop two or three photos here. By posting them you license them CC BY-SA 4.0 so they can go on the map; say so here if you need a different license. + validations: + required: true + - type: textarea + id: links + attributes: + label: Links + description: A video, a project page, your Instagram — anything you want the pin to carry. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f8d7c51e..77679420 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,4 +11,3 @@ --- - [ ] Touches `web/` → it builds (`npm run build`) -- [ ] Sharing a pattern? It's CC-BY-SA 4.0 with an `// Author:` header (see CONTRIBUTING) diff --git a/.github/release.yml b/.github/release.yml deleted file mode 100644 index 008952b9..00000000 --- a/.github/release.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Groups merged PRs into sections when you click "Generate release notes" -# on a new GitHub release. Categories match by PR label, top to bottom. -changelog: - exclude: - labels: - - skip-changelog - categories: - - title: 🚀 New features - labels: - - "type:feature" - - title: ✨ Improvements - labels: - - "type:improvement" - - title: 🐛 Fixes - labels: - - "type:bug" - - title: 📖 Docs - labels: - - "area:docs" - - title: 🧩 Other changes - labels: - - "*" diff --git a/.github/workflows/community-surfaces.yml b/.github/workflows/community-surfaces.yml new file mode 100644 index 00000000..b4272f32 --- /dev/null +++ b/.github/workflows/community-surfaces.yml @@ -0,0 +1,45 @@ +name: Community folders carry their README + +# Two folders take outside contributions as whole new subfolders: +# hardware/case/remixes/-/ and integrations//. Their +# files (STL, DXF, .maxpat, .tox) cannot carry an SPDX header, so each +# folder's README is the header — it names the author and the license, and +# an integration names the contract it is built against. That is all this +# checks. There is no geometry check and no build; stdlib shell, seconds. +on: + pull_request: + paths: + - "hardware/case/remixes/**" + - "integrations/**" + - ".github/workflows/community-surfaces.yml" + +concurrency: + group: community-surfaces-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Every remix folder has a README with Author and License + run: | + fail=0 + for d in hardware/case/remixes/*/; do + [ -d "$d" ] || continue + if [ ! -f "$d/README.md" ]; then echo "::error::$d has no README.md"; fail=1; continue; fi + grep -Eq '^Author: *[^ ]' "$d/README.md" || { echo "::error::$d/README.md has no 'Author:' line"; fail=1; } + grep -Eq '^License: *CC-BY(-SA)?-4\.0' "$d/README.md" || { echo "::error::$d/README.md has no 'License: CC-BY-SA-4.0' (or CC-BY-4.0) line"; fail=1; } + done + exit $fail + - name: Every integration has a README that names a contract + run: | + fail=0 + for d in integrations/*/; do + if [ ! -f "$d/README.md" ]; then echo "::error::$d has no README.md"; fail=1; continue; fi + grep -Eq 'rest-api\.md|osc-spec\.md|midi-spec\.md|mqtt-spec\.md|audio-ws-spec\.md|pfst-v2-spec\.md' "$d/README.md" \ + || { echo "::error::$d/README.md does not name a contract in docs/ (rest-api, osc-spec, midi-spec, mqtt-spec, audio-ws-spec, pfst-v2-spec)"; fail=1; } + done + exit $fail diff --git a/.github/workflows/docs-links.yml b/.github/workflows/docs-links.yml index 4daa0988..ce102d47 100644 --- a/.github/workflows/docs-links.yml +++ b/.github/workflows/docs-links.yml @@ -11,6 +11,12 @@ on: - "**/*.md" - ".github/scripts/check_links.py" - ".github/workflows/docs-links.yml" + # A renamed STL, CSV, Gerber zip or image breaks a link just as a + # renamed page does; these trees are where those files live. + - "hardware/**" + - "docs/**" + - "integrations/**" + - "tools/**" push: branches: [main] paths: diff --git a/AGENTS.md b/AGENTS.md index e520c3e5..4a8d17e4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Patternflow — AI Agent Context -This file provides persistent project context for AI coding agents (Antigravity, Cursor, Claude Code). It is loaded automatically at the start of every session. It is the only agent-context file; `.agents/` holds nothing an agent needs to read first. +This file provides persistent project context for AI coding agents (Antigravity, Cursor, Claude Code). It is loaded automatically at the start of every session. Cursor and Antigravity read it directly; Claude Code reads it through the one-line `CLAUDE.md` shim. The human-facing map of the repository is `docs/REPOSITORY.md`; the contributor rules are `CONTRIBUTING.md`, and the hard rules below are the same rules. ## What this project is Patternflow is an open-source hardware instrument: four rotary encoders controlling generative light patterns on a 128×64 LED matrix, powered by an ESP32-S3. It is an open-source reinterpretation of Nam June Paik's *Participation TV* (1963). The project is multi-domain, encompassing Arduino-based firmware, KiCad/Blender hardware designs, a Next.js web ecosystem, and comprehensive documentation. @@ -14,12 +14,11 @@ It is standalone (not Eurorack), powered from a 5 V power bank through a screw t - `web/` — Next.js site at patternflow.work: landing page (`/`, `/pattern`, `/build`, `/inside` are tabs of one view), Pattern Lab (`/pattern-lab`), community (`/community/**`, SQLite + Drizzle + Better Auth, only on the Pi deployment), browser flasher (`/flash`), the edition shelf (`/editions`; `/variants` redirects there), device update handoff (`/update`), journal, roadmap. `lib/pattern/` is the pattern runtime and annotations, `lib/lab/` the Pattern Lab, `lib/community/` the community, `lib/ai/` the Gemini client. Architecture doc: `web/ARCHITECTURE.md`. The JS presets in `web/src/lib/presets/` are the source of truth for firmware preset headers. - `tools/` — desktop-side helpers: `patternflow-audio-extension` (the browser audio-react extension — also the authoring source the device's `/audio-in` console page is assembled from), `patternflow-audio-android` (phone capture app), `rtpmidi-probe`. - `integrations/` — host-software integrations, each with its own README. `integrations/ableton/` is the Max for Live bridge (knobs → Live parameters over OSC). The Home Assistant integration left this repository on 2026-09-03; its author maintains it separately. Integrations are built against the contracts in `docs/` (`rest-api.md`, `osc-spec.md`, `midi-spec.md`), not against the firmware source. `docs/rest-api.md` also has the table for choosing between HTTP, OSC and MQTT, and the rules that make the device's single-connection web server easy to knock over. -- `.agents/` — AI harness folder for Antigravity. Its skills were retired on 2026-09-03 (they described the pre-`features/` firmware and the v2 board); this file is the context. ## Hard rules (do not violate) 1. Founders boards (#001–#005) are private. The KiCad project in `hardware/pcb/kicad/` is the public v3.9 board (silkscreen "PATTERNFLOW … v3.9"). Never commit founders artifacts to this repo. 2. `hardware/bom/bom_v3.9.csv` is the BOM source of truth. The parts table in `BUILD_GUIDE.md` and the schematic in `hardware/pcb/schematic.pdf` must match it. If you change one, check the other two. (`bom_v3.0.csv` stays for people holding a v3.0 board; it is not the source of truth.) -3. License split is strict: firmware and web code = MIT; hardware designs (PCB, case STLs, Blender source) = CC-BY-SA 4.0. Two separate license files at root: `LICENSE-MIT` and `LICENSE-CC-BY-SA`. Do not merge them. +3. License split is strict: firmware and web code = MIT; hardware designs (PCB, case STLs, Blender source) = CC-BY-SA 4.0. Two separate license files at root: `LICENSE-MIT` and `LICENSE-CC-BY-SA`. Do not merge them; the root `LICENSE` is a pointer to both so GitHub's detector sees both, not a third license. 4. Brand naming: body text = "Patternflow", physical engravings (PCB silkscreen, future case engravings) = "PATTERNFLOW", filenames and URLs = lowercase "patternflow". Never mix these in a single context. 5. Known issues of the current board are documented in `BUILD_GUIDE.md` section 10; the v2.0 fixes and leftovers are in `BUILD_GUIDE_v2.md` section 10. Reference those sections instead of restating the issues. 6. **The board has exactly one power input: `J4`, the screw terminal. Never describe USB-C as a power option.** v3.9 removed the `USB1` footprint and its `R1`/`R2` CC pull-downs outright, after a USB-C-powered v3.0 board ran for 20–30 minutes and then smoked at a connector pin ([#221](https://github.com/engmung/Patternflow/issues/221)) — the failure is delayed, so "it seems to work" is not evidence. On v3.9 the footprint does not exist; on a v3.0 board already in someone's hands, `USB1`/`R1`/`R2` stay unpopulated. Release notes and changelog entries that record the hold as it stood are history — update current-state docs, leave the record alone. @@ -34,12 +33,12 @@ It is standalone (not Eurorack), powered from a 5 V power bank through a screw t - KiCad exports: Export Gerbers from `hardware/pcb/kicad/patternflow.kicad_pcb`. Export STLs from `hardware/case/source/patternflow_case.blend`. ## Versioning -- Project: v3.10.2 (current, released 2026-09-11), using unified semantic versioning across firmware, hardware, web, and docs — the firmware reports it as `PF_IMPROV_FW_VERSION` in `net_config.h`, and `CHANGELOG.md` gets a section per release. Current hardware is the v3.0 board; every v3.x release since has been firmware/web on unchanged hardware. +- Project: v3.10.2 (current, released 2026-09-11), using unified semantic versioning across firmware, hardware, web, and docs — the firmware reports it as `PF_IMPROV_FW_VERSION` in `net_config.h`, and `CHANGELOG.md` gets a section per release. Current hardware is the v3.9 board — the v3.0 board with the USB-C footprint removed, same enclosure, pin map and guide; every v3.x release has been firmware/web on it. - Editions carry their own version line, independent of the project version (`PF_VARIANT_VERSION` in `firmware/bundles//overrides.h`): Audio v0.6.2, Performance v0.2.7, Clock v0.1.5 at the time of writing. The shelf (`web/src/app/editions/editions-data.ts`) and the flasher manifest (`web/public/flash/manifest.json`) name the images that are live; only those images are kept in `web/public/flash/bin/` — older ones live on their release tags. - Firmware source lives in `firmware/patternflow/`; use release tags for versioning instead of encoding the release in the folder name. Tags are `vX.Y.Z`. - Conventions: filenames lowercase with underscores; commit messages start with the area (`firmware:`, `web:`, `docs:`, `hardware:`) then a short present-tense summary. ## Documentation entry points - New users: `README.md` → `BUILD_GUIDE.md` -- Contributors: this file → `docs/EDITIONS.md` (firmware) · `web/ARCHITECTURE.md` (web) · `CONTRIBUTING.md` +- Contributors: `CONTRIBUTING.md` → `docs/REPOSITORY.md` → `docs/EDITIONS.md` (firmware) · `web/ARCHITECTURE.md` (web) - Version history: `CHANGELOG.md` diff --git a/AUDIO_GUIDE.md b/AUDIO_GUIDE.md index d593769f..f6bcf373 100644 --- a/AUDIO_GUIDE.md +++ b/AUDIO_GUIDE.md @@ -163,54 +163,21 @@ about boxes and curves applies unchanged. The edition is also a **MIDI device over the network** (RTP-MIDI, the same protocol macOS calls *Network MIDI*). Once a session is up the panel is an -ordinary MIDI port in any DAW: four knobs in, four knobs out, four buttons, -and a pattern selector. The contract is [`docs/midi-spec.md`](docs/midi-spec.md). - -**Connect** — the Windows walk-through with screenshots is -[`docs/midi-ableton.md`](docs/midi-ableton.md); the short version: - -- **macOS / iOS** — *Audio MIDI Setup → Window → Show MIDI Studio → Network*. - `Patternflow` appears in the directory by itself (Bonjour); select it and - press *Connect*. It is now a MIDI port. -- **Windows** — install the free [rtpMIDI](https://www.tobias-erichsen.de/software/rtpmidi.html) - driver, add a session, pick `Patternflow` from the directory (or add it - by IP, port 5004) and connect. Live sees it as a MIDI in/out port. -- **Linux** — `rtpmidid`. - -**What the messages mean** — the short version: - -| | | -|---|---| -| **CC 20–23** → knob 1–4, **absolute** | 0–127 pins the knob, the way the Director does. A hand turning that knob takes it back. | -| **CC 24–27** → knob 1–4, **relative** | 64 = still, 65 = one detent up, 63 = one down. Endless encoders. | -| **Notes 60–63** → button 1–4 | A pad press is a button press. | -| **Program Change** → pattern | Index on `/patterns`. Remembered like a knob pick. | -| **Out:** CC 24–27, notes 60–63, Program Change | The encoders (as an ordinary 0–127 knob value the panel keeps for you), the buttons and pattern changes, as they happen. | +ordinary MIDI port in any DAW: four knobs in (CC 20–23 absolute, CC 24–27 +relative), four buttons (notes 60–63), a pattern selector (Program Change), +and the same four knobs, buttons and pattern changes out as they happen. +The contract is [`docs/midi-spec.md`](docs/midi-spec.md); the walk-through +with screenshots — connecting on macOS, Windows (rtpMIDI) and Linux, mapping +in Ableton, the per-knob sensitivity slider, and telling the panel your +computer's address so it reconnects itself after a reboot — is +[`docs/midi-ableton.md`](docs/midi-ableton.md). This is the missing half of [`docs/director-midi.md`](docs/director-midi.md): the Director's `.mid` export writes CC 20–23, so drop the clip on a MIDI track, set the track's output to the panel's port, and the show plays on the -panel from Live — the same automation the `.pfs` plays natively, now under -the DAW's transport. Ableton's *Remote* switch on the port's input lets the -panel's knobs MIDI-map to anything in the set, the way the OSC bridge does -without a Max device. Map with a couple of detents in either direction; the -panel sends a plain knob value, so there is nothing for Live to guess. - -**If a knob moves the mapped parameter too much (or too little) per turn**, -the console's **MIDI** page has a sensitivity slider per knob from ×8 (a -quarter turn sweeps a parameter) through 1:1 to 1/16 (ten turns), moved -together or one by one. It is remembered across reboots; the same page shows the four values the DAW is seeing, the -session, and the computer the panel should call on boot. - -**So you do not reconnect after every panel reboot**, tell the panel your -computer's address once — `POST http://patternflow.local/api/midi?host=192.168.0.23` -(your PC's LAN IP) — and it invites the computer itself on every boot and -whenever the session drops. rtpMIDI must allow incoming connections -(*Who may connect to me: Anyone*, the default). - -The `MIDI` row on the panel's NETWORK screen switches it off without -reflashing; `/api/status` reports the session, sensitivity and message -counts under `midi`. +panel from Live's transport. The `MIDI` row on the panel's NETWORK screen +switches it off without reflashing; `/api/status` reports the session, +sensitivity and message counts under `midi`. ## OSC — Ableton today, anything tomorrow @@ -244,6 +211,8 @@ editor everywhere, two homes for the settings. ## For the AI agent +The hard rules — the same ones humans follow — are in [`AGENTS.md`](AGENTS.md#hard-rules-do-not-violate); the contributor flow is [`CONTRIBUTING.md`](CONTRIBUTING.md). + You were pointed here to work on Patternflow's audio path. The map: **Firmware — the mapping engine and mic** diff --git a/BUILD_GUIDE.md b/BUILD_GUIDE.md index 50d6b779..8664611a 100644 --- a/BUILD_GUIDE.md +++ b/BUILD_GUIDE.md @@ -1,8 +1,8 @@ -# Patternflow v3.0.0 -- Build Guide +# Patternflow v3 -- Build Guide > **Building a v2.x board?** Use the [v2 build guide](BUILD_GUIDE_v2.md) instead — v2 and v3 parts are **not** interchangeable. -This guide walks you through building a Patternflow v3.0.0 from scratch. No prior soldering experience needed — every joint is big, forgiving through-hole, and the board was deliberately kept that simple so a first-time solderer can finish it. +This guide walks you through building a Patternflow on the v3 board from scratch. v3.9 is the current revision; a v3.0 board follows the same steps (its USB-C footprint stays unpopulated, see §2). No prior soldering experience needed — every joint is big, forgiving through-hole, and the board was deliberately kept that simple so a first-time solderer can finish it. **Estimated build time:** about 1 hour of hands-on work (~30 min soldering + ~30 min assembly), plus ~10 hours of 3D printing. Parts shipping typically takes ~2 weeks — order first, build later. @@ -57,7 +57,7 @@ Budget **US$100–200**. The lower end is the well-sourced build where nothing g Key sourcing rules (details in the BOM README): -- **LED matrix panel: the linked listing is the verified, zero-surprises path** — the case is dimensioned around that exact panel. You're free to buy a different one, but **check two things first**: ① the **driver IC** must be a classic shift-register type (74HC595, FM6124, FM6126A, ICN2037, ICN2038S, DP5125D, DP3246, MBI5124, SM162xx). S-PWM "video wall" panels — ICN2053, FM6353, FM6363C/FM6373C, MBI505x, listings leading with "3840 Hz" or a Novastar/Colorlight receiving card — stay **completely dark** no matter what you configure, and *"HUB75E" on the listing is not a compatibility promise*. The catch: **the driver IC is almost never printed in the listing**, so the practical check is to read the buyer reviews for anyone running it off an ESP32/Arduino/Raspberry Pi, then ask the seller. Read **[LED Panel Compatibility](docs/panel-compatibility.md)** before ordering — it has the review keywords, a copy-paste question for the seller, and a prompt for handing the listing to an AI assistant. ② Compare its mounting-screw positions against the case — if they differ, print the adjustable-mount version (Section 4) or **adapt the enclosure yourself** from the Blender source (`hardware/case/source/`). Verify before you buy, not after. +- **LED matrix panel: the linked listing is the verified, zero-surprises path** — the case is dimensioned around that exact panel. You're free to buy a different one, but **check two things first**: ① the **driver IC** must be one the firmware can drive. The list of chips that work and the ones that stay completely dark, the review keywords to look for, a copy-paste question for the seller and a prompt for handing the listing to an AI assistant are all in **[LED Panel Compatibility](docs/panel-compatibility.md)** — read it before ordering, because the IC is almost never printed in the listing and *"HUB75E" is not a compatibility promise*. ② Compare its mounting-screw positions against the case — if they differ, print the adjustable-mount version (Section 4) or **adapt the enclosure yourself** from the Blender source (`hardware/case/source/`). Verify before you buy, not after. - **ESP32-S3**: Espressif is the reference part, but AliExpress modules are usually fine — if yours hits the cold-boot issue, one 10k resistor fixes it ([#16](https://github.com/engmung/Patternflow/issues/16)). - **Encoders**: any 5-pin EC11 with a push switch works — the cheapest packs just fail more often. Reference part: Bourns PEC11R-4220F-S0024 (20mm shaft — print the matching knob file). @@ -279,14 +279,9 @@ No installation required — desktop **Chrome or Edge** only (Web Serial; Firefo - To wipe stored Wi-Fi credentials, enable **Tools → Erase All Flash Before Sketch Upload** before uploading (see the Wi-Fi note in §8.1). - **ArduinoOTA** works over Wi-Fi after the first join — functional, but the flasher and wired upload are the primary paths. -### Want OSC / Ableton control? Build it yourself once +### Want OSC / MIDI / Ableton control? Install the Audio edition -The stock flasher image ships with **OSC disabled at compile time** — live control (the [Ableton bridge](integrations/ableton/), or any OSC host) needs one custom build with your Wi-Fi credentials baked in: - -1. Download this repo and open `firmware/patternflow/patternflow.ino` in Arduino IDE. -2. In the same folder, copy `patternflow_secrets.example.h` → `patternflow_secrets.h`, fill in your Wi-Fi SSID/password, and enable what you want (e.g. `PF_OSC_ENABLED 1`). The file is gitignored, so your credentials stay local. -3. Set up the IDE following [`firmware/README.md`](firmware/README.md) — ESP32 board package, libraries, and the board settings above. -4. **First upload is wired**: plug the USB cable into the DevKit's port labeled **COM** (not the one labeled USB) and hit Upload. Once that first flash joins your Wi-Fi, later uploads can go wireless via ArduinoOTA. +OSC, network MIDI and audio-react ship in the **Audio** edition: open [patternflow.work/editions](https://patternflow.work/editions), pick Audio, and it installs from the browser over Wi-Fi with your patterns and settings intact — no rebuild, no secrets file, no cable. The [Ableton bridge](integrations/ableton/) and the [MIDI walk-through](docs/midi-ableton.md) start from there. (Building your own image with a different feature set is [docs/EDITIONS.md](docs/EDITIONS.md).) ## 9. Final Checks diff --git a/BUILD_GUIDE_v2.md b/BUILD_GUIDE_v2.md index 035409bd..5131e802 100644 --- a/BUILD_GUIDE_v2.md +++ b/BUILD_GUIDE_v2.md @@ -393,7 +393,7 @@ No installation required. Works on any desktop with Chrome or Edge. ### 8.2 Arduino IDE (Manual / Custom Builds) -Use this method if you want to modify the firmware source, add custom patterns, or if the browser flasher doesn't work for your setup. Custom patterns are not added to the release flasher automatically; you compile and upload your own firmware build. See [docs/assembly/firmware/custom-patterns.md](docs/assembly/firmware/custom-patterns.md) and [firmware/CUSTOM_PATTERNS.md](firmware/CUSTOM_PATTERNS.md). +Use this method if you want to modify the firmware source, add custom patterns, or if the browser flasher doesn't work for your setup. Custom patterns are not added to the release flasher automatically; you compile and upload your own firmware build. See [firmware/CUSTOM_PATTERNS.md](firmware/CUSTOM_PATTERNS.md). #### Prerequisites diff --git a/CHANGELOG.md b/CHANGELOG.md index e1166814..639c27de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,17 @@ # Changelog -All notable changes to Patternflow will be documented in this file. +All notable changes to Patternflow will be documented in this file, newest first. The story of each release, with its flashable images, is on [GitHub Releases](https://github.com/engmung/Patternflow/releases); this file is the complete record. ## [Unreleased] +### Hardware + +- **v3.9 board** (2026-09-04). The USB-C power receptacle `USB1` and its CC pull-downs `R1`/`R2` — which v3.0 shipped and every document told you to leave unpopulated after the delayed burnout in [#221](https://github.com/engmung/Patternflow/issues/221) — come off the board. `J4`, the screw terminal, is the only power input there is. Edge cuts and mounting holes are byte-identical to v3.0, so the v3 cases fit both. The 330 mm case gains a pass-through to the DevKit's USB port (data, for wired MIDI/OSC and flashing — not power) and a cable exit slot. Gerbers, BOM, renders and schematic regenerated; `bom_v3.9.csv` is the BOM source of truth. + +### Docs + +- **The repository explains itself to a contributor.** One table in `CONTRIBUTING.md` of what you have → where it goes → what CI runs on it; `docs/REPOSITORY.md` as the folder-by-folder map for people; `SUPPORT.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, `CITATION.cff` and a root `LICENSE` pointer; a README in every contributor-facing subtree (`tools/`, `integrations/`, `firmware/modules/`, `firmware/toolchain/`, both `presets/`); `docs/mqtt-spec.md` beside the other contracts; `hardware/case/remixes/` as the named home for community enclosure variants, with a one-step CI check of its READMEs. Dead pointers removed: GitHub Discussions (never enabled), pattern pull requests (patterns are published from the Pattern Lab to the Community), the `.agents/` folder, `release.yml`. Stale facts fixed in place: the assembly map and `hardware/README.md` now name the v3.9 board, `firmware/README.md` no longer teaches the console pause that ended in 3.6.3, and the Ableton bridge says "install the Audio edition" instead of "rebuild with OSC". + ## [3.10.2] - 2026-09-11 ### Fixed — pattern storage diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000..d28cd12f --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,26 @@ +cff-version: 1.2.0 +message: "If you use or write about Patternflow, please cite it as below." +type: software +title: "Patternflow: an open-source LED synthesizer" +authors: + - family-names: Lee + given-names: Seunghun + alias: engmung +repository-code: "https://github.com/engmung/Patternflow" +url: "https://patternflow.work" +abstract: >- + An open-source hardware instrument: four rotary encoders play generative + light patterns on a 128x64 LED matrix driven by an ESP32-S3. Firmware, + PCB, enclosure, web editor and pattern community are all published. + Conceived as an open-source reinterpretation of Nam June Paik's + Participation TV (1963). +keywords: + - led-matrix + - esp32 + - open-source-hardware + - creative-coding + - generative-art + - media-art +license: + - MIT + - CC-BY-SA-4.0 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..43c994c2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..b9531587 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,28 @@ +# Code of conduct + +This applies everywhere Patternflow people meet: this repository (issues, pull requests), the [Discord](https://discord.gg/Vr9QtsxeTk), and the [community site](https://community.patternflow.work/community). + +## How we treat each other + +The rule was written before there was anything to build, as a short poem, and it is the sixth section of the [manifesto](docs/manifesto.md#6-how-we-treat-each-other). In full: + +> 아무것도 몰라도, 하고자 한다면 만들 수 있습니다. +> 도와주는 게 당연합니다. 고마운 게 당연합니다. +> 말하면 더 좋고 안 해도 압니다. +> 받았으면 주고, 주는 걸 자랑스러워 합니다. +> 그렇게 우리는 연결됩니다. + +- **You don't have to be special.** Nobody arrives knowing how to do this. Wanting to make it is the qualification; a beginner's question is the normal kind, not the wrong kind. +- **Helping is ordinary, not generous.** Answering a build question is what the room is for. Condescension is not. +- **What you receive, you pass on.** Everything here was published by someone. The natural response is to publish something back. +- **Credit stays attached.** A pattern, a photo, a fix keeps its author's name. A fork carries `Based on:`. + +## What is not welcome + +Harassment, insults, slurs, unwanted sexual attention, doxxing, and sustained disruption of a conversation after being asked to stop. Also: passing off someone else's pattern, build or design as your own. + +## Reporting + +Report to the maintainer privately: a direct message on Discord, or the [private report form](https://github.com/engmung/Patternflow/security/advisories/new) on GitHub (the same private channel [SECURITY.md](SECURITY.md) uses; nobody else can read it). On the community site, every pattern and post has a report control. + +The maintainer decides what happens next, in proportion: a word, a removal, a ban. Reports are kept confidential. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b1dda16..2b6aa98a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,33 +1,59 @@ # Contributing to Patternflow -Patternflow has a set of build paths (the official PCB in a 3D-printed enclosure, breadboard electronics, other panel sizes), pattern tools, a community site, and firmware editions — and contributions to any of them. +Patternflow is a device, a firmware, a website and a pile of documentation, and people contribute to all of them. This page says where each kind of contribution goes, what checks it meets, and the few rules that are not negotiable. Where to *ask* things is [SUPPORT.md](SUPPORT.md); how we treat each other is [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md); what is where is [docs/REPOSITORY.md](docs/REPOSITORY.md). -This file is intentionally a starting point. The contribution process will become more detailed as more people build, test, and modify Patternflow. +## Where your contribution goes -## Ways to Help +| You have… | It goes in | A change touches | What CI runs on it | +| :--- | :--- | :--- | :--- | +| A fix to a guide, a README or a spec | the markdown file itself: the root guides, `docs/`, any folder's README | one file | every relative link must resolve (`docs-links`) | +| A fix to the **breadboard** build guide | `web/src/app/build/breadboard/page.tsx` — that guide is React, not markdown | JSX text | `web-ci` (`next build`) | +| Your finished build, for the [build map](https://patternflow.work/inside) | `web/src/components/sections/InsideGlobe/builds.ts` plus a photo folder `web/public/builds//` — see [Your build](#your-build). Not a git person? The [Share your build](../../issues/new?template=share_build.yml) form does the same | one `Build` entry and its photos | `web-ci` (the typecheck rejects a malformed entry) | +| A firmware **feature** | `firmware/patternflow/features//`, plus one line in the bundle it joins (`firmware/bundles//features_local.h`) | the folder, the bundle line, an inventory row in `features/README.md`; a console page also adds a row to `firmware/toolchain/console_pages.py`; a library also goes in `platformio.ini` | `firmware-checks` (boundary, ABI freeze, presets, console pages…) and `firmware-build` (every edition, from scratch) | +| A fix to the firmware **core** (`patternflow.ino`, `src/`) | the file itself — for a bug or a real improvement to the device, never to make room for a feature | | the same, and the change must still be right on a build that has no features at all | +| An enclosure or mechanical **remix** | `hardware/case/remixes/-/`, with a README that states Author, License, what it is based on, which board it fits, and the print or cut settings | one new folder; the official folders stay as they are | the README fields are checked | +| A **bridge** to host software (TouchDesigner, Node-RED, Max, a DAW…) | `integrations//` with a README, plus a row in `integrations/README.md`. It talks to the panel through the contracts in `docs/` and never imports firmware or web code | one new folder | the README has to name the contract it is built against | +| A change to a **wire contract** | [`docs/rest-api.md`](docs/rest-api.md), [`osc-spec.md`](docs/osc-spec.md), [`midi-spec.md`](docs/midi-spec.md), [`mqtt-spec.md`](docs/mqtt-spec.md), [`audio-ws-spec.md`](docs/audio-ws-spec.md), [`pfst-v2-spec.md`](docs/pfst-v2-spec.md) | the spec: bump its version line and add a Version history row | `docs-links` | +| Web app work | `web/` — start at [`web/README.md`](web/README.md) and [`web/ARCHITECTURE.md`](web/ARCHITECTURE.md) | | `web-ci`: lint, typecheck, the `check:*` suites, `next build` | +| A **pattern** | **not this repository.** Make it in the [Pattern Lab](https://patternflow.work/pattern-lab), press **Publish**, and it is on the [Community](https://community.patternflow.work/community) for every Patternflow to play, under the license you choose. [PATTERN_GUIDE.md](PATTERN_GUIDE.md) walks the loop. The presets bundled in the firmware and the Basics pack are curated by the maintainer from the repository's own sources | | | -- Build Patternflow and share photos, problems, and notes. -- Improve the build documentation when a step is unclear. -- Test the alternate build paths — breadboard electronics, other panel sizes, other printers — and report what breaks. -- Suggest better sourcing options for parts. -- Share custom patterns with the community. -- Report firmware, web, or documentation issues. +Parts sourcing tips, better photos of a build step, a clearer sentence: all of those are the first row. -## Pattern Contributions +## Sending it -Custom patterns are welcome as community work, but official bundled firmware patterns will be curated for now. If you make something interesting, share it in the Discord **patterns** channel (follow the post guidelines there) or open an issue with the source. +1. Fork the repository and branch from `dev` (`fix/…`, `feat/…`, `docs/…`). +2. Commit with the area first, then a short present-tense summary — see [Commit messages](#commit-messages). +3. Open the pull request **against `main`**. `dev` is the maintainer's working branch; outside work is reviewed and merged into `main`, and `main` is pulled back into `dev` afterwards. +4. Fill in the template: what, why, how you tested it, and which row of the table above it is. The checks in that row run on every pull request, forks included, with no secrets involved. +5. Nobody pushes to `main` directly, the maintainer included, so a one-word typo fix is also a pull request. It is a small one. -**Licensing — inbound = outbound.** By sending a pattern *to this repository* (Discord, issue, or PR) you agree to license it under **CC-BY-SA 4.0** — the same commons as the rest of Patternflow — with attribution kept in the code header (`// Author:` and `// SPDX-License-Identifier: CC-BY-SA-4.0`). There is no copyright assignment (no CLA): you keep authorship, and the project just gets the right to bundle and redistribute it. You may set a different license in the header as long as it still lets the project bundle and redistribute the pattern. +## Before you open a firmware pull request -**Publishing to the Community is a different thing.** Patterns posted to the -[Community site](https://community.patternflow.work/community) are not repository -contributions — you pick their license yourself when you publish (CC-BY-SA 4.0 -by default, or CC-BY 4.0), and nothing here applies to them. See the -[License Summary](docs/LICENSE-SUMMARY.md). +- **Read, in this order:** [`docs/EDITIONS.md`](docs/EDITIONS.md) (what a feature and an edition are, and the promises every build keeps) → [`firmware/patternflow/features/README.md`](firmware/patternflow/features/README.md) (the hooks) → [`firmware/bundles/README.md`](firmware/bundles/README.md) (the two files that make an edition). [FEATURE_GUIDE.md](FEATURE_GUIDE.md) is the walk-through, written so an AI coding agent can follow it too. +- **The core names no feature.** No `#include`, no `#if PF__ENABLED`, not even the feature's name in a string on the panel. If your change to `src/` would be wrong on a build without your feature, it belongs in `features/`. `firmware/toolchain/check_boundaries.py` enforces this in CI. +- **A feature without a bundle line compiles nowhere.** Name the edition it joins in the pull request, or propose a new one. +- **Run before pushing:** `./firmware/bundles/build.sh all` (every edition, and a scan of each image for its feature markers) and `python firmware/toolchain/check_boundaries.py`. Touching the core means building every composition; the default build has no features to break. +- **Explore in a fork first.** Features need room to be wrong for a while. Once it runs on a panel, an in-tree pull request that touches only `features//` and a bundle line is welcome. + +## The rules that are not negotiable + +The full list, with the reasons, is [`AGENTS.md`](AGENTS.md#hard-rules-do-not-violate) — written for AI coding agents, but the rules are the project's, not the agents'. In short: + +- **The board has one power input, `J4`, the screw terminal.** Never describe USB-C as a power option; a board powered that way smoked at a connector pin after twenty minutes. +- **`hardware/bom/bom_v3.9.csv` is the BOM source of truth.** The parts table in `BUILD_GUIDE.md` and `hardware/pcb/schematic.pdf` must match it; change one, check the other two. +- **Two licenses, strictly split.** Code is MIT, hardware and docs are CC BY-SA 4.0, and the SPDX header in a file is the authority where one exists. Don't merge the license files. +- **Brand naming.** "Patternflow" in prose, "PATTERNFLOW" engraved on hardware, `patternflow` in filenames and URLs. +- **The firmware core is not the place to put a feature.** See above. + +## Licensing — inbound = outbound + +What you send is licensed the way the place it lands in is licensed: code under [MIT](LICENSE-MIT), hardware files, guides and docs under [CC BY-SA 4.0](LICENSE-CC-BY-SA). You keep your copyright and your name; the project gets the right to bundle and redistribute. There is no CLA. Code files carry an SPDX line (and an `// Author:` line if it is yours); a remix folder or a bridge states its license in its README, because STL and DXF files cannot carry a header. The whole table is [docs/LICENSE-SUMMARY.md](docs/LICENSE-SUMMARY.md). + +Patterns published to the Community are a different thing: you pick their license when you publish (CC BY-SA 4.0 by default, or CC BY 4.0), and nothing on this page applies to them. ## Commit messages -Keep it simple: start with the area, then a short summary in plain present tense. +Start with the area, then a short summary in plain present tense. ``` area: short summary @@ -35,44 +61,16 @@ area: short summary web: collapse the preset list on mobile firmware: fix reversed encoder direction docs: clarify the breadboard wiring step +hardware: knob plate for 18 mm shafts +integrations: node-red example flow ``` -Common areas: `web`, `firmware`, `pcb`, `enclosure`, `docs`, `pattern`. Use -`wip(area): …` for work that isn't finished yet. That's the whole rule — no -tooling enforces it, it just keeps the history (and the Discord dev-log) -readable. - -## Development workflow - -Work happens on `dev`, then lands on `main` through a pull request. - -``` -work on dev → commit early and often → open a PR into main → merge -``` - -- **Commit freely on `dev`.** Commits are cheap save points; small and frequent - is good. Keep throwaway `wip:` commits here rather than on `main`. -- **Code changes go through a PR** (anything under `web/` or `firmware/`). A PR - runs CI (`web/` PRs must pass `next build`) and groups the change into one - clean Discord dev-log post. Trivial doc/typo fixes can be committed directly. -- **Bigger or riskier work** gets its own branch (`feat/…`, `fix/…`) off `dev`. -- **After a PR merges**, pull `main` back into `dev` so the branches don't - drift apart over time: - - ``` - git checkout dev && git merge origin/main && git push origin dev - ``` - -Releases are published from `main` (GitHub → Releases → *Generate release -notes*), which posts an announcement to Discord. +Use `wip(area): …` for work that isn't finished yet. Nothing enforces this; it keeps the history and the Discord dev-log readable. -## Project Rules +## Your build -Project rules and guidelines are currently under consideration and will be finalized in the future. +A pin on the map is one entry in [`web/src/components/sections/InsideGlobe/builds.ts`](web/src/components/sections/InsideGlobe/builds.ts) and a folder of photos in `web/public/builds//`. The type at the top of that file documents every field; the comments next to the fields say how precise a location to give and what a `collaboration` is. The `slug` becomes the pin's URL (`/inside/`), so pick it once. Photos you add are CC BY-SA 4.0 unless the entry says otherwise. If you would rather not touch the file, the [Share your build](../../issues/new?template=share_build.yml) form asks for the same things and the maintainer files it. -## Where to Start +## How the maintainer works -- Assembly map: [docs/assembly/README.md](docs/assembly/README.md) -- Current full build guide: [BUILD_GUIDE.md](BUILD_GUIDE.md) -- Custom patterns: [firmware/CUSTOM_PATTERNS.md](firmware/CUSTOM_PATTERNS.md) -- Discord: [discord.gg/Vr9QtsxeTk](https://discord.gg/Vr9QtsxeTk) +The `dev` → `main` routine, release cutting and what the workflows attach live in [docs/RELEASING.md](docs/RELEASING.md). Governance is one person: the maintainer decides what merges, in the open, in the pull request. Two collaborators have write access to the areas they built. diff --git a/FEATURE_GUIDE.md b/FEATURE_GUIDE.md index 3e23af84..1fbf221f 100644 --- a/FEATURE_GUIDE.md +++ b/FEATURE_GUIDE.md @@ -70,6 +70,8 @@ that doesn't include it exactly nothing. ## For the AI agent +The hard rules — the same ones humans follow — are in [`AGENTS.md`](AGENTS.md#hard-rules-do-not-violate); the contributor flow is [`CONTRIBUTING.md`](CONTRIBUTING.md). + You were pointed here to build a Patternflow firmware feature. Read this section fully before writing code. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..e1ad4a48 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Patternflow is dual-licensed by content type. This file is a pointer so the +repository page shows both; the two license texts are next to it. + + Code - firmware/, web/, tools/, integrations/ MIT -> LICENSE-MIT + Works - hardware/, docs/, the build guides, and the bundled patterns + (web/src/lib/presets/, firmware/patternflow/presets/) + CC BY-SA 4.0 -> LICENSE-CC-BY-SA + +An SPDX-License-Identifier header inside a file is the authority for that file; +folders are not license boundaries. Files that cannot carry a header (STL, DXF, +Gerber, images) take the license the nearest README states. Patterns published +to the Community site are licensed by their authors. The full table, and what +each license lets you do, is docs/LICENSE-SUMMARY.md. diff --git a/README.md b/README.md index 25ddb9d8..0681b7a1 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ The clearest case started on the other side of the world. A media art collective The **[Full Build Guide](BUILD_GUIDE.md)** covers the official route: the custom PCB and a 3D-printed enclosure. Don't want to order a board? The **[Breadboard Build Guide](https://patternflow.work/build/breadboard)** wires the same electronics with jumper wires instead, and that's a real Patternflow, not a temporary prototype. Every other combination is on the **[Assembly Map](docs/assembly/README.md)**. Parts run about US$100–200 ([BOM](BUILD_GUIDE.md#1-bill-of-materials-bom)), it's all big through-hole joints, and every first-timer who has built one finished it. Most came back saying the soldering was the fun part. -**When yours lights up, tell us.** Post it in [Discord](https://discord.gg/Vr9QtsxeTk) or [Discussions](../../discussions) and it goes on the build map, a globe of Patternflows where each pin carries its build's story. The map is for the ones people made themselves: every pin is someone who built one from these files, in their own material, wherever they are. The goal is simple: cover it with pins. +**When yours lights up, tell us.** Post it in [Discord](https://discord.gg/Vr9QtsxeTk) or fill in the [Share your build](../../issues/new?template=share_build.yml) form and it goes on the build map, a globe of Patternflows where each pin carries its build's story. The map is for the ones people made themselves: every pin is someone who built one from these files, in their own material, wherever they are. The goal is simple: cover it with pins.

Build map: a globe of Patternflows built around the world, with the story of every build @@ -85,14 +85,12 @@ Patterns don't stay on the wall, either. New pattern studies go up on **[Instagr Patterns are the surface. Underneath is an instrument still being designed, in the open, by whoever shows up. This is the whole system on one napkin:

- Hand-drawn map of the Patternflow ecosystem: the device, the GitHub files and Crowd Supply routes to it; patternflow.work with the Live Editor, Pattern Lab and Community wall; Discord, Instagram and GitHub Discussions; and the Workshop, where project talk is gathering + Hand-drawn map of the Patternflow ecosystem: the device, the GitHub files and Crowd Supply routes to it; patternflow.work with the Live Editor, Pattern Lab and Community wall; Discord, Instagram and GitHub

-The **[Workshop](https://community.patternflow.work/community/workshop)** is where the project's future is worked out. It's a map of directions Patternflow could take: a wired OSC version, laser-cut enclosures, bigger panels. Anyone can pin themselves to a direction, say what they're working on, and start a thread. - It's already moving in directions I didn't choose. One contributor is building out MQTT further than I've had time to follow: units reaching each other across a network, one person's playing coming out of somebody else's device. Another is working on sound on the board itself. Another is taking the TouchDesigner link further than I did. None of it was assigned. -**[CONTRIBUTING.md](CONTRIBUTING.md)** covers how contributions flow. Questions and ideas go to **[Discussions](../../discussions)** or the **[Discord](https://discord.gg/Vr9QtsxeTk)**, whichever you can reach. +**[CONTRIBUTING.md](CONTRIBUTING.md)** covers how contributions flow. Questions and ideas go to the **[Discord](https://discord.gg/Vr9QtsxeTk)**; where to ask what is **[SUPPORT.md](SUPPORT.md)**. ## Quick facts @@ -104,7 +102,7 @@ It's already moving in directions I didn't choose. One contributor is building o | **Power** | 5 V over USB from any power bank; about **4 h per 10,000 mAh** at max brightness with a typical pattern (see [runtime](#power--runtime)) | | **Size / weight** | 245 × 325 × 36 mm (9.6 × 12.8 × 1.4 in) · 933 g (2.06 lb) | | **Firmware** | Arduino-compatible C++, modular pattern architecture, runtime switching (no reflash) | -| **Flashing** | Everything from the browser: one USB flash the first time, then it's all Wi-Fi. Patterns install as modules in seconds, full firmware builds land wirelessly too. Arduino IDE only for firmware development or other matrix resolutions | +| **Flashing** | Everything from the browser: one USB flash the first time, then it's all Wi-Fi. Patterns install as modules in seconds, full firmware builds land wirelessly too. A local build (PlatformIO) only for firmware development or other matrix resolutions | | **Connectivity** | Wi-Fi and USB. Network MIDI (a MIDI port in any DAW — Ableton, Logic, Bitwig), bidirectional OSC (Max/TouchDesigner/Resolume), MQTT and audio-react each ship in an [edition](docs/EDITIONS.md) you install in one click — the way patterns do — and switching keeps your patterns, networks and settings | | **Editions** | One image ships on the board; others are a click away on [the shelf](https://patternflow.work/editions). Writing a feature or cutting your own firmware starts at **[docs/EDITIONS.md](docs/EDITIONS.md)** | | **Build** | ~1 h hands-on (≈30 min soldering + ≈30 min assembly, first-build friendly) + ~10 h 3D printing · US$100–200 in parts ([BOM](BUILD_GUIDE.md#1-bill-of-materials-bom)) | @@ -151,7 +149,7 @@ A new board boots into **Origin**, concentric sine waves sampled by an emergent A new board is therefore nearly empty, so a set ships with it: the **Basics pack**, 33 patterns, at the top of the [decks shelf](https://community.patternflow.work/community/decks). One click installs the lot, no account and no build queue, or drop the `.zip` on your board's Patterns page yourself. The [Live Editor](https://patternflow.work/pattern) opens with its own preset library of 42 patterns, each loadable and remixable in the browser. -The Arduino IDE is only needed for firmware feature development or targeting an LED matrix with a different resolution; see [`firmware/patternflow/README.md`](firmware/patternflow/README.md) and [Custom Patterns](firmware/CUSTOM_PATTERNS.md). To rebuild the shipped pack from the repo's own preset sources, see [`firmware/toolchain/make_pack.py`](firmware/toolchain/make_pack.py). +A local firmware build (PlatformIO, driven by `firmware/bundles/build.sh`) is only needed for feature development or targeting an LED matrix with a different resolution; the setup is [`firmware/README.md`](firmware/README.md) and a new feature starts at [FEATURE_GUIDE.md](FEATURE_GUIDE.md). Patterns never need it: [Custom Patterns](firmware/CUSTOM_PATTERNS.md). To rebuild the shipped pack from the repo's own preset sources, see [`firmware/toolchain/make_pack.py`](firmware/toolchain/make_pack.py). ## MIDI, OSC, MQTT & audio-react @@ -165,7 +163,7 @@ Each of these lives in an **edition** — a firmware you install from [the shelf **Bidirectional OSC.** Over Wi-Fi, Patternflow speaks OSC in both directions: knob turns, button presses, and pattern switches stream out to a remote host (Ableton Live, Max/MSP, TouchDesigner, anything that speaks OSC), and incoming OSC messages drive the device exactly like physical encoder motion. Play Patternflow as a controller for your set, let your set drive the light, or both at once. OSC is the language of Max, TouchDesigner, Resolume and Processing, and it carries more than MIDI can — pattern names, floats, a heartbeat. For Ableton Live Suite there's a ready-made Max for Live bridge in [`integrations/ableton`](integrations/ableton). Click Connect, map the four knobs to any Live parameters, done. The wire protocol is documented in [`docs/osc-spec.md`](docs/osc-spec.md). -**MQTT.** Patternflow also speaks MQTT, both ways, on the broker you already run. Knob turns and pattern changes publish as they happen; messages coming the other way move the knobs and switch patterns exactly as a hand on the encoder would. Two boards pointed at the same broker follow each other, which is the short version of why it exists. It also puts the device on the same bus as the rest of a home or venue setup, so Home Assistant, Node-RED or a lighting desk can drive it without anything Patternflow-specific in the middle. Point it at a broker on the device's own **MQTT** page; a pattern can be addressed by name or by slug. Publishing `1` to `/sleep` puts the panel to sleep and `0` wakes it, with the current state mirrored on `/sleep/state`. That is enough for a Home Assistant switch, and it is the one command a panel obeys whether it is set to Publisher or Subscriber. Contributed by **[@SimonePDA](https://github.com/SimonePDA)** (Simone Majocchi), along with the browser-side zip unpacking that makes pattern packs install in one click. +**MQTT.** Patternflow also speaks MQTT, both ways, on the broker you already run ([the contract](docs/mqtt-spec.md)). Knob turns and pattern changes publish as they happen; messages coming the other way move the knobs and switch patterns exactly as a hand on the encoder would. Two boards pointed at the same broker follow each other, which is the short version of why it exists. It also puts the device on the same bus as the rest of a home or venue setup, so Home Assistant, Node-RED or a lighting desk can drive it without anything Patternflow-specific in the middle. Point it at a broker on the device's own **MQTT** page; a pattern can be addressed by name or by slug. Publishing `1` to `/sleep` puts the panel to sleep and `0` wakes it, with the current state mirrored on `/sleep/state`. That is enough for a Home Assistant switch, and it is the one command a panel obeys whether it is set to Publisher or Subscriber. Contributed by **[@SimonePDA](https://github.com/SimonePDA)** (Simone Majocchi), along with the browser-side zip unpacking that makes pattern packs install in one click. **Audio-react.** The Chrome/Edge extension in [`tools/patternflow-audio-extension`](tools/patternflow-audio-extension) captures the current tab's audio, splits it into four bands you shape on a response graph, and sends the levels to the panel over a WebSocket. A pattern reads them as ordinary parameters, so every encoder-driven pattern reacts with no audio code of its own. The panel can also listen for itself: an on-board microphone runs the same analysis on the device, with no browser involved. @@ -181,18 +179,22 @@ Patternflow is built around a standalone ESP32-S3 driving a HUB75 RGB LED matrix | :--- | :--- | | `firmware/` | Arduino code for ESP32-S3, the custom pattern template, and the toolchain that builds patterns into `.pfm` modules and packs | | `hardware/` | Enclosure files and electronics source files (case, PCB, Gerbers, schematic PDF) | -| `web/` | Next.js site (landing, Live Editor, Pattern Lab, community, browser flasher & build server, journal) | -| `docs/` | Assembly map, build-guide media, manifesto, license summary | +| `web/` | Next.js site (landing, Live Editor, Pattern Lab, community, browser flasher & build server, journal); the breadboard build guide is a React page here | +| `docs/` | The contracts (HTTP, OSC, MIDI, MQTT, audio WebSocket, show files), how the firmware is put together (`EDITIONS.md`), the assembly map, walk-throughs, records and media — indexed in [`docs/README.md`](docs/README.md), with the folder-by-folder map in [`docs/REPOSITORY.md`](docs/REPOSITORY.md) | | `tools/` | Desktop-side helpers, including the audio-react browser extension | | `integrations/` | Host-software bridges: Ableton Live / Max for Live (OSC knob mapping) | +| `.github/` | Issue and PR templates, and the CI that runs on every pull request (web, every firmware edition, doc links, console pages) | -**Docs:** [Full Build Guide](BUILD_GUIDE.md) · [Pattern Guide](PATTERN_GUIDE.md) · [Audio Guide](AUDIO_GUIDE.md) · [Feature Guide](FEATURE_GUIDE.md) · [Assembly Map](docs/assembly/README.md) · [Custom Patterns](firmware/CUSTOM_PATTERNS.md) · [HTTP API](docs/rest-api.md) · [MIDI in Ableton](docs/midi-ableton.md) · [MIDI Spec](docs/midi-spec.md) · [OSC Spec](docs/osc-spec.md) · [Director → MIDI](docs/director-midi.md) · [Manifesto](docs/manifesto.md) · [Changelog](CHANGELOG.md) · [License Summary](docs/LICENSE-SUMMARY.md) +**Build:** [Full Build Guide](BUILD_GUIDE.md) · [Assembly Map](docs/assembly/README.md) · [Panel Compatibility](docs/panel-compatibility.md) · [Hardware files](hardware/README.md) +**Play:** [Pattern Guide](PATTERN_GUIDE.md) · [Custom Patterns](firmware/CUSTOM_PATTERNS.md) · [Audio Guide](AUDIO_GUIDE.md) · [MIDI in Ableton](docs/midi-ableton.md) +**Extend:** [Feature Guide](FEATURE_GUIDE.md) · [Editions](docs/EDITIONS.md) · [Firmware build](firmware/README.md) · [Web architecture](web/ARCHITECTURE.md) · [HTTP API](docs/rest-api.md) · [OSC Spec](docs/osc-spec.md) · [MIDI Spec](docs/midi-spec.md) · [Director → MIDI](docs/director-midi.md) +**Project:** [Contributing](CONTRIBUTING.md) · [Support](SUPPORT.md) · [Repository map](docs/REPOSITORY.md) · [Changelog](CHANGELOG.md) · [License Summary](docs/LICENSE-SUMMARY.md) · [Manifesto](docs/manifesto.md) **Links:** [patternflow.work](https://patternflow.work) · [Community](https://community.patternflow.work/community) · [Crowd Supply](https://www.crowdsupply.com/engmung/patternflow) · [Releases](../../releases) · [Discord](https://discord.gg/Vr9QtsxeTk) · [Instagram](https://www.instagram.com/patternflow.work) ## Contributing -Builds, documentation fixes, part sourcing tips, and custom patterns are all welcome. **[CONTRIBUTING.md](CONTRIBUTING.md)** covers how contributions flow, including the inbound = outbound pattern licensing. +Builds, documentation fixes, part sourcing tips, enclosure remixes, firmware features and host-software bridges are all welcome. **[CONTRIBUTING.md](CONTRIBUTING.md)** has one table of where each kind goes and what CI runs on it; **[SUPPORT.md](SUPPORT.md)** says where to ask. Patterns don't come here: publish them from the Pattern Lab to the Community. ## Story so far @@ -223,7 +225,7 @@ Patternflow's PCB fabrication and 3D-printed enclosure are sponsored by **[PCBWa ## License -The SPDX header inside a file is the authority; folders are not license boundaries. Full breakdown in the **[License Summary](docs/LICENSE-SUMMARY.md)**. +A file's SPDX header is the authority where one exists; folders are not license boundaries, and files that cannot carry a header (STL, Gerber, images) take the license the nearest README states. Full breakdown in the **[License Summary](docs/LICENSE-SUMMARY.md)**. - Firmware & web code: **MIT** ([LICENSE-MIT](./LICENSE-MIT)) - Hardware, designs & docs: **CC-BY-SA 4.0** ([LICENSE-CC-BY-SA](./LICENSE-CC-BY-SA)) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..98915fe8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,20 @@ +# Security + +## Reporting + +Please report privately through [GitHub's report form](https://github.com/engmung/Patternflow/security/advisories/new) (Security → Report a vulnerability), or by direct message to the maintainer on [Discord](https://discord.gg/Vr9QtsxeTk). Do not open a public issue for something exploitable. You will get a reply within a few days; there is no bounty. + +## What is in scope + +- **The community site** ([community.patternflow.work](https://community.patternflow.work)): accounts and sessions, uploads, the sandboxed iframe that runs published patterns, moderation. +- **The browser flasher and update handoff** ([patternflow.work/flash](https://patternflow.work/flash), `/update`): the firmware images it serves and where they come from. +- **The build worker** that turns a pattern into a `.pfm` module on the community host. +- **Published firmware images.** They must not carry credentials. Three early releases did carry the maintainer's Wi-Fi credentials, which is why the release workflow now refuses an image built with `patternflow_secrets.h`. If you find one that slipped through, that is a report. + +## What is a documented trust model, not a vulnerability + +The device's HTTP server on the LAN has **no authentication**: anyone on the same network can call any `/api/*` route, including `POST /update`. This is deliberate and documented in [docs/rest-api.md](docs/rest-api.md#transport) — the panel is an instrument on a home or venue network, with the same posture as ArduinoOTA's default. `PF_WEBUPDATE_ALWAYS_ARMED 0` is the one lever that narrows it. Reports that amount to "the LAN API is open" will be closed with a pointer here; reports of a way to reach that API from *outside* the LAN, or from a web page on another origin, are in scope. + +## Supported versions + +Only the images currently on [the shelf](https://patternflow.work/editions) and in the flasher manifest are supported. Older images are on their release tags and are not patched. Each release lists the `sha256` of every image it attaches; verify a download against it before flashing. diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 00000000..50150f75 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,17 @@ +# Getting help + +One table for "where do I ask". Issues are for things that change this repository; everything else has a faster place. + +| I want to… | Go to | +| :--- | :--- | +| Get unstuck building, soldering or flashing | [Discord → hardware-help](https://discord.com/channels/1497757947827327067/1499907910707187833) — photos and quick back-and-forth | +| Show a pattern I made, or get feedback on one | Publish it from the [Pattern Lab](https://patternflow.work/pattern-lab) to the [Community](https://community.patternflow.work/community), then [Discord → patterns](https://discord.com/channels/1497757947827327067/1499908302962819236) | +| Put my finished build on the [build map](https://patternflow.work/inside) | [Share your build](https://github.com/engmung/Patternflow/issues/new?template=share_build.yml) — or post it in Discord | +| Talk about where the project should go, or find people working on the same direction | [Discord](https://discord.gg/Vr9QtsxeTk) — the dev-log and the project channels | +| Report a bug, a wrong or missing doc, or propose a feature | [New issue](https://github.com/engmung/Patternflow/issues/new/choose) — pick the template | +| Report a security problem | [SECURITY.md](SECURITY.md) — privately, not as an issue | +| Contribute code, hardware files or docs | [CONTRIBUTING.md](CONTRIBUTING.md) | + +**If Discord is blocked where you live**, the community site still works for patterns and decks without it, and a bug or a documentation issue here reaches the maintainer the same day. A discussion venue that works everywhere is planned; until it exists, an issue is the fallback for a question too. + +There is no support email. Replies come from the maintainer and from other builders, usually within a day or two. diff --git a/docs/EDITIONS.md b/docs/EDITIONS.md index ce701260..687e567f 100644 --- a/docs/EDITIONS.md +++ b/docs/EDITIONS.md @@ -449,4 +449,4 @@ is in [`investigations/2026-08-the-wifi-portal-a-samsung-never-drew.md`](investi | cut an edition | [`bundles/README.md`](../firmware/bundles/README.md) — the two files, in full | | know why any of this | [RFC](rfc-core-and-variants.md) §2.13, §2.14, §2.15 | | talk to a panel | [`rest-api.md`](rest-api.md) | -| write a pattern | [`firmware/README.md`](../firmware/README.md) | +| write a pattern | [`PATTERN_GUIDE.md`](../PATTERN_GUIDE.md) — the Pattern Lab route; [`firmware/CUSTOM_PATTERNS.md`](../firmware/CUSTOM_PATTERNS.md) for the hands-on one | diff --git a/docs/LICENSE-SUMMARY.md b/docs/LICENSE-SUMMARY.md index 22df6dff..6a7d2a64 100644 --- a/docs/LICENSE-SUMMARY.md +++ b/docs/LICENSE-SUMMARY.md @@ -1,7 +1,8 @@ # License Summary -**The SPDX header inside a file is the authority.** Folders are not license -boundaries — the pattern presets live under `web/` and `firmware/`, which are +**Where a file carries an SPDX header, that header is the authority.** Files that +cannot carry one (STL, DXF, Gerber, images) take the license the nearest README +states. Folders are not license boundaries — the pattern presets live under `web/` and `firmware/`, which are otherwise MIT, and each preset file carries its own `SPDX-License-Identifier: CC-BY-SA-4.0`. That header wins. This page describes the layout; it does not override it. @@ -10,9 +11,12 @@ override it. |---|---|---| | Firmware code (`firmware/`) | MIT | Use, modify, distribute freely. Keep the copyright notice. | | Web code (`web/`) | MIT | Same as above. | +| Tools and integrations (`tools/`, `integrations/`) | MIT | Same as above. A `.maxpat`, `.tox` or flow file that cannot carry a header takes the license its folder's README states. | | Hardware designs (`hardware/`) | CC BY-SA 4.0 | Modify and share — derivatives use the same license, and credit the author. | | Docs & build guides (`docs/`, `BUILD_GUIDE*.md`) | CC BY-SA 4.0 | Same as above. | | Journal (`web/content/journal/`) | CC BY-SA 4.0 | Same as above. | +| Enclosure remixes (`hardware/case/remixes/`) | Stated in each remix's README (CC BY-SA 4.0 by default) | The README is the license header for files that cannot carry one. | +| Build-map photos (`web/public/builds/`) | CC BY-SA 4.0 unless the build's entry says otherwise | Credit the maker. | | **Bundled patterns** (`web/src/lib/presets/`, `firmware/patternflow/presets/`) | CC BY-SA 4.0 | Per-file SPDX headers. They sit in code folders but they are artwork, not code. | | **Community patterns** | **Chosen by their author** — see below | Read the header in the pattern itself. | @@ -45,8 +49,9 @@ These are different things and are governed by different documents. | | Repository contribution | Community publishing | |---|---|---| -| Where | GitHub PR / issue / Discord | The community site | -| License | Inbound = outbound (CC BY-SA 4.0) | The author's choice, above | +| Where | GitHub PR / issue | The community site, from the Pattern Lab | +| What | Code, hardware files, docs, build-map entries | Patterns | +| License | Inbound = outbound: MIT for code, CC BY-SA 4.0 for the rest | The author's choice, above | | Governed by | [CONTRIBUTING.md](../CONTRIBUTING.md) | Terms of use *(not yet written)* | ## Trademark diff --git a/docs/README.md b/docs/README.md index b59ea4c8..096215e3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,37 +1,50 @@ # docs/ -What is here, and what is not. The rule for the split: **a guide somebody follows start to finish lives at the repository root** (`BUILD_GUIDE.md`, `BUILD_GUIDE_v2.md`, `PATTERN_GUIDE.md`, `FEATURE_GUIDE.md`, `AUDIO_GUIDE.md`); **a contract, a reference or a record lives here.** +What is here, and what is not. The rule for the split: **a guide somebody follows start to finish lives at the repository root** (`BUILD_GUIDE.md`, `BUILD_GUIDE_v2.md`, `PATTERN_GUIDE.md`, `FEATURE_GUIDE.md`, `AUDIO_GUIDE.md`); **a contract, a reference, a walk-through for one task, or a record lives here**; and a folder explains itself in its own README. The folder-by-folder map of the whole repository is [`REPOSITORY.md`](REPOSITORY.md). ## Contracts — other software is built against these, not against the firmware source -- [`rest-api.md`](rest-api.md) — the device's HTTP API (`/api/*`), the console pages, and how to choose between HTTP, OSC and MQTT +- [`rest-api.md`](rest-api.md) — the device's HTTP API (`/api/*`), the console pages, and the table for choosing between HTTP, OSC, MIDI and MQTT - [`osc-spec.md`](osc-spec.md) — OSC over UDP (DAWs, Max, TouchDesigner) - [`midi-spec.md`](midi-spec.md) — the panel as a network MIDI port +- [`mqtt-spec.md`](mqtt-spec.md) — MQTT topics, both directions, the roles, and why the channel decides whether a write sticks - [`audio-ws-spec.md`](audio-ws-spec.md) — the audio-react WebSocket the browser extension and the phone app speak - [`pfst-v2-spec.md`](pfst-v2-spec.md) — the `.pfs` show table, with test vectors in [`pfst-v2-vectors/`](pfst-v2-vectors/) -- [`panel-compatibility.md`](panel-compatibility.md) — which HUB75 panels work, and what to change for other sizes +- [`panel-compatibility.md`](panel-compatibility.md) — a buying guide first (which HUB75 panels light up and which stay dark), then the reference for other sizes ## How the firmware is put together - [`EDITIONS.md`](EDITIONS.md) — features, compositions, editions: the seam, the rule that the core names no feature, the vocabulary. **Read before touching firmware.** -- [`rfc-core-and-variants.md`](rfc-core-and-variants.md) and its [progress log](rfc-core-and-variants-progress.md) — the 2026-08 RFC that produced the seam. Historical: it says *addon* and *variant* where the tree says *feature* and *edition*. +- [`rfc-core-and-variants.md`](rfc-core-and-variants.md) and its [progress log](rfc-core-and-variants-progress.md) — the 2026-08 RFC that produced the seam. Historical: it says *addon* and *variant* where the tree says *feature* and *edition*, and its listing rules have been superseded by `EDITIONS.md`. + +## Walk-throughs — one task, start to finish + +- [`midi-ableton.md`](midi-ableton.md) — the panel as a MIDI port in Ableton Live, with screenshots - [`director-midi.md`](director-midi.md) — how a Director show becomes a MIDI clip -- [`midi-ableton.md`](midi-ableton.md) — the Ableton walk-through +- [`assembly/`](assembly/README.md) — the assembly map: which enclosure with which electronics, and where each route is documented +- [`../firmware/CUSTOM_PATTERNS.md`](../firmware/CUSTOM_PATTERNS.md) — the hands-on pattern route (the everyday one is [`PATTERN_GUIDE.md`](../PATTERN_GUIDE.md)) +- [`../firmware/patternflow/console/README.md`](../firmware/patternflow/console/README.md) — editing or adding a device console page ## Building it -- [`assembly/`](assembly/README.md) — the assembly map: electronics paths, enclosure paths, firmware. The full guide is [`../BUILD_GUIDE.md`](../BUILD_GUIDE.md). +- [`assembly/`](assembly/README.md) — the map. The full guide is [`../BUILD_GUIDE.md`](../BUILD_GUIDE.md); the files to order are described in [`../hardware/README.md`](../hardware/README.md) - [`build-guide/images/`](build-guide/images/) — photos for the build guides (`images/` is the v2 set, `images/v3/` the current board) -- [`LICENSE-SUMMARY.md`](LICENSE-SUMMARY.md) — MIT for code, CC-BY-SA 4.0 for hardware and bundled patterns, and what that means for a pattern you publish +- [`LICENSE-SUMMARY.md`](LICENSE-SUMMARY.md) — MIT for code, CC BY-SA 4.0 for hardware, docs and bundled patterns, and what that means for a pattern you publish or a remix you share + +## Contributing + +- [`../CONTRIBUTING.md`](../CONTRIBUTING.md) — one table: what you have → where it goes → what CI runs on it +- [`../SUPPORT.md`](../SUPPORT.md) — where to ask what +- [`REPOSITORY.md`](REPOSITORY.md) — the folder-by-folder map, and the list of files that outside things link to ## Running the project -- [`RELEASING.md`](RELEASING.md) — cutting a release: version, changelog, tag, what the workflows attach +- [`RELEASING.md`](RELEASING.md) — the `dev` → `main` routine, cutting a release, what the workflows attach - [`SERVICES.md`](SERVICES.md) — the production hosts (community, build worker) and their systemd units. Korean. ## Records - [`investigations/`](investigations/) — dated post-mortems -- [`releases/`](releases/) — long-form notes for particular releases; `CHANGELOG.md` at the root is the complete record -- [`manifesto.md`](manifesto.md) — why Patternflow exists +- [`releases/`](releases/) — long-form notes for three July 2026 releases, kept as written; notes since 3.2 are on [GitHub Releases](https://github.com/engmung/Patternflow/releases), and `CHANGELOG.md` at the root is the complete record +- [`manifesto.md`](manifesto.md) — why Patternflow exists, and (section 6) how we treat each other - [`images/`](images/), [`media/`](media/) — assets the guides and the site reference diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 56e7072d..9404ff17 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -43,7 +43,7 @@ What the two commands do, step by step - and the way to do it by hand. 2. Bump the version the firmware reports: `PF_IMPROV_FW_VERSION` in `firmware/patternflow/net_config.h` (written `X.Y.Z`, no `v`). `shelf.sh` refuses a core image whose define disagrees with the version it is being shelved as. 3. Turn `CHANGELOG.md`'s `[Unreleased]` into `## [X.Y.Z] - YYYY-MM-DD` and open a fresh `[Unreleased]` above it. 4. Update the version the docs claim: the "current" line in `AGENTS.md`, the *Moving fast* note in `README.md`, and any guide that names a release. -5. If hardware changed: confirm `BUILD_GUIDE.md`'s parts table, `hardware/bom/bom_v*.csv` and the schematic agree. +5. If hardware changed: confirm `BUILD_GUIDE.md`'s parts table, `hardware/bom/bom_v*.csv` and the schematic agree; regenerate the Gerber zip, renders and schematic exports with the recipe in `hardware/pcb/README.md`; add a `### Hardware` entry to the changelog section; update the board table in `hardware/README.md` and the version line in `docs/assembly/README.md`; attach the Gerber zip, the BOM CSV and the STLs to the release. 6. Stage the images the site serves: ```bash @@ -63,11 +63,25 @@ What the two commands do, step by step - and the way to do it by hand. 9. Create the GitHub Release from the tag. Publishing it triggers **Firmware release assets**, which attaches the four flash images from the tag plus a generated `FLASHING.md` with offsets and hashes. 10. Confirm it went green before announcing; re-run it with `workflow_dispatch` if it did not fire. +## Branches + +Work happens on `dev`, then lands on `main` through a pull request. `main` is protected: nobody pushes to it directly, the maintainer included. + +- **Commit freely on `dev`.** Commits are cheap save points; small and frequent is good. Throwaway `wip:` commits belong here rather than on `main`. +- **Bigger or riskier work** gets its own branch (`feat/...`, `fix/...`) off `dev`. +- **Outside pull requests target `main`.** After one merges, pull `main` back into `dev` so the branches don't drift: + + ``` + git checkout dev && git merge origin/main && git push origin dev + ``` + +- **A release** is `dev` → `main` as one pull request, opened by `release.py publish` from the changelog section; merging it posts the dev-log to Discord. + ## Current release line `CHANGELOG.md` is the record — one section per release, newest first — and the [releases page](https://github.com/engmung/Patternflow/releases) carries the notes and the flashable images. The shape of the line, for orientation: - `v1.x` -- first public buildable release, then the multi-pattern firmware and browser flasher. - `v2.x` -- the v2.0 board (GPIO0 cold-boot fix, cleaned silkscreen), custom pattern workflow, the web platform. `v2.1.0` is the last release for v2.x hardware. -- `v3.0.0` -- the v3.0 board. Every later `v3.x` is firmware/web on unchanged hardware: `.pfm` modules over Wi-Fi (3.2), shows and the Director (3.6), the feature seam (3.7), editions (3.8). +- `v3.0.0` -- the v3.0 board; `v3.9` (2026-09, unreleased as a tag) removed its USB-C footprint and changed nothing else. Every later `v3.x` is firmware/web on that hardware: `.pfm` modules over Wi-Fi (3.2), shows and the Director (3.6), the feature seam (3.7), editions (3.8). - Editions (`audio`, `performance`, `clock`) carry their own version lines, independent of the project version — see `docs/EDITIONS.md`. diff --git a/docs/REPOSITORY.md b/docs/REPOSITORY.md new file mode 100644 index 00000000..4d95ecc1 --- /dev/null +++ b/docs/REPOSITORY.md @@ -0,0 +1,50 @@ +# The repository, folder by folder + +A map for people. (AI coding agents get the same map with more rules in [`../AGENTS.md`](../AGENTS.md).) Version numbers are deliberately not written here; the current project and edition versions are stamped into `AGENTS.md` by the release script and listed on the [releases page](https://github.com/engmung/Patternflow/releases). + +## Top level + +| Folder | What it is | Start at | +| :--- | :--- | :--- | +| `firmware/` | The ESP32-S3 firmware. `patternflow/` is the sketch: `patternflow.ino`, `config.h` (pins, brightness, limits, calibration), `net_config.h` (Wi-Fi, OTA, update defaults), `src/` (the engine: HUB75 driver, encoders, canvas, colour, noise, Wi-Fi, web console — the **core**, compiled unchanged into every published firmware), `features/` (everything optional: OSC, audio, MQTT, MIDI, shows, weather, clock — attached through hooks the core exposes), `presets/` (the curated patterns), `abi/` (the frozen contract a `.pfm` module is compiled against), `console/` (the device's web pages as plain HTML). `bundles/` names the editions (which features compile in) and holds `build.sh`. `toolchain/` is the repo-level scripts: build a module, make the Basics pack, and the `check_*.py` that CI runs. `modules/` is the module toolchain's working folder, not a place patterns are contributed to | [`firmware/README.md`](../firmware/README.md) for building; [`EDITIONS.md`](EDITIONS.md) before touching anything | +| `hardware/` | `pcb/` (KiCad source, Gerbers per revision, schematic PDF), `case/` (STLs by printer bed size, knob plates, the Blender source, legacy v2 cases, and `remixes/` for community variants), `bom/` (the CSV that is the BOM source of truth) | [`hardware/README.md`](../hardware/README.md) | +| `web/` | The Next.js site at patternflow.work: landing page, Live Editor, Pattern Lab, the community (only on the community host), browser flasher, edition shelf, device update handoff, journal, roadmap. The JS presets in `src/lib/presets/` are the source of truth for the firmware's preset headers. The breadboard build guide is a React page here, `src/app/build/breadboard/` | [`web/README.md`](../web/README.md), then [`web/ARCHITECTURE.md`](../web/ARCHITECTURE.md) | +| `docs/` | Contracts other software is built against, how the firmware is put together, walk-throughs, the assembly map, project records and media | [`README.md`](README.md) | +| `tools/` | Clients Patternflow ships that run on their own: the audio-react browser extension (also the authoring source of the device's `/audio-in` page), the Android capture app, an RTP-MIDI probe | [`tools/README.md`](../tools/README.md) | +| `integrations/` | Bridges that run inside or beside someone else's software (Ableton / Max for Live today) and talk to the panel only through the contracts in `docs/` | [`integrations/README.md`](../integrations/README.md) | +| `.github/` | Issue and pull-request templates, the CI workflows (web, firmware editions, doc links, console-page sync, firmware release assets, Discord dev-log) and their helper scripts | the header comment of each workflow | +| root | `README.md`, the five guides (`BUILD_GUIDE.md`, `BUILD_GUIDE_v2.md`, `PATTERN_GUIDE.md`, `FEATURE_GUIDE.md`, `AUDIO_GUIDE.md`), `CHANGELOG.md`, `CONTRIBUTING.md`, `SUPPORT.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, `CITATION.cff`, the two license texts and the `LICENSE` pointer, `AGENTS.md` (+ `CLAUDE.md`, a one-line shim) | | + +The rule for where a document lives: **a guide somebody follows start to finish is at the root; a contract, a reference or a record is in `docs/`; a folder explains itself in its own README.** + +## What you want to do → where to start + +| Goal | Path | +| :--- | :--- | +| Build one | [`README.md`](../README.md) → [`BUILD_GUIDE.md`](../BUILD_GUIDE.md) (or the [assembly map](assembly/README.md) for the other routes) → [`hardware/README.md`](../hardware/README.md) for which files to order | +| Make a pattern | [`PATTERN_GUIDE.md`](../PATTERN_GUIDE.md) — the Pattern Lab, the community, and the device. No local toolchain involved. [`firmware/CUSTOM_PATTERNS.md`](../firmware/CUSTOM_PATTERNS.md) is the older, more hands-on route | +| Write a firmware feature or cut an edition | [`FEATURE_GUIDE.md`](../FEATURE_GUIDE.md) → [`EDITIONS.md`](EDITIONS.md) → [`features/README.md`](../firmware/patternflow/features/README.md) → [`bundles/README.md`](../firmware/bundles/README.md) | +| Control the panel from other software | [`rest-api.md`](rest-api.md) has the table for choosing between HTTP, OSC, MIDI and MQTT; then the spec for the one you chose | +| Put sound into it | [`AUDIO_GUIDE.md`](../AUDIO_GUIDE.md) | +| Work on the site | [`web/README.md`](../web/README.md) → [`web/ARCHITECTURE.md`](../web/ARCHITECTURE.md) | +| Contribute anything | [`CONTRIBUTING.md`](../CONTRIBUTING.md) — one table of where each kind of contribution goes | +| Cut a release, run the servers | [`RELEASING.md`](RELEASING.md), [`SERVICES.md`](SERVICES.md) | + +## Deliberately not in git + +`_temp/`, `_tmp/` (scratch), `.claude/` (local agent config), `firmware/patternflow/patternflow_secrets.h` (per-device Wi-Fi credentials — never commit, and never build a shelf image with it present), `firmware/modules/.build/` and `firmware/patternflow/data/patterns/` (module build outputs), `web/data/` (a community host's SQLite and uploads), the PlatformIO clones under `firmware/patternflow/`. The reasons are in `.gitignore`'s comments. + +## Naming + +Filenames are lowercase with underscores. Tags are `vX.Y.Z`; the firmware source is not versioned by folder name. Preset files are named by the date they were made (`preset_0510.h`) and that name is also the site's sort key, so they are not renamed. `encloser.stl` is a typo that shipped in a release and on MakerWorld; it stays. + +## Files that other things link to — check before renaming + +The site, the firmware and the release notes hold absolute `blob/main/…` links into this tree, and none of them is checked by CI. Before renaming or moving any of these, grep `web/src`, `web/content`, `firmware/patternflow/console` and `firmware/patternflow/features/*/*_index.h`: + +- `README.md`, `BUILD_GUIDE.md` (three section anchors are linked from the site), `AUDIO_GUIDE.md`, `FEATURE_GUIDE.md`, `PATTERN_GUIDE.md` +- `docs/assembly/README.md`, `docs/midi-ableton.md`, `docs/midi-spec.md` (the last two are baked into the Audio edition's console pages) +- `docs/EDITIONS.md`, `docs/rest-api.md`, `firmware/README.md`, `hardware/bom/bom_v3.9.csv` +- `web/src/app/compliance/` — the `/compliance` URL is printed on the box. Never move, redirect or noindex it. + +`.github/scripts/check_links.py` checks the *relative* links in every tracked markdown file and runs on every pull request that touches one. diff --git a/docs/assembly/README.md b/docs/assembly/README.md index 885931dc..5f1e0b6c 100644 --- a/docs/assembly/README.md +++ b/docs/assembly/README.md @@ -1,13 +1,13 @@ # Build Patternflow — Assembly Map -> **Which version am I building?** Current hardware is **v3.0.0**: follow **[BUILD_GUIDE.md](../../BUILD_GUIDE.md)**, with every file bundled at the **[v3.0.0 release](https://github.com/engmung/Patternflow/releases/tag/v3.0.0)** (STLs, Gerbers, Bambu print project, firmware images). +> **Which version am I building?** Current hardware is the **v3.9 board** (the v3.0 board with the USB-C footprint removed; same case, same guide): follow **[BUILD_GUIDE.md](../../BUILD_GUIDE.md)** and take the files from the tree — [`hardware/README.md`](../../hardware/README.md) says exactly which Gerber, BOM and STL to use. The [v3.0.0 release](https://github.com/engmung/Patternflow/releases/tag/v3.0.0) bundles the previous revision's files. > Own a **v2.x board**? Use **[BUILD_GUIDE_v2.md](../../BUILD_GUIDE_v2.md)** and the **[v2.1.0 release](https://github.com/engmung/Patternflow/releases/tag/v2.1.0)** instead. **v2 and v3 parts are not interchangeable** — the boards do not fit each other's cases. > 🔦 **Buying the LED panel? Read [LED Panel Compatibility](../panel-compatibility.md) first.** Patternflow scans the panel directly from the ESP32-S3, so the **driver IC** decides whether it lights up — "HUB75E" on the listing does not, and a spec-matching panel with S-PWM "video wall" drivers stays completely dark with no firmware fix. Since that part number is almost never in the listing, the practical check is the **buyer reviews**: someone running it off an ESP32 or Raspberry Pi is the best evidence you'll get. Patternflow is not a single, rigid kit. It is a modular system divided into two core parts: -1. **Enclosure** — how you house the device (3D printed today; laser cut in testing). +1. **Enclosure** — how you house the device (3D printed; community remixes welcome). 2. **Electronics** — how you wire the hardware (custom PCB, or breadboard). Build those two, flash the firmware, and your Patternflow is alive. @@ -16,9 +16,9 @@ Build those two, flash the firmware, and your Patternflow is alive. | Enclosure | Electronics | Firmware | Status | | --- | --- | --- | --- | -| [3D printed enclosure](enclosure/3d-print.md) | [Custom PCB, hand-soldered](electronics/pcb.md) | [Browser flash / custom patterns](firmware/custom-patterns.md) | **Current — fully documented** | +| [3D printed enclosure](../../hardware/case/README.md) | [Custom PCB, hand-soldered](../../hardware/pcb/README.md) | [Browser flash](../../BUILD_GUIDE.md#8-firmware) · [your own patterns](../../PATTERN_GUIDE.md) | **Current — fully documented** | -This is the route [BUILD_GUIDE.md](../../BUILD_GUIDE.md) walks start to finish: PLA parts on any 256 mm-bed FDM printer, the hand-soldered v3.0 board (all through-hole — deliberately kept first-timer easy), and firmware flashed from the browser. Two ordering shortcuts are wired straight to it: +This is the route [BUILD_GUIDE.md](../../BUILD_GUIDE.md) walks start to finish: PLA parts on any 256 mm-bed FDM printer, the hand-soldered v3.9 board (all through-hole — deliberately kept first-timer easy), and firmware flashed from the browser. Two ordering shortcuts are wired straight to it: - **PCB** — the [PCBWay shared project](https://www.pcbway.com/project/shareproject/Patternflow_An_LED_synthesizer_776d796c.html): no Gerber upload, and ordering there supports Patternflow development. - **Case** — the [MakerWorld listing](https://makerworld.com/en/models/3072492-patternflow-open-source-led-synthesizer-case#profileId-3459015): tuned one-click print profiles for Bambu printers (STLs in `hardware/case/` for everyone else). @@ -29,10 +29,9 @@ This is the route [BUILD_GUIDE.md](../../BUILD_GUIDE.md) walks start to finish: | --- | --- | --- | | 3D printed enclosure | Custom PCB | **Current** — [BUILD_GUIDE.md](../../BUILD_GUIDE.md) | | 3D printed enclosure | Breadboard / jumper-wire electronics | Available — [Breadboard Build Guide](https://patternflow.work/build/breadboard) | -| Laser-cut enclosure | Custom PCB | In testing — [#123](https://github.com/engmung/Patternflow/issues/123) | -| Laser-cut enclosure | Breadboard / jumper-wire electronics | In testing — [#123](https://github.com/engmung/Patternflow/issues/123) | +| Laser-cut or any other enclosure | either | Not an official path. Community variants live in [`hardware/case/remixes/`](../../hardware/case/remixes/README.md); the old acrylic drawings are in [`legacy_lasercut/`](../../hardware/case/legacy_lasercut/README.md) | -The in-testing paths exist to make Patternflow easier and cheaper to start. A breadboard build is not just a temporary prototype — if that form is enough for you, it is a valid Patternflow build. Want a more finished object later? Move to the PCB and printed-enclosure path whenever you like. +The breadboard path exists to make Patternflow easier and cheaper to start. A breadboard build is not just a temporary prototype — if that form is enough for you, it is a valid Patternflow build. Want a more finished object later? Move to the PCB and printed-enclosure path whenever you like. The custom PCB path is stable. PCBA may become a later electronics path for people who want the same PCB with less hand assembly. @@ -42,11 +41,11 @@ To bring the hardware to life you flash it with firmware, and you can **create a Custom patterns no longer need a local toolchain or a reflash: a pattern built in the browser installs on the panel over Wi-Fi as a `.pfm` module in seconds (the browser flasher writes the firmware itself over USB once, and sets up Wi-Fi while it is at it). The PlatformIO route remains only for firmware feature development or targeting a different LED matrix resolution. -- **[Create custom patterns (recommended)](firmware/custom-patterns.md)** — make a pattern in the [Live Editor](https://patternflow.work) or [Pattern Lab](https://patternflow.work/pattern-lab), preview it live, and install it from the browser. +- **[Make your own patterns](../../PATTERN_GUIDE.md)** — the Pattern Lab, the community, and sending a pattern to your device over Wi-Fi. [`firmware/CUSTOM_PATTERNS.md`](../../firmware/CUSTOM_PATTERNS.md) is the hands-on route. ## Guides & releases | Version | Guide | Everything bundled | | --- | --- | --- | -| **v3.0.0** (current) | [BUILD_GUIDE.md](../../BUILD_GUIDE.md) | [v3.0.0 release](https://github.com/engmung/Patternflow/releases/tag/v3.0.0) | +| **v3.9 board** (current; v3.0 is the same board with a USB-C footprint you leave empty) | [BUILD_GUIDE.md](../../BUILD_GUIDE.md) | the files in [`hardware/`](../../hardware/README.md); the [v3.0.0 release](https://github.com/engmung/Patternflow/releases/tag/v3.0.0) bundles the v3.0 revision | | v2.1.0 (legacy) | [BUILD_GUIDE_v2.md](../../BUILD_GUIDE_v2.md) | [v2.1.0 release](https://github.com/engmung/Patternflow/releases/tag/v2.1.0) | diff --git a/docs/assembly/electronics/pcb.md b/docs/assembly/electronics/pcb.md deleted file mode 100644 index 689dca9c..00000000 --- a/docs/assembly/electronics/pcb.md +++ /dev/null @@ -1,31 +0,0 @@ -# Custom PCB Electronics - -Status: supported now. - -This path uses the custom Patternflow KiCad PCB in `hardware/pcb/`, populated by hand. It is the most polished electronics path currently documented. - -## What This Path Means - -- Order the Patternflow PCB — easiest via the [PCBWay shared project](https://www.pcbway.com/project/shareproject/Patternflow_An_LED_synthesizer_776d796c.html) (no Gerber upload), or from any fab with the Gerber zip below. -- Hand-solder the through-hole parts (the v3.9 board has no surface-mount parts at all). -- Mount the ESP32-S3 module on female headers. -- Power comes in via `J4`, the back-side 2-pin screw terminal — strip a USB cable, screw the wires in, done. It is the board's only power input: v3.0's USB-C footprint was withdrawn after a delayed burnout ([#221](https://github.com/engmung/Patternflow/issues/221)) and v3.9 no longer carries it (see [BUILD_GUIDE.md §2](../../../BUILD_GUIDE.md#2-power-input--use-the-screw-terminal)). - -The custom PCB path pairs with the current [3D printed enclosure](../enclosure/3d-print.md). - -## Files - -| File | Purpose | -| --- | --- | -| `hardware/pcb/kicad/` | Editable KiCad source | -| `hardware/pcb/gerber/patternflow_v3.9_gerber.zip` | Current production Gerber — v3.0 ([#114](https://github.com/engmung/Patternflow/issues/114)) with the USB-C input removed | -| `hardware/bom/bom_v3.9.csv` | Machine-readable BOM (every part by MPN) | -| `hardware/pcb/schematic.pdf` | Schematic PDF | - -For the exact BOM, soldering order, wiring, and first boot checks, follow the current detailed guide: - -[Open the PCB assembly guide](../../../BUILD_GUIDE.md#5-pcb-assembly) - -## Alternative — Breadboard - -Don't want to order a PCB? The [breadboard / jumper-wire path](https://patternflow.work/build/breadboard) is available now — same core parts and firmware, no PCB and no soldering iron. diff --git a/docs/assembly/enclosure/3d-print.md b/docs/assembly/enclosure/3d-print.md deleted file mode 100644 index 30caab46..00000000 --- a/docs/assembly/enclosure/3d-print.md +++ /dev/null @@ -1,27 +0,0 @@ -# 3D Printed Enclosure - -Status: supported now. - -This path uses the current PLA enclosure files in `hardware/case/`, organized by printer bed size for the **v3.0 board**. The reference print was made on a Bambu P1S with standard PLA. - -## Files - -| File | Contents | -| --- | --- | -| `hardware/case/bed_330mm/encloser.stl` | One-piece snap-fit body — H2S-class (330 mm+) beds, one STL | -| `hardware/case/bed_256mm/encloser.stl` | The standard 256 mm print — P1S-class beds, print & assembly verified | -| `hardware/case/bed_256mm/for_other_panels/divided_v3_part1..5.stl` | Adjustable-mount variant for non-standard LED panels ([#169](https://github.com/engmung/Patternflow/issues/169)) | -| `hardware/case/knobs/knobs_15mm.stl` | Knobs for 15mm encoder shafts | -| `hardware/case/knobs/knobs_20mm.stl` | Knobs for 20mm encoder shafts (the BOM reference part is 20mm) | - -Building for a **v2.x board**? Its case files moved to `hardware/case/legacy_v2/`; follow the [v2.1.0 build guide](https://github.com/engmung/Patternflow/blob/v2.1.0/BUILD_GUIDE.md) instead. - -For full print settings, bonding steps, and assembly photos, follow the current detailed guide: - -[Open the 3D print build steps](../../../BUILD_GUIDE.md#4-3d-printing) - -Knob caps are printed separately (not part of the body) from the standalone knob STL matching your encoder shaft, in black. See [hardware/case/README.md](../../../hardware/case/README.md) for the full option matrix and assembly watch-outs. - -## Planned Alternative - -A laser-cut enclosure path is planned. The goal is to keep the same overall external shape and dimensions while making the build cheaper and more accessible for people without a large 3D printer. diff --git a/docs/assembly/firmware/custom-patterns.md b/docs/assembly/firmware/custom-patterns.md deleted file mode 100644 index bfa0af5b..00000000 --- a/docs/assembly/firmware/custom-patterns.md +++ /dev/null @@ -1,41 +0,0 @@ -# Create Custom Patterns - -Status: supported now. - -Use this path when you want to run your own pattern on Patternflow hardware. - -Two routes: - -- **From the browser** — the server compiles your pattern into a loadable module and it goes to the device over Wi-Fi. Nothing to install, no cable. -- **From PlatformIO** — a local build that compiles the pattern into the firmware as a preset. Still the route for firmware development, config changes, or working offline. - -## Workflow — from the browser - -1. Open the Patternflow Live Editor at [patternflow.work](https://patternflow.work). -2. Make or tune a JavaScript pattern. -3. Click **Copy C++ prompt** and use an AI assistant to convert the pattern. -4. In **Pattern Lab**, press **To hardware**, paste the C++, and press **Apply to my Patternflow**. Takes about half a second. -5. Press **Send over Wi-Fi**. The pattern appears in the device's list immediately. - -Needs the device powered on and on the same Wi-Fi as your computer. A community pattern already marked `.h` skips steps 2–4 and offers **Send to my Patternflow** directly. - -## Workflow — from PlatformIO - -1. Open Pattern Lab at [patternflow.work/pattern-lab](https://patternflow.work/pattern-lab). -2. Make or tune a JavaScript pattern. -3. Click **Copy C++ prompt** and use an AI assistant to convert the pattern. -4. Save the generated C++ as a new `preset_.h` file in `firmware/patternflow/presets/` (its includes use the `"../src/..."` form — `_TEMPLATE.h` explains). -5. Register the pattern in `firmware/patternflow/pattern_registry.h`. Compiled-in presets cost internal DRAM the console needs, so keep it to one or two. -6. Build and flash: `./firmware/bundles/build.sh flash patternflow.local` (over Wi-Fi) or `cd firmware/patternflow && pio run -t upload` (USB). The Arduino IDE still opens `patternflow.ino` if you prefer it, with the board settings below. -7. Select the ESP32-S3 board settings described in `firmware/README.md` — and install the ESP32 board package at **2.0.x**, not the latest 3.x. The newer core takes ~71 KB more internal RAM before the sketch starts, which is enough to stop large patterns loading. -8. Compile and upload the sketch to your ESP32-S3. - -Or skip the board-settings step entirely: `firmware/patternflow/platformio.ini` pins the right core and fetches the libraries itself — `cd firmware/patternflow && pio run -t upload`. - -The full custom pattern guide is here: - -[Open firmware/CUSTOM_PATTERNS.md](../../../firmware/CUSTOM_PATTERNS.md) - -## Pattern Contributions - -For now, custom patterns and official built-in patterns are separate. You can share patterns with the community, but only selected patterns will be bundled into official firmware releases. diff --git a/docs/investigations/2026-09-firmware-runtime.md b/docs/investigations/2026-09-firmware-runtime.md index 56f9f326..75dfe85c 100644 --- a/docs/investigations/2026-09-firmware-runtime.md +++ b/docs/investigations/2026-09-firmware-runtime.md @@ -5,7 +5,7 @@ research that followed them, the runtime rework that research prompted, the network-MIDI follow-up, and the regression the rework caused and how it was fixed. It supersedes four separate reports written along the way. -**Baseline** `68e23cb`, project 3.9.5, Audio 0.5.4. All of it is unreleased. +**Baseline** `68e23cb`, project 3.9.5, Audio 0.5.4. All of it shipped in 3.10.0 (see `CHANGELOG.md`). **Bench** one ESP32-S3 panel, 128×64, 8 colour bits / 260 Hz, brightness 97, Arduino 2.0.17 / ESP-IDF 4.4. No SDK, pin, partition or ABI change anywhere in this work. Individual patterns and their setup iterations are unchanged. diff --git a/docs/mqtt-spec.md b/docs/mqtt-spec.md new file mode 100644 index 00000000..533efc43 --- /dev/null +++ b/docs/mqtt-spec.md @@ -0,0 +1,75 @@ +# Patternflow MQTT contract + +**Version 1.0** (2026-09-14). Sibling of [`rest-api.md`](rest-api.md) (HTTP), [`osc-spec.md`](osc-spec.md) (OSC) and [`midi-spec.md`](midi-spec.md) (network MIDI); the table for choosing between the four is [Choosing a transport](rest-api.md#choosing-a-transport). MQTT ships in the **Performance** edition ([`EDITIONS.md`](EDITIONS.md)); a build without it serves neither the topics below nor `GET /api/mqtt`. The implementation is `firmware/patternflow/features/mqtt/`; this file is what a client is written against. + +What MQTT is for: putting the panel on the bus a home or a venue already runs (Home Assistant, Node-RED, a lighting desk), and making two panels follow each other. It reaches the knobs, the pattern, a banner message and sleep — not firmware update, pattern install or Wi-Fi, which stay on HTTP. + +## Transport + +| | | +| :--- | :--- | +| Broker | Any MQTT 3.1.1 broker. Set on the panel's **MQTT** console page or with `POST /api/mqtt` (host, port, user, password, prefix, role). Passwords are stored on the panel and never reported back. | +| Prefix | One topic prefix per panel, default `patternflow`. The prefix **is the channel** (below). No wildcards are used, so an ACL can be an exact list. | +| Roles | `off` (socket closed) · `publisher` (sends knobs, pattern and, on a show channel, a snapshot heartbeat) · `subscriber` (applies the retained snapshot, then follows the live topics). The two are exclusive: a Publisher ignores incoming knob and pattern writes. `GET /api/mqtt` reports the current role and `POST /api/mqtt?role=subscriber` flips it. | +| Channels | Prefix `patternflow` is **Broadcast**; `patternflow1` … `patternflow4` are **channels 1–4**; `patternflow5` is **Live**; anything else is **Custom**. Channels 1–4 force the Subscriber role. `GET /api/mqtt` reports the channel as `broadcast`, `ch1`–`ch4`, `live`, `custom` or `off`. | + +## Topics + +All under `/`. Direction is from the panel's point of view. + +| Topic | Direction | Payload | Retain | Obeyed in | +| :--- | :--- | :--- | :--- | :--- | +| `knob/1` … `knob/4` | out (publisher), in (subscriber) | absolute accumulated click count, signed integer | no | subscriber | +| `param/1` … `param/4` | out (publisher), in (subscriber) | absolute `0`–`1000`; empty payload releases the hold | no | subscriber | +| `pattern` | out (publisher), in (subscriber) | the pattern's display name or slug | no | subscriber | +| `message` | in | banner text shown on the panel | broadcast: yes · channels: no | either role | +| `sleep` | in | `1` / `on` / `true` / `sleep` · `0` / `off` / `false` / `wake` · `toggle` | no | either role | +| `sleep/state` | out | `0` or `1`, on every change and once per connection | no | — | +| `snapshot` | out (publisher, every 8 s), in (subscriber) | compact JSON of the mid-join state, including `param:[a,b,c,d]` | yes | channels 1–4 and Live only | + +Director-only, on a local broker and not part of the public ACL: `query` (panel → its module inventory), `select` (Director → mark modules on `/patterns` for ZIP export), `select/ack` (panel → `{matched, presets, missing, rev}`). Ignore them unless you are that tool. + +`sleep` and `message` are the two topics a panel obeys in **either** role: they are "tell the panels something", not "mirror this panel", and a panel that publishes its knobs is still one you want to switch off from home automation. Publishing `1` to `/sleep` puts the panel to sleep, `0` wakes it, and `sleep/state` mirrors the result — enough for a Home Assistant switch. + +## Which topic to write + +Decided per pattern by `absoluteReady` in its sidecar (`GET /api/patterns//sidecar` — see `rest-api.md`). + +- **`absoluteReady: true`** → publish `0`–`1000` to `param/`. The pattern pins that parameter to a fraction of its declared range; the value is idempotent and survives a restart of whatever is driving it. Physical encoder motion releases the hold, so a hand on the panel always wins. An empty payload releases it explicitly. +- **`absoluteReady: false`** (every module built before the parameter bus existed, and presets, which cannot be interrogated) → publish a new absolute click count to `knob/`. The panel diffs it against the last value it received and injects the difference as a detent delta. This is a **relative** control: the pattern integrates the delta through its own step size, so the number you send is not a value the parameter will arrive at. + +`knob`, `param` and `pattern` are obeyed **only in Subscriber role**; a Publisher ignores them silently. + +## The channel decides whether your writes survive + +Channels 1–4 and Live subscribe to the retained `/snapshot`, whose `param:[…]` the firmware applies straight onto the knobs, exactly as if it had arrived on `param/`. A Publisher on the channel re-sends one every 8 seconds, and the retained copy is redelivered on every reconnect. So on those five channels an external controller is not the only writer: its value is overwritten a moment later by whatever the snapshot last said, with every write succeeding and nothing reporting an error. The symptom is a control that will not stay where it is put. + +**Broadcast has no snapshot subscription.** For one panel driven by one external controller — home automation, a script, a dashboard — **Broadcast + Subscriber** is the combination that behaves. The show channels exist for a Director driving several panels, which is a different job. + +## Knob ordering + +Two orderings exist. **Physical** is K1–K4 left to right on the front panel. **Logical** is the order patterns read parameters in. The MQTT indices are **logical**, like the OSC indices, `knobs[]` in a sidecar and `/api/status`. The mapping between the two is in `rest-api.md` under [Knob ordering](rest-api.md#knob-ordering). + +## Examples + +Home Assistant, a switch that sleeps and wakes the panel: + +```yaml +mqtt: + switch: + - name: Patternflow + command_topic: patternflow/sleep + state_topic: patternflow/sleep/state + payload_on: "0" + payload_off: "1" + state_on: "0" + state_off: "1" +``` + +Node-RED, driving knob 2 of an `absoluteReady` pattern from a slider: an `mqtt out` node on topic `patternflow/param/2` with the slider's `0`–`1000` value; put the panel on Broadcast in Subscriber role first. + +Two panels following each other: point both at the same broker, same prefix on a show channel (`patternflow1`), one as Publisher and the other as Subscriber. + +## Version history + +- **1.0** (2026-09-14) — first written contract. Lifted verbatim, with no behaviour change, from the header comment of `firmware/patternflow/features/mqtt/core_mqtt.h` and the "Which topic to write" and "The channel decides" sections of `rest-api.md` 1.x. diff --git a/docs/panel-compatibility.md b/docs/panel-compatibility.md index 10e218d4..591152b0 100644 --- a/docs/panel-compatibility.md +++ b/docs/panel-compatibility.md @@ -141,7 +141,7 @@ Please: Six names, **four distinct behaviours** — verified in the library's `shiftDriver()`. `FM6126A`, `FM6124` and `ICN2038S` dispatch to one identical function with no per-chip branch, so picking between those three changes nothing. -The **browser flasher** ships `PANEL_STANDARD`, since one image serves everyone. `PANEL_PROFILE` is a compile-time setting, so a panel that genuinely needs another profile needs a local build — Arduino IDE, following [`firmware/README.md`](../firmware/README.md). (Loadable modules cannot help here: they carry patterns, not driver configuration.) +The **browser flasher** ships `PANEL_STANDARD`, since one image serves everyone. `PANEL_PROFILE` is a compile-time setting, so a panel that genuinely needs another profile needs a local build — PlatformIO through `firmware/bundles/build.sh`, following [`firmware/README.md`](../firmware/README.md). (Loadable modules cannot help here: they carry patterns, not driver configuration.) ## 7. Reading the chip off a board you own diff --git a/docs/releases/v3.0.0-launch.md b/docs/releases/v3.0.0-launch.md index 8fe31e78..a09f7084 100644 --- a/docs/releases/v3.0.0-launch.md +++ b/docs/releases/v3.0.0-launch.md @@ -1,5 +1,7 @@ # v3.0.0 Launch Checklist +> **Historical (2026-07).** The one-off checklist used for the v3.0.0 release. The current procedure is [`docs/RELEASING.md`](../RELEASING.md); release notes since 3.2 live on [GitHub Releases](https://github.com/engmung/Patternflow/releases). + Everything for the v3.0.0 release is **pre-loaded**. When the final gate below passes, run the steps top to bottom — each is copy-paste. (Windows: run the PowerShell blocks in PowerShell, everything else in any terminal with `git` + `gh`.) ## Gate 0 — the one thing left (hardware) diff --git a/docs/releases/v3.0.0.md b/docs/releases/v3.0.0.md index bf6801cb..599220fb 100644 --- a/docs/releases/v3.0.0.md +++ b/docs/releases/v3.0.0.md @@ -1,5 +1,7 @@ # Patternflow v3.0.0 — the hybrid-power generation +> **Historical (2026-07).** Long-form notes for one release, kept as written. Release notes since 3.2 live on [GitHub Releases](https://github.com/engmung/Patternflow/releases); `CHANGELOG.md` at the root is the complete record. + The biggest hardware revision since launch. The v3.0 board and enclosure were rebuilt around one goal: **anyone should be able to solder and assemble this.** **Building one? Start here → [BUILD_GUIDE.md](https://github.com/engmung/Patternflow/blob/v3.0.0/BUILD_GUIDE.md)** — the PCB soldering is covered by a [full video walkthrough](https://youtu.be/NZCjMBCsDAc). diff --git a/docs/releases/v3.1.0.md b/docs/releases/v3.1.0.md index 428dc6f8..ef7271f9 100644 --- a/docs/releases/v3.1.0.md +++ b/docs/releases/v3.1.0.md @@ -1,5 +1,7 @@ # Patternflow v3.1.0 — the browser does everything now +> **Historical (2026-07).** Long-form notes for one release, kept as written. Release notes since 3.2 live on [GitHub Releases](https://github.com/engmung/Patternflow/releases); `CHANGELOG.md` at the root is the complete record. + **Hardware unchanged** — the v3.0 board and case carry over exactly as they are. This release is the software half of the instrument catching up: a pattern community, firmware builds that run on a server and flash from the browser, wireless updates, and one important piece of safety guidance. **Building one? Start here → [BUILD_GUIDE.md](https://github.com/engmung/Patternflow/blob/v3.1.0/BUILD_GUIDE.md)** — about 30 minutes of (deliberately easy, all through-hole) soldering, 30 minutes of assembly, ~10 hours of printer time, around US$100 in parts. diff --git a/docs/rest-api.md b/docs/rest-api.md index bc1148c4..36bec52f 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -6,7 +6,7 @@ Clients should tolerate unknown status fields and probe capabilities before call Patternflow serves a plain HTTP server on port 80 over the local Wi-Fi network. It carries two different things: the **device console** — HTML pages a person opens in a browser — and a **JSON API** under `/api/`, which is the contract between the firmware and any host software that drives a device over the network. Anything that drives a panel — a bridge, a card, a script — is built against this file, not against the firmware source. -`docs/osc-spec.md` is the sibling contract for OSC over UDP, aimed at DAWs and show software, and `docs/midi-spec.md` the one for MIDI. The MQTT topic layout is documented in the header comment of `firmware/patternflow/features/mqtt/core_mqtt.h`. The three are not interchangeable — see [Choosing a transport](#choosing-a-transport). +`docs/osc-spec.md` is the sibling contract for OSC over UDP, aimed at DAWs and show software, and `docs/midi-spec.md` the one for MIDI. [`docs/mqtt-spec.md`](mqtt-spec.md) is the one for MQTT. The four are not interchangeable — see [Choosing a transport](#choosing-a-transport). ## Transport @@ -294,29 +294,9 @@ Both directions work over HTTP. This section used to say the opposite, and it wa Also reported: `normalHost`, `normalPort`, `normalUser`, `normalPrefix`, `normalHasPassword` (the saved Normal-mode broker, kept while Director mode overlays it) and `directorHost`. Passwords are never returned — only whether one is set. -### Which topic to write +### Which topic to write, and which channel -Decided per pattern by `absoluteReady` from the sidecar. - -**`absoluteReady: true`** → publish `0`–`1000` to `/param/<1..4>`. The pattern pins that parameter to a fraction of its declared range; the value is idempotent and survives a restart of whatever is driving it. Physical encoder motion releases the hold, so a hand on the device always wins. An empty payload releases it explicitly. - -**`absoluteReady: false`** — every module built before the bus existed, and presets, which cannot be interrogated — → publish a new absolute click count to `/knob/<1..4>`. The device diffs it against the last value it received and injects the difference as a detent delta. This is a **relative** control: the pattern integrates the delta through its own step size, so the value you send is not a value the parameter will arrive at. - -`/sleep` is the exception to everything above: it is obeyed in **either** role, because a panel that publishes its knobs is still a panel somebody wants to be able to switch off. `/sleep/state` mirrors it on every change and once per connection. - -Everything else — `knob`, `param`, `pattern` — is obeyed **only in Subscriber role**. A device set to Publisher will ignore knob writes silently. `POST /api/mqtt?role=subscriber` flips it, at the cost of the device no longer publishing its own knob turns; the two roles are exclusive. - -### The channel decides whether your writes survive - -The prefix **is** the channel: `patternflow` is Broadcast, `patternflow1`–`patternflow4` are channels 1–4, `patternflow5` is Live, anything else is Custom. Setting a prefix therefore selects a channel, which is not obvious from either end. - -It matters because **channels 1–4 and Live also subscribe to a retained `/snapshot`**, whose payload carries `param:[a,b,c,d]` — and the firmware applies those values straight onto the knobs, exactly as if they had arrived on `param/N`. A Publisher on the channel re-sends one every 8 seconds, and the retained copy is redelivered on every reconnect. - -So on those five channels an external controller is not the only writer. Its value is overwritten a moment later by whatever the snapshot last said, the write having succeeded, MQTT being healthy, and nothing anywhere reporting an error. The symptom is a control that will not stay where it is put. - -**Broadcast has no snapshot subscription.** For a single panel driven by one external controller — home automation, a script, a dashboard — Broadcast plus Subscriber is the combination that behaves. The show channels are for a Director driving several panels, which is a different job and the reason the snapshot bus exists at all. - -`GET /api/mqtt` reports the channel as `broadcast`, `ch1`–`ch4`, `live`, `custom` or `off`, so a client can check this rather than guess. +Moved to [`mqtt-spec.md`](mqtt-spec.md): the `absoluteReady` rule that decides between `param/` and `knob/`, which topics each role obeys, and why an external controller on channels 1–4 or Live sees its writes overwritten by the retained snapshot (Broadcast + Subscriber is the combination that behaves for one panel and one controller). ### Knob ordering diff --git a/firmware/CUSTOM_PATTERNS.md b/firmware/CUSTOM_PATTERNS.md index f55d38f2..84a03744 100644 --- a/firmware/CUSTOM_PATTERNS.md +++ b/firmware/CUSTOM_PATTERNS.md @@ -5,7 +5,7 @@ Patternflow includes a workflow for creating patterns without writing low-level - **AI-assisted** — describe the pattern in plain language, paste the AI's output into the editor, tune knobs, copy to C++, flash. No shader knowledge required. - **Direct** — write the JavaScript pattern by hand in the editor, then convert to C++. For anyone comfortable with fragment-shader-style code. -> **New: you no longer need the Arduino IDE.** The last step — turning a pattern into firmware and getting it onto the board — now runs in the browser: the server compiles, your browser flashes over USB. See [step 5](#5-convert-to-c-and-flash). The Arduino IDE route still works and is still the right one for firmware development. +> **New: you no longer need the Arduino IDE.** The last step — turning a pattern into firmware and getting it onto the board — now runs in the browser: the server compiles, your browser flashes over USB. See [step 5](#5-convert-to-c-and-flash). A local build is still the route for firmware development itself; that is PlatformIO through `firmware/bundles/build.sh`, not the Arduino IDE (the IDE cannot build the editions' library set). > **Newer still: patterns can install without any flashing at all.** On firmware > with loadable-module support, the same `.h` builds into a tiny `.pfm` module @@ -62,11 +62,11 @@ Requirements: the device powered on and on the same Wi-Fi as your computer. (Bak If the build fails you get the compiler's own error. The usual causes are a helper the firmware already provides being redefined, or a type that differs from the JavaScript original. -#### Route B — from the Arduino IDE (local build) +#### Route B — a local build (PlatformIO) -1. Save the C++ output as `pattern_yourname.h` inside `firmware/patternflow/`. +1. Save the C++ output as `preset_yourname.h` inside `firmware/patternflow/presets/`. 2. Open `pattern_registry.h` and add two lines (see [Installing your pattern](#installing-your-pattern) below). -3. Open `patternflow.ino` in Arduino IDE, select your ESP32-S3 port, and upload. +3. Build and flash with the bundled toolchain: `./firmware/bundles/build.sh flash patternflow.local` (over Wi-Fi) or `cd firmware/patternflow && pio run -t upload` (USB). Still the route for changing anything beyond a pattern — firmware development, config edits, working offline, or building for a board you cannot plug into this machine. @@ -74,7 +74,7 @@ Still the route for changing anything beyond a pattern — firmware development, Long-press encoder 4 on the device to cycle to your new pattern. -> Either route flashes a **whole firmware image**, replacing the firmware and the presets compiled into it. Any `.pfm` modules you've installed live on a separate FATFS partition and **survive a normal reflash** — they only go away if you enable *Erase All Flash Before Sketch Upload*, or format the partition from `/patterns`. +> Route B flashes a **whole firmware image**, replacing the firmware and the presets compiled into it; Route A installs a module and touches nothing else. Any `.pfm` modules you've installed live on a separate FATFS partition and **survive a normal reflash** — they only go away if you enable *Erase All Flash Before Sketch Upload*, or format the partition from `/patterns`. --- @@ -211,19 +211,19 @@ function render(x, y, t) { `x` and `y` are pixel coordinates (0 to 127, 0 to 63). `t` is time in seconds. Knob values and helper functions are available — refer to the prompt copied by **Copy creation prompt** for the full API. That prompt is the canonical reference; this README only summarizes the workflow around it. -The conversion path is the same: tune until you like it, click **Copy C++ prompt**, paste into an AI, save the result as a `pattern_*.h` file, register it. +The conversion path is the same: tune until you like it, click **Copy C++ prompt**, paste into an AI, save the result as a `presets/preset_*.h` file, register it. --- ## Share what you make -If your pattern is good, send it. +If your pattern is good, publish it. -- **Discord** — drop it in the patterns channel: [discord.gg/Vr9QtsxeTk](https://discord.gg/Vr9QtsxeTk) -- **GitHub** — open a PR adding your `pattern_yourname.h` to `firmware/patternflow/` -- **Instagram** — DM Patternflow with a clip and the code +- **Community** — from the Pattern Lab, click **Publish** and it goes on the [community wall](https://community.patternflow.work/community) under the license you pick, playable by every Patternflow. This is where patterns live; the repository does not take pattern pull requests. +- **Discord** — show it off in the patterns channel: [discord.gg/Vr9QtsxeTk](https://discord.gg/Vr9QtsxeTk) +- **Instagram** — DM Patternflow with a clip and it can get featured -Good patterns get bundled into future releases, with credit. The Patternflow pattern library should belong to the people who actually make patterns, not just the people who designed the hardware. +The Patternflow pattern library belongs to the people who actually make patterns, not just the people who designed the hardware. --- diff --git a/firmware/README.md b/firmware/README.md index e25667f3..c0061fa5 100644 --- a/firmware/README.md +++ b/firmware/README.md @@ -1,10 +1,26 @@ # Patternflow Firmware -Arduino-based firmware for the ESP32-S3 powering Patternflow. One image serves every board generation — the pin map is identical on v2.x and v3.0. +Arduino-based firmware for the ESP32-S3 powering Patternflow. One image serves every board generation — the pin map is identical on v2.x, v3.0 and v3.9. The firmware handles the ESP32-S3 DMA driver for the HUB75 LED matrix, reads four rotary encoders to control generative patterns, and supports Arduino OTA for wireless updates. -> ⚠️ **Panel compatibility.** This firmware drives the panel directly from the ESP32-S3, so the panel's **driver IC** must be one the `ESP32-HUB75-MatrixPanel-DMA` library can drive: classic shift-register parts — **74HC595**, **FM6124**, **FM6126A**, **ICN2037**, **ICN2038S**, **DP5125D**, **DP3246**, **MBI5124**, **SM162xx**. S-PWM / GCLK "video wall" panels (**ICN2053**, **FM6353**, **FM6363C / FM6373C**, **DP3264/DP3265**, **ICND2055**, **MBI505x** — sold as high-refresh "1920/3840Hz" modules needing a sending/receiving card) will **not** work and stay completely dark. "HUB75E" on a listing guarantees a connector, not compatibility. Check this **before buying** — see **[docs/panel-compatibility.md](../docs/panel-compatibility.md)**. Select your panel's driver below via `PANEL_PROFILE`. +> ⚠️ **Panel compatibility.** This firmware drives the panel directly from the ESP32-S3, so the panel's **driver IC** decides whether it lights up: classic shift-register parts work, S-PWM "video wall" parts stay completely dark, and "HUB75E" on a listing proves nothing. The list of chips, and how to check before buying, is **[docs/panel-compatibility.md](../docs/panel-compatibility.md)**; the driver is selected below via `PANEL_PROFILE`. + +## Reading order + +This file is long because it is the firmware's reference. Most people need one part of it: + +| you want to | read | +| --- | --- | +| build and flash the firmware locally | [Setup](#setup) — the board package, PlatformIO, the libraries | +| write a feature or cut an edition | [`docs/EDITIONS.md`](../docs/EDITIONS.md) first, then [Adding features — the constraints that matter](#adding-features--the-constraints-that-matter) here | +| write a pattern | [`PATTERN_GUIDE.md`](../PATTERN_GUIDE.md) (nothing here is needed); the hands-on route is [`CUSTOM_PATTERNS.md`](CUSTOM_PATTERNS.md) | +| know what a pattern can call | [Foundation modules](#foundation-modules) | +| know how `.pfm` modules load and what they cost | [Loadable pattern modules](#loadable-pattern-modules-pfm) | +| tune brightness, calibration, refresh | [Configuration](#configuration-configh) | +| know what the knobs and buttons do | [Controls](#controls), [Sleep mode](#sleep-mode) | +| drive the panel from other software | the contracts in [`docs/`](../docs/README.md): HTTP, OSC, MIDI, MQTT, audio | +| edit a console page | [`patternflow/console/README.md`](patternflow/console/README.md) | ## Building your own firmware @@ -155,20 +171,9 @@ firmware/ │ ├── audio/ performance/ clock/ # features_local.h + overrides.h │ ├── build.sh # build the default, a named edition, or `all` │ └── shelf.sh # stage a publishable image for the site -├── modules/ # Loadable-pattern sources (one dir per pattern) -│ └── /pattern.cpp # + optional module.json sidecar +├── modules/ # The module toolchain's working folder, not an inbox — see modules/README.md ├── encoder_test/ # Standalone encoder diagnostic sketch -└── toolchain/ # Repo-level tooling - ├── build_module.py # pattern.cpp → .pfm (Xtensa relocatable ELF) - ├── make_pack.py # presets → a .zip pattern pack the site serves - ├── port_preset.py # firmware .h → freestanding module source - ├── console_pages.py # console/*.html ⇄ *_index.h (extract / build / check) - ├── console_serve.py # the console on localhost with a fake device behind it - ├── build_audio_in_page.py # assembles console/audio-in.html from the extension's editor - ├── check_boundaries.py # CI: the core names no feature - ├── check_abi_freeze.py # CI: the module ABI has not moved - ├── check_sources.py # CI: sources the build expects are present - └── module.ld # Collapses a module to .text/.rodata/.data/.bss +└── toolchain/ # Repo-level tooling: build a module or a pack, the console pages, the CI checks — see toolchain/README.md ``` `src/` holds the foundation that patterns build on; `features/` holds everything the device can do beyond being a panel with four knobs, and the core never names any of it — that rule, and why, is [`docs/EDITIONS.md`](../docs/EDITIONS.md). Patterns and the main sketch reference the helpers via `#include "src/core_*.h"`. @@ -199,6 +204,10 @@ The current word-aligned blit measured **6.86 → 5.90 ms** on the bench's framebuffer. See the [performance and recovery bench report](../docs/investigations/2026-09-firmware-runtime.md) for the controlled comparison and its limits. +All five calibration values are tunable from `config.h` — see "LED panel calibration" below. + +### Runtime notes (3.10): activation worker, memory admission, network maintenance, MIDI transport + The follow-up [runtime research](../docs/investigations/2026-09-firmware-runtime.md) measures whole-loop delays, upload/load overlap, simulation warm-up and memory policy. It separates tested improvements from experimental results and proposed @@ -267,7 +276,6 @@ false. This implementation lives entirely in `features/midi/`. See the [implementation and MIDI follow-up report](../docs/investigations/2026-09-firmware-runtime.md) for measured before/after results and remaining limitations. -All five calibration values are tunable from `config.h` — see "LED panel calibration" below. ### `core_math.h` — PFMath ```cpp @@ -349,10 +357,10 @@ Both tiers share the same pattern shape — a namespace with: - `draw()` — draws via `PFCanvas::setPixel(...)` and ends with `PFCanvas::present();` and the same foundation (`PFCanvas`/`PFMath`/`PFColor`/`PFNoise`), so a pattern -written for one tier ports to the other mechanically. `CUSTOM_PATTERNS.md` -documents the submission format; the Pattern Lab at -[patternflow.work](https://patternflow.work) generates conforming C++ from a -JavaScript pattern via its "Copy C++ prompt" flow. +written for one tier ports to the other mechanically. The everyday way to make +one is the Pattern Lab — [`PATTERN_GUIDE.md`](../PATTERN_GUIDE.md) — which +generates conforming C++ from a JavaScript pattern and installs it over Wi-Fi; +[`CUSTOM_PATTERNS.md`](CUSTOM_PATTERNS.md) is the hands-on route. To add a **preset** (rare — curated set): copy `_TEMPLATE.h` into `presets/preset_.h` and add one `PATTERN_ENTRY(...)` line in @@ -411,7 +419,7 @@ surface — the math headers are literally the same files, included with uploads them one by one with progress, retries, and per-file results. - `curl -F "module=@slug.pfm" http://patternflow.local/api/patterns` for scripts. -**Costs and limits, measured on hardware** (128×64, esp32 core 3.3.8): +**Costs and limits, measured on hardware** (v3.2, 2026-07, 128×64, esp32 core 3.3.8 build — a core 2.x build has more heap and renders faster; see [How to measure this](#how-to-measure-this-before-reading-it)): | Property | Measured | |---|---| @@ -497,8 +505,8 @@ Two names that mean different things, and have already been mixed up here: And the rule the rest of this section exists to serve: **a heap number travels with its build, its procedure and its date, or it is not evidence.** The figures below are v3.5.2 on a core 2.x build and are quoted as history — -[RFC §2.13](../docs/rfc-core-and-variants.md) has current ones, taken under -the protocol above. In 2026-08 the `86,004` below was copied into five +the [September 2026 runtime report](../docs/investigations/2026-09-firmware-runtime.md) +has the 3.10 numbers, taken under the protocol above. In 2026-08 the `86,004` below was copied into five documents as a measurement of something else entirely. ### Current state (v3.5.2, measured on hardware, core 2.x build) @@ -625,11 +633,13 @@ far, each confirmed by A/B on hardware: mid-statement and never runs, and the console looks blank while every API underneath answers fine. - The console therefore **pauses the pattern**: opening any console page - evicts the module, and `tick()` restores it after 25 s of console - silence (`core_patterns_http.h`). The request that triggers the eviction - cannot be rescued — its send path is already constrained — so it gets a - 552-byte interstitial that reloads itself. + Until 3.6.3 the console therefore **paused the pattern**: opening a page + evicted the module and restored it after 25 s of silence. Since 3.6.3 the + page sender streams PROGMEM in small slices under a 5-second budget, the + pages are gzip-compressed, and the core-2 builds have the heap for it, so + a page no longer evicts anything; `status.consolePaused` now means only + that a pattern-install batch is in progress ([`docs/rest-api.md`](../docs/rest-api.md)). + The memory wall below is still real, and is why pages stay small. **This is a memory wall, not a pacing problem.** It is tempting to read the 10 s stall as impatience — `NetworkClient::write()` really does send @@ -659,24 +669,10 @@ far, each confirmed by A/B on hardware: ### Adding a console page -Follow the existing shape (`core_status_http.h` + `status_index.h` is the -smallest example): one `core__http.h` that attaches routes in a -`begin()` called from the Wi-Fi connect edge in `patternflow.ino`, one -`_index.h` PROGMEM HTML bundle, a row on `home_index.h`. Rules: - -- Self-contained HTML only — the device serves with no internet in the loop. - Match the cream/ink/LED design tokens of the existing pages. -- **Syntax-check the page's JavaScript before flashing**: - extract the `