Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
135 commits
Select commit Hold shift + click to select a range
bddb272
v30: set the power supplies at start as the stock firmware does (issu…
isod89 Nov 4, 2026
129b6a4
v31: turn the long-press reset off at start, as the stock firmware do…
isod89 Nov 4, 2026
c76e8c8
loader: never erase the preset store during an update; test that pres…
isod89 Nov 4, 2026
0d02e43
v32: web preset editor (live editing, 52 presets, export/import) over…
isod89 Nov 4, 2026
cd6a627
v33: leave the power supplies at the boot loader's levels again (v30'…
isod89 Nov 4, 2026
8ae04f4
tests: v33 identity
isod89 Nov 4, 2026
f76e288
docs: PINR at power-on, Pocket X 08 / BlackBox 09
isod89 Nov 4, 2026
3308f90
fitting tools: amp / drive / cab tables in floats with a PX_FIT hook …
isod89 Nov 5, 2026
3107bd1
v34: 8 amps, 4 drives and the 4x12 V30 fitted to captures of the real…
isod89 Nov 5, 2026
229babd
editor: preset files from v32 / v33 import onto the nearest v34 amp /…
isod89 Nov 5, 2026
e013a14
v35: new reverbs (FDN + Dattorro plate), delay up to 2 s, 16-bit dela…
isod89 Nov 5, 2026
ffd4f6b
Offline installer: INSTALL-OFFLINE.bat serves the installer and the p…
isod89 Nov 5, 2026
2d66afd
v36: pro levels, 24 levelled factory presets, a new PLAY / EDIT screen
isod89 Nov 5, 2026
cc055eb
v36: battery gauge, tuner mute, VOL 100 display; what's new since v33
isod89 Nov 5, 2026
591ba2f
Publishing: GitHub main = this folder's files, with the history; PUBL…
isod89 Nov 5, 2026
f48d8ee
Publishing: only the version's files (the bundle's list) go to GitHub…
isod89 Nov 5, 2026
86dbc61
Merge GitHub main (pull request #7: portable container build)
isod89 Nov 6, 2026
8e4539e
Windows: build.bat falls back to WSL without Docker; offline installe…
isod89 Nov 6, 2026
661391d
MIDI: the pure logic: packet decoder, Control Change map, messages to…
VinSpee Nov 6, 2026
b1d2c41
MIDI over USB-C: drain usb.c's ring, Program / Control Change in, the…
VinSpee Nov 6, 2026
fe7df6b
MIDI test channel: v37 MIDI TEST package, installer ?channel=midi-tes…
VinSpee Nov 6, 2026
108a11c
CI: build the MIDI test channel package and upload it as its own arti…
VinSpee Nov 6, 2026
357708e
no-mistakes(review): Add package-test rule to Makefile
VinSpee Nov 6, 2026
88ff0aa
Add an advertise-only Bluetooth test image (make ble)
VinSpee Nov 6, 2026
1f08da9
no-mistakes(review): Guard BLE scheduler delays; drop build.sh text test
VinSpee Nov 6, 2026
0626291
Fix the BLE test image's boot crash: leave the stack-limit guard off,…
VinSpee Nov 6, 2026
1f041dd
BLE test image: show the failing step and return code, and scroll the…
VinSpee Nov 6, 2026
38e5a81
no-mistakes(document): Correct BLE-TEST status example and marquee width
VinSpee Nov 6, 2026
cdf669b
BLE test image: create the CRC lock the controller start-up waits on,…
VinSpee Nov 6, 2026
bd5b54b
no-mistakes(document): Refresh BLE-TEST status after second flash
VinSpee Nov 6, 2026
cc05542
BLE test image: fix the null low-power handle, add a stall watch, kee…
VinSpee Nov 6, 2026
50606f0
no-mistakes(document): Update BLE-TEST status for third flash
VinSpee Nov 6, 2026
73c2531
feat: add a command-line flasher (op.mjs flash) and the openpocket-fl…
VinSpee Nov 6, 2026
f8b3a6a
feat: host the simulated pedal over HTTP (op.mjs sim) with a virtual …
VinSpee Nov 6, 2026
93174ff
BLE test image: proof counters for the radio, a public address, and a…
VinSpee Nov 6, 2026
861aea5
no-mistakes(document): Point README and pxota.js header at the CLI fl…
VinSpee Nov 6, 2026
7baa8b3
no-mistakes(document): Point README to the hosted simulator
VinSpee Nov 6, 2026
4d6b372
no-mistakes(document): Refresh BLE-TEST status, address and ADVERTISI…
VinSpee Nov 6, 2026
7a29a11
feat(tools): add a Mac-side UBOOT rescue tool for a crash-looping Bla…
VinSpee Nov 6, 2026
36f539f
no-mistakes(review): Report unreadable --loader file as RescueError
VinSpee Nov 6, 2026
bad4b35
feat(rescue): make the rescue a first-class command (op.mjs rescue, m…
VinSpee Nov 6, 2026
e254dfe
no-mistakes(review): Remove unused rescue gap tracking
VinSpee Nov 6, 2026
d66406e
Merge fm/openpocket-upstream-cli-flasher
VinSpee Nov 6, 2026
8b4ae72
Merge fm/openpocket-upstream-pedal-simulator
VinSpee Nov 6, 2026
74d08a9
Merge fm/openpocket-upstream-ble-advertise
VinSpee Nov 6, 2026
5fab7d1
feat: add a rescue mode to the command-line flasher (flash --rescue)
VinSpee Nov 6, 2026
4b4b186
Resume leftover update loader on fresh flash --rescue
VinSpee Nov 6, 2026
fb3e3fa
Refuse foreign update loaders in rescue dry run
VinSpee Nov 6, 2026
95cfc52
Document --package requirement for rescue loader resume
VinSpee Nov 6, 2026
19ae77b
feat(sim): hold buttons with down and up, timed as the firmware
VinSpee Nov 6, 2026
ea07ca7
feat(sim): the pedal's front panel as the simulator's page
VinSpee Nov 6, 2026
b458901
Key held buttons by e.code; assert served page types
VinSpee Nov 6, 2026
1cbecde
feat(sim): five dry guitar loops from Guitar-TECHS, CC BY 4.0
VinSpee Nov 6, 2026
b22cf0f
feat(sim): the engine as WebAssembly
VinSpee Nov 6, 2026
77e55c8
feat(sim): hear the pedal, from the skin
VinSpee Nov 6, 2026
69b9d14
Retry audio build after a failed start
VinSpee Nov 6, 2026
7741f46
Test all 52 presets and loops: WebAssembly matches native
VinSpee Nov 6, 2026
d1953b9
Correct simulator audio parity docs
VinSpee Nov 6, 2026
f1031c3
BLE test image: a read-only log channel over USB MIDI (DIAG) and node…
VinSpee Nov 6, 2026
ac4ee42
Add the Mac Bluetooth scan to op.mjs log and the verify-pedal skill: …
VinSpee Nov 6, 2026
c5edf3a
Merge fm/openpocket-upstream-midi
VinSpee Nov 6, 2026
f32669b
Merge fm/openpocket-upstream-cli-flasher
VinSpee Nov 6, 2026
c3c1435
Merge fm/openpocket-upstream-ble-advertise
VinSpee Nov 6, 2026
cc980e1
BLE test image: fix the silent radio (transmit I/Q trims, coexistence…
VinSpee Nov 6, 2026
5f63141
BLE test image: a read-only log channel over USB MIDI (DIAG) and node…
VinSpee Nov 6, 2026
191dafa
Add the Mac Bluetooth scan to op.mjs log and the verify-pedal skill: …
VinSpee Nov 6, 2026
7d90ec8
BLE test image: a connectable Bluetooth MIDI peripheral on the vendor…
VinSpee Nov 6, 2026
d1f7287
BLE test image: pass LE events to the host, log the link, peer and st…
VinSpee Nov 6, 2026
2be7a71
BLE test image: set the advertisement up again after a disconnection
VinSpee Nov 6, 2026
9204d9c
Docs: the Bluetooth test image is a BLE MIDI peripheral now
VinSpee Nov 6, 2026
d1fb2fb
Docs: the Chocolate Plus test on the pedal
VinSpee Nov 6, 2026
a0cb349
Docs: claim only what the Chocolate test showed
VinSpee Nov 6, 2026
939015e
tools/chocolate: build Chocolate Plus setup files from your own CubeS…
VinSpee Nov 6, 2026
5fa8e4d
OpenPocket v37: neural amp models (NAM A2-Lite) on the pedal, user mo…
isod89 Nov 6, 2026
cd64aba
v37: fix the link (relocation truncated): the engine back in the flas…
isod89 Nov 6, 2026
6529afe
v37 diagnostic: NAM bench screen at boot (the pedal showed BYP 223%)
isod89 Nov 6, 2026
4553306
NAM engine rewritten for this CPU: blocks of 32 frames, gather + dot …
isod89 Nov 6, 2026
30fb08d
v37 diagnostic: bench screen v2 (cost of each instruction kind and of…
isod89 Nov 6, 2026
c1bbfe8
NAM: dot products on the chip's vector unit (the FFT block), checked …
isod89 Nov 6, 2026
90d901b
Button lights on the BlackBox: they follow the blocks (issue #8)
steponme82 Nov 6, 2026
8dc8635
v37 diagnostic 4: cache RAM (seven data-cache ways as RAM) and where …
isod89 Nov 6, 2026
2bc1a65
v37 diagnostic 5: does the NAM work scale over the second core and th…
isod89 Nov 7, 2026
db06489
v37 diagnostic 6: NAM on the vector unit beside the CPU, post2, start…
isod89 Nov 7, 2026
b872e3f
v37 diagnostic 7: fix the start-up check, the CPU shares the dot prod…
isod89 Nov 7, 2026
c3f4fdb
v37 diagnostic 8: NAM on two cores, 16-bit weights for the vector unit
isod89 Nov 7, 2026
3808475
v37 diagnostic 9: NAM layers read straight from their rings (no gather)
isod89 Nov 7, 2026
4701e1b
v37 diagnostic 10: the vector unit reads from cache RAM, rdot unrolle…
isod89 Nov 7, 2026
c0eb48c
v37 diagnostic 11: the vector unit's speed by command shape
isod89 Nov 7, 2026
290f09f
v37 diagnostic 12: what a paired multiply-add costs when it waits for…
isod89 Nov 7, 2026
d73b13d
v37 diagnostic 13: rdq, the layer routines with each value loaded ahead
isod89 Nov 7, 2026
e7ec912
v37 diagnostic 14: CPU1 taking 20 or 24 frames, each effect's cost
isod89 Nov 7, 2026
64efcb3
v37 diagnostic 15: the engine block by block; drive, cabinet and NAM …
isod89 Nov 7, 2026
eee34cf
v37 diagnostic 16: filters two at a time
isod89 Nov 7, 2026
22f5dcd
v37 diagnostic 17: integer cabinet and drive filters, the drive at x2…
isod89 Nov 7, 2026
cb9497d
v37 diagnostic 18: NAM time per part, post3, post in whole blocks wit…
isod89 Nov 7, 2026
2808bb3
v37 diagnostic 19: tap pointers moved on without position arithmetic,…
isod89 Nov 7, 2026
b019aa9
v37 diagnostic 20: a NAM layer's groups of 4 frames in one call (rdl)
isod89 Nov 7, 2026
af1a65a
v37 diagnostic 21: every factory preset timed as the audio interrupt …
isod89 Nov 7, 2026
9d672f3
v37 diagnostic 22: CPU1 serves the network after its effects, roles t…
isod89 Nov 7, 2026
3a44f74
v37: the benchmark screen off at boot again, '2C' in the status corne…
isod89 Nov 7, 2026
0b32e1b
editor: browse TONE3000 and load an A2 model from it (their Select flow)
isod89 Nov 7, 2026
8ef4605
editor: OpenPocket's TONE3000 publishable key
isod89 Nov 7, 2026
07d12ad
OpenPocket v37: merge the version history
isod89 Nov 7, 2026
b428642
OpenPocket v37: the source of the release
isod89 Nov 7, 2026
8e1c591
Merge #17: button lights on the BlackBox follow the blocks (steponme82)
isod89 Nov 7, 2026
b755244
Merge #10: MIDI over USB-C (VinSpee)
isod89 Nov 7, 2026
b003d65
Merge #11: a command-line flasher sharing one protocol module with th…
isod89 Nov 7, 2026
8a6c625
Merge #14: a Mac-side UBOOT rescue for a crash-looping BlackBox (VinS…
isod89 Nov 7, 2026
0c67b16
Merge #12: host the simulated pedal over HTTP (VinSpee)
isod89 Nov 7, 2026
a43dd18
Merge #13: an advertise-only Bluetooth test image, make ble (VinSpee)
isod89 Nov 7, 2026
a638a87
Merge #16: connectable Bluetooth MIDI in the test image, the radio fi…
isod89 Nov 7, 2026
81d10b6
Merge #15: rescue mode in the flasher, the simulator's front panel an…
isod89 Nov 7, 2026
29e25c7
v37 with the merged pull requests: what's new, the AMP-page sound tes…
isod89 Nov 7, 2026
9e4f821
Button lights on the BlackBox: they follow the blocks (issue #8)
steponme82 Nov 6, 2026
f02c1ff
v37: Bluetooth MIDI in the release firmware, the battery gauge fixed
claude Nov 7, 2026
259b1fc
Bluetooth: the radio start-up on the main stack (fixes the first cras…
claude Nov 7, 2026
42faa9d
Bluetooth: poll the controller once a millisecond; BYP shows which co…
claude Nov 7, 2026
a3546a6
MIDI page: the radio's activity a second (diagnostic for the bypass w…
claude Nov 7, 2026
ca4bbc3
v37: Bluetooth out of the release for now; the battery gauge fix kept
claude Nov 7, 2026
99f2fb0
Battery: read the cell on PC9 as the stock firmware does
claude Nov 7, 2026
2ad39cc
Crackles: a soft knee at the output, late blocks shown; the battery r…
claude Nov 7, 2026
aca95fa
Merge #17 (rebased onto v37): button lights follow the blocks (issue #8)
claude Nov 8, 2026
8e48d67
OpenPocket v37: the source of the release
isod89 Nov 8, 2026
182a6dc
publish_github.ps1: the history merge and the release after a moved tag
claude Nov 8, 2026
ef5d48a
OpenPocket v37: merge the version history
isod89 Nov 8, 2026
991244e
PIT LOW OCTAVE: an octave down on the low strings only, the rest dry
steponme82 Nov 8, 2026
9ead8bb
LOW OCTAVE: power chords on the low strings, GROWL, no rumble
steponme82 Nov 8, 2026
61faf6c
Simulator: the WebAssembly engine rebuilt with LOW OCTAVE
steponme82 Nov 8, 2026
1640511
LOW OCTAVE: phase halving (14 ms instead of 64), LOWEST, COMP
steponme82 Nov 8, 2026
2883a3c
Simulator: the WebAssembly engine rebuilt with LOW OCTAVE v3
steponme82 Nov 8, 2026
318796c
LOW OCTAVE: every knob in plain values, a neck table, settings to sta…
steponme82 Nov 8, 2026
e3a3e3f
Simulator: the WebAssembly engine rebuilt with the LOW OCTAVE knob texts
steponme82 Nov 8, 2026
0535fe6
SYN PAD: a chord held under you, caught on the left footswitch
steponme82 Nov 8, 2026
c9c3403
tests/engine/pad.c: no overrun on the last, partial block
steponme82 Nov 8, 2026
394776f
Test branch only: build the WebAssembly engine in CI and upload it
steponme82 Nov 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .claude/skills/openpocket-flash/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
name: openpocket-flash
description: Build, dry-run and flash an OpenPocket package to a connected Donner Pocket X or M-VAVE BlackBox from the command line (node tools/op.mjs flash, over USB MIDI, no browser). Use to check that a built package fits the connected pedal, or, only when the captain explicitly instructs it, to write it. Covers the safety rules and the SD-card recovery.
---

# Flash a pedal from the command line

`node tools/op.mjs flash` (or `make flash ARGS="..."`) runs the same update protocol as the browser installer
(`docs/index.html`): the code is `docs/pxota.js` and `docs/pxpkg.js`, shared with the page; `tools/flash.mjs` only adds the
command line. Run everything from the repository root. The command runs on the host, not in the container: the pedal is a USB
device.

## Safety rules (read first)

1. **Never flash without the captain's explicit instruction** for this pedal and this package. A task that says "build" or
"test" is not that instruction. Without it, stop after the dry run and report.
2. **Always dry-run first**, and read its output: pedal, model and package must be the ones intended.
3. Never put `--confirm-flash` in a script, alias or default. It is typed for one flash, on the captain's say-so.
4. Keep the pedal plugged in and powered until the command prints `done`. Close the browser installer and any other MIDI
program first; they hold the port.
5. Only one pedal on the bus. The command refuses when none or several answer.
6. A failure prints the recovery pointer. Do not retry in a loop: report the output to the captain.

## 1. Set up once

```
node tools/op.mjs doctor # Node 18+ (Docker only for building)
npm ci --prefix tools # the one dependency, @julusian/midi (prebuilt RtMidi), into tools/node_modules
```

On Linux the MIDI library needs ALSA (`libasound2`); the pedal's USB MIDI port must be visible to your user.

## 2. Build the package

```
node tools/op.mjs package # docs/firmware/PocketX_9NN.fwsc, BlackBox_9NN.fwsc, manifest.json, build/SHA256SUMS
```

Details and failures: the `openpocket-toolchain` skill. To flash a package built elsewhere (for example a test image from
another branch), keep its `.fwsc` next to the `manifest.json` that was built with it and pass both.

## 3. Dry run (writes nothing)

```
node tools/op.mjs flash --dry-run
node tools/op.mjs flash --dry-run --package build-x/PocketX_925.fwsc --manifest build-x/manifest.json
```

It checks the package against the manifest (size, SHA-256, identity, UFW and CRC structure), finds the pedal by asking each
MIDI input for its identity (a read-only query), and refuses a package for the other model. The pedal's flash is not touched.
Success ends with `dry run ok: would update PocketX_923 -> PocketX_924`.

Refusals (exit 2, nothing sent to the pedal): no `--confirm-flash` and no `--dry-run`; package not listed in the manifest;
SHA-256, size or identity differs from the manifest; wrong model; no pedal; several pedals; pedal already in update mode
(power-cycle it).

## 4. Flash (only on the captain's explicit instruction)

```
node tools/op.mjs flash --confirm-flash [--package FILE] [--manifest FILE]
```

Same checks as the dry run, then: the pedal stages its update loader (step 1), the loader writes the app area (step 2), the
pedal restarts, and the command reads the version it reports. `done: the pedal reports PocketX_924.` is success (exit 0); a
version that differs from the package is a failure. It takes about a minute. Report the full output to the captain.

## 4b. A pedal that restarts in a loop (only on the captain's explicit instruction)

```
node tools/op.mjs flash --rescue --confirm-flash [--package FILE] [--manifest FILE] [--timeout SECONDS]
make flash-rescue ARGS="--confirm-flash"
```

The installer page's rescue mode, on the command line. Start it first, then plug the pedal in or let it restart. It polls every
MIDI port every 100 ms; the moment the pedal answers its identity query it sends the upgrade command (which stops the sound
processing, so the restarts stop) and installs the package of the pedal's model. `--package` pins one package: a pedal of
the other model is refused before anything is sent. Without `--package` the manifest's package for the pedal's model is used.
It waits `--timeout` seconds (default 180); an install already running is never cut short. A pedal that vanishes before the
update finished has had nothing written: it is waited for again. `--rescue --dry-run` waits for the pedal and checks the model,
writing nothing. Reached states: the running firmware's MIDI port, and an OpenPocket update loader left half-done (finished only with `--package`: a loader does not report its model; a foreign loader is refused). Not reached
(same as the page): a firmware that crashes before it answers, and the chip's mask-ROM loader `WL80UBOOT` (USB 4C4A:8057, no
MIDI); both need the SD-card recovery. Timeout is exit 1 with the recovery text. One pedal on the bus: the first to answer is used.

## 5. Recovery

A failure (exit 1) prints `FAILED: ...` and the recovery text. In order:

1. Unplug USB, power-cycle the pedal, run `--dry-run` again to see what it reports. A pedal in update mode (`ota-...`) is
finished by the browser installer's resume button, or by this command after a power-cycle.
2. If it stays dead, the SD-card recovery in the README (`flash/`: fm1buddy on an ESP32-S3; the image comes from your factory
backup with `make appimage DUMP=fm1-1.bin` or `makeimage.bat`). Only the captain does this.

Never publish or commit a pedal backup (`fm1-1.bin`, `fm1-300.bin`): they hold the official firmware.

## Tests

`node tests/test_flash.mjs` (part of `node tools/op.mjs test`) drives the real command against the simulated pedal that runs
the pedal's own `ota.c` and loader code: update, dry run writes nothing, wrong model, damaged package, missing confirmation,
no pedal, two pedals, failure with the recovery text, and `--rescue` against a pedal that restarts in a loop (answers on the
Nth restart, never answers, vanishes mid-transfer, wrong model, missing confirmation). Real hardware is not covered by any test.
68 changes: 68 additions & 0 deletions .claude/skills/openpocket-rescue/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
name: openpocket-rescue
description: Rescue a Donner Pocket X / M-VAVE BlackBox that restarts in a loop or no longer boots, from a Mac with no extra hardware (make rescue, tools/uboot_rescue.py, over the chip's USB UBOOT boot state). Use when the installer's rescue mode cannot finish, the pedal loops after a bad image, or a flash left the app area broken. Covers when to use it, the safety rules, who runs the sudo write, and how to read the result.
---

# Rescue a looping or bricked pedal

The pedal's mask ROM always enters the USB boot state ("UBOOT": USB `4C4A:8057`, shown as `WL80UBOOT1.00`) when the app is bad,
and it cannot be damaged. `tools/uboot_rescue.py` waits for that state, loads JieLi's RAM loader and rewrites the app area from
the official package. Full walkthrough and the record of the first real run: `docs/RESCUE.md`.

## When to use it

- The pedal restarts in a loop and the installer page's rescue mode (or `node tools/op.mjs flash`) cannot finish. Try those first:
they need no root.
- The pedal stays in the boot state (no loop, no sound) after an interrupted flash: the rescue finishes the job.
- Not for Bluetooth or firmware behaviour questions, and not on a pedal that works.

## Rules (read first)

1. **Read-only first.** `make rescue` writes nothing to the flash (the loader goes to RAM only) and saves a backup of the whole flash
to `~/OpenPocket-rescue`. Read its output before anything else.
2. **The captain runs the write.** `make rescue-write` and every `sudo` command need the captain's password and instruction. An
agent runs neither: prepare the exact command and give it with the `!` prefix.
3. The write range is only whole 4 KiB sectors in `[0x4000, end of the package)`, `[0x4000, 0xA3000)` for BlackBox. Never write
the head `[0, 0x4000)` or anything above the package. The tool refuses; do not work around it.
4. Never chip erase (`FB02`) and never burn a key (`FC12`). The tool cannot send them; do not add them.
5. Stop on any `REFUSED` (wrong device, chip key not `0x980F`, flash not 1 MiB, head differs from the package). Nothing was written;
report the message.
6. Keep the first backup. Never commit or publish a backup or a package: they hold the official firmware.
7. After a restore, do not reinstall the Bluetooth test image until the cause of the loop is known.

## How

```
brew install libusb # once
make doctor # rescue lines: libusb, official package, loader, root
make rescue # read-only: INQUIRY, loader to RAM, key, flash id, backup, head check, sectors that differ
make rescue-write # only on the captain's instruction, after the read-only run passed
```

Options go after `ARGS=` (`make rescue ARGS="--package FILE.fwsc"`; `--image flash.bin`, `--loader FILE`, `--backup-dir DIR`,
`--attempts N`). Plug the pedal straight into the Mac (no hub). Start the command first; it waits for the pedal and starts by
itself the moment it appears (the pedal is in UBOOT about 44 s of every 70 s while it loops). Needs the official BlackBox package
(default `~/Documents/BlackBox (2).fwsc`) and Python 3.

## Reading the result

| Output | Meaning |
| --- | --- |
| `INQUIRY: WL82 UBOOT1.00 1.00` | the right device |
| `loader running: chip key 0x980f, flash id ... (1024 KiB)` | the checks passed; record the flash id |
| `backup saved: ...dump.bin` | the rollback copy is complete on disk |
| `head [0, 0x4000) matches the image` | the package fits this pedal |
| `N of 160 sectors ... differ` | what a write would do; a rerun after a partial write shows fewer |
| `Read-only run finished, nothing was written` | exit 0, the read-only run is done |
| `Done: N sectors written and verified` | exit 0 after `--write`; power-cycle the pedal |
| `the pedal went away ... waiting for it again` | the window closed; it resumes by itself |
| exit 1 `ERROR` | setup: libusb, `sudo`, missing package; the message names the fix |
| exit 2 `REFUSED` | a safety check stopped it, nothing written |
| `LIBUSB_ERROR_ACCESS` | run with `sudo` (`make rescue` does); if it persists as root, report it |

Save the output of a real run (`make rescue 2>&1 | tee rescue.log`) and add its numbers to `docs/RESCUE.md`.

## Tests

`make rescue-check` runs the tool against a simulated UBOOT pedal (protocol, guards, resume, no hardware); it is part of
`make test`. Hardware behaviour is not covered by any test.
74 changes: 74 additions & 0 deletions .claude/skills/openpocket-simulator/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
name: openpocket-simulator
description: Start and drive the hosted OpenPocket simulated pedal (node tools/op.mjs sim, make sim): the firmware's real screen, editor protocol and preset store running natively behind a local HTTP API, with a code-drawn front panel of the pedal as its page. Use to press or hold footswitches and buttons, turn the knobs, read the screen as a PNG, read the pedal state, send SysEx, dump or reset the flash, or to use the installer and editor pages against a simulated pedal in a browser. Use for verifying UI, editor-protocol or preset-store changes without a pedal, and for trying a preset or a screen. Use to hear a preset through bundled guitar loops, a file or a computer input. Not for USB or Bluetooth (not simulated).
---

# OpenPocket simulator

The pedal's own `ui.h`, `px_editor.h` and `px_store.h` run natively (`tests/edsim.c`) behind HTTP. Full reference:
`docs/SIMULATOR.md` (every endpoint, the state fields, what is not simulated). Run from the repository root. Needs Node 18+
and a C compiler (`node tools/op.mjs doctor` says whether `cc` is there); no Docker.

## Start

```
node tools/op.mjs sim --port 0 --reset & # prints: OpenPocket simulator (pedal) listening on http://127.0.0.1:PORT/
```

`--port 0` picks a free port: read the URL from the first line of output. Without `--port` it is 8766. The flash lives in
`build/sim/flash.bin` (`--flash FILE`); without `--reset` a session continues where it stopped. The first start compiles
the simulator (a few seconds). Stop it with Ctrl-C or by killing the process **you** started.

## Drive (with `U=http://127.0.0.1:PORT`)

| Do | Request |
| --- | --- |
| A footswitch or button | `curl -X POST $U/api/press/NAME`: `fs_up` `fs_dn` `fx` `home` `push` `mod` `amp` `cab` `dly` `rev`; `push_long` (SAVE), `dpush` (MOVE), `home_long` (tuner) |
| Hold a button | `curl -X POST $U/api/down/NAME`, later `$U/api/up/NAME`: the same ten names, timed as the firmware does (HOME 0.7 s, PUSH 0.8 s, double click 350 ms) |
| Turn the knob | `curl -X POST $U/api/knob/3` (negative: other way; `?fast` for steps of 5) |
| Volume, battery, tuner pitch | `POST $U/api/volume/55`, `/api/battery/3640[/USB_MV]`, `/api/tuner/110` |
| Bypass, load in the status text | `POST $U/api/bypass/1`, `/api/load/17` |
| Read the state | `curl -s $U/api/state` (slot, name, mode, focused block, the whole sound, `frame`, `held`, `knob`) |
| Read the screen | `curl -s "$U/api/screen.png?scale=2" -o screen.png`, then look at it; `screen.rgb565` is the raw pixels |
| SysEx | `curl -s -X POST $U/api/sysex -d 'f0 7d 4f 50 01 f7'` returns `{"reply":"hex"}` (`docs/EDITOR-PROTOCOL.md`) |
| Dump / reset the flash | `curl -s $U/api/flash -o flash.bin`; `curl -X POST $U/api/reset` |

Every request is applied before it is answered. The pedal's pages: `chain` (PLAY) -> `block` -> `edit`; `push_long` opens
`save` (knob picks the slot, `push` saves); `home_long` opens the tuner, any control leaves it. A worked path to change a
setting: `press/amp`, `knob/2`, `press/push`, `knob/8`, then `state` shows `preset.p[5][0]` up by 8 and `dirty: 1`.
Prove a screen claim by reading `screen.png`, not only the state.

## Browser

`$U/` is the pedal's front panel (`tools/sim/pedal.html`) with the live screen in its LCD: press and hold the buttons and
footswitches, drag or wheel the knobs, click PARA for PUSH, or use the keys listed on the page. Scripts on the page have
`window.pedal` (`down`, `up`, `tap`, `turn`, `master`, `state`); a control is `[data-control=NAME]`, `#master` or `#para`.
`$U/plain` is the plain page with the screen, every control as a button, the battery, the tuner pitch and the state.
`$U/site/index.html` (installer) and `$U/site/editor.html`
(editor) are `docs/` with a virtual Web MIDI injected, so Connect finds the simulated pedal in any browser; drive them with
`chrome-devtools-axi` or Playwright. For the installer use the update flow instead of the pedal:
`node tools/op.mjs sim --update FLASH.bin` (an image such as `flash_old.bin` from `python3 tests/fixtures.py DIR`).

## Sound

The skin's Sound section runs the real engine (`engine/px_dsp.c`, built to `tools/sim/px_engine.wasm`) in an AudioWorklet:
pick a bundled loop (`assets/loops/`), a dropped file or a device, press Play (the first click starts the audio; a device
starts muted), and the preset on the screen is what you hear. `window.pedal.audio()` reports it (`running`, `heard`,
`muted`, ...), and `#in-meter`, `#out-meter` carry `data-db`. Without a browser, `node tools/sim/render.mjs OUTDIR [preset]`
renders the loops to WAV. After any change to `engine/`, run `node tools/op.mjs wasm` (Docker) and commit the new binary:
`tests/test_audio.mjs` fails otherwise. `docs/SIMULATOR.md` ("The sound") has the reasons and limits.

## Limits (say so when verifying)

No LEDs or bypass footswitch (the firmware has none: the skin's backlights are
its own reading of the state), no USB or Bluetooth. `press/NAME` has no timing, `down` and `up` do. The panel's TUNER line
is the stock firmware's: in OpenPocket both footswitches are two presses, and the tuner is a long press of HOME.
`press()` and the `button_*` functions in `tests/edsim.c` mirror the control dispatch of `firmware/openpocket.c`: if that
loop changes, change them too. The proof is the firmware's real code on a PC, not the pedal.

## Tests

`node tests/test_sim.mjs` (also in `node tools/op.mjs test`) covers the API, the flash file and the shim.
`node tests/test_pedal_input.mjs` covers the skin's input mapping, and `node tests/test_pedal_page.mjs OUTDIR` drives the skin
in Chromium and saves screenshots (needs Playwright). `node tests/test_audio.mjs` covers the engine and the loops, and
`node tests/test_audio_page.mjs OUTDIR` the Sound section in Chromium with a fake audio input (needs Playwright).
7 changes: 5 additions & 2 deletions .claude/skills/openpocket-toolchain/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ Run commands from the repository root.
| Build the firmware, the update loader and the installer packages | `node tools/op.mjs build` |
| Build, then write `build/SHA256SUMS` | `node tools/op.mjs package` |
| Host tests (engine, preset store, screen, simulated pedal, installer, editor) | `node tools/op.mjs test` |
| The simulated pedal on a local port (host `cc`, no Docker; skill `openpocket-simulator`) | `node tools/op.mjs sim` |
| Remove every build output (needs no Docker) | `node tools/op.mjs clean` |
| A shell in the container | `node tools/op.mjs shell` |
| Dry-run or flash a built package to a connected pedal (host, USB MIDI) | `node tools/op.mjs flash`: see the `openpocket-flash` skill |

Outputs: `firmware/openpocket.bin`, `docs/firmware/PocketX_9NN.fwsc`, `BlackBox_9NN.fwsc`, `manifest.json`. All of them
are gitignored. Two clean builds give identical bytes; a changed checksum means the source or a pin changed.
Expand All @@ -50,8 +52,9 @@ Follow `references/updating-pins.md`. Never bypass the checksum check.

## Safety rules of this repository

- **Never flash a device from an agent.** No `flash`, no WebUSB or MIDI write to a real pedal. Only the captain installs,
from the browser installer, by hand. Hardware checks are in `docs/MIDI-TEST.md` when present.
- **Never flash a device unless the captain explicitly instructs it.** `node tools/op.mjs flash --dry-run` is allowed; `--confirm-flash`
and any other write to a real pedal only on the captain's say-so, following the `openpocket-flash` skill. Builds and tests never
touch a pedal. Hardware checks are in `docs/MIDI-TEST.md` when present.
- **Never publish an unreviewed package as the installer default.** A test package is a separate, labelled file and is
not listed in the default `docs/firmware/manifest.json`. `PUBLISH-*.bat` is the maintainer's tool: do not run it.
- **Keep the official-firmware analysis out of the repository.** Facts about the vendor's firmware (offsets, tables,
Expand Down
Loading
Loading