Skip to content

ble: connectable Bluetooth MIDI, radio fix and Chocolate Plus control - #16

Merged
isod89 merged 31 commits into
isod89:mainfrom
VinSpee:fm/openpocket-upstream-chocolate-bluetooth
Nov 8, 2026
Merged

isod89 merged 31 commits into
isod89:mainfrom
VinSpee:fm/openpocket-upstream-chocolate-bluetooth

Conversation

@VinSpee

@VinSpee VinSpee commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

This depends on #10, #13 and also #11, and it stays a draft until they merge. The branch merges their head branches, so the diff includes their changes for now. The new work is the 10 commits after those merges. #11 is needed because the link's status reaches the computer through the log channel, and op.mjs log uses that PR's MIDI transport. For the same reason this branch carries the two log channel commits from #15. They drop out of whichever of the two merges second.

It adds three things, all inside the make ble test image except the toggles. The radio fix corrects two faults found by comparing with the stock firmware's start-up: the transmit gains were never set, and wf_rf_common_init returned nothing, which switched on the coexistence arbiter. It also reads the stored radio records raw from flash. The link makes the pedal a connectable Bluetooth MIDI peripheral named OpenPocket, with a parser in firmware/px_ble_midi.h and pairing. Over USB and Bluetooth, CC 8 to 19 now flip an effect on value 0, so a preset change cannot put a footswitch out of step. tools/chocolate/ builds Chocolate Plus setup files from a user's own CubeSuite export, and its README explains the format as understood, the steps and the risks. No device export is in this PR, and the tests use a made-up one.

On a real BlackBox, a Mac connected and sent the messages: CC 12 value 0 turned DRIVE off, CC 24 switched it on and off, and PC 5 and PC 9 loaded those slots, each read back over USB. A Chocolate Plus on H paired by itself, and footswitches A to D changed Drive, Mod, Delay and Reverb. The pedal's log keeps only the last message, so it does not show which CC numbers those taps sent. Separately, a Mac listening to the Chocolate received CC 24, 28, 29 and 30 from the file built with today.json. make ble on this branch builds the same image that ran in that test, with a SHA-256 starting 230876156ad7.

The six-effect file from final.json is untested as a whole, and its Mode 5 layout is inferred. Pairing does not persist across a power cycle, because bonds are kept in RAM, and reconnection after one is untested. Nobody has listened for crackle or measured the DSP load under a link, the longest link has lasted minutes, and the pedal only receives over Bluetooth. node tools/op.mjs test, build and ble pass on this branch. The Playwright page tests did not run because Playwright is not in the build image.

… send, settings byte, clock to delay time

firmware/px_midi.h has no hardware and is tested on a PC (tests/midisim.c). px_dly_value_for_ms picks the delay TIME
step nearest a time.
… pedal's changes out, MIDI page, settings in the store

usb.c is unchanged. Nothing emptied its MIDI input ring, which also stalls the editor's and the installer's SysEx when it
fills; px_midi_service now drains it every pass. FX held 0.8 s opens the MIDI page (channel, send, clock to delay); the
settings are one byte in the preset store's header (0 = defaults, as every earlier save).
…t, docs and the hardware test plan

OP_CHANNEL=midi-test builds firmware/openpocket-midi-test.bin and docs/firmware/midi-test/ with their own version and
manifest; the release files and the default manifest are not touched. docs/MIDI.md (design, CC map, Bluetooth findings and
future work), docs/MIDI-TEST.md (checklist for the captain), README and SOUNDS-AND-CONTROLS.
A separate build target that links JieLi's Apache-2.0 LE controller
(third_party/jieli-ble) into the normal firmware and sends one scannable
advertisement named OpenPocket-BLE-TEST with the BLE-MIDI service UUID.
No connections, pairing or MIDI over Bluetooth. The normal firmware and
installer packages are byte-identical; the VM and BTIF areas are not touched.

Built, linked and packaged; not yet run on a pedal (docs/BLE-TEST.md lists
what is unverified).
… and refuse a link that would trip it

The first hardware run faulted at the first instruction of the first BLE task (EMU bit 3, stack limit):
the task stacks lie outside [_ustack_lo, _sstack_top]. The test image is now built with FM1_GUARD_STACK off
(OP_GUARD_STACK=0); the normal firmware is byte-identical. app.ld fails the link if the stack guard is on while the
task stacks are outside the window. docs/BLE-TEST.md lists the other guards as checked on paper only.
… full failure line

The status text now reads STARTING <stage> or FAILED <step> <code>; a timeout names the stage it stalled in. On FAILED the
whole line (step and stage in words) scrolls across the status area in the 500 ms redraw. The logic is in ble_status.h and
tested on the PC (tests/test_ble_status.c). The normal firmware is byte-identical.
… and two related fixes

The controller task deadlocked in ll_init -> thread_create -> crc_get_32bit: the lock is created by the vendor
__crc16_mutex_init(), which only the SDK's own start-up calls (FAILED 5 3 on the pedal). ble_test_start now calls it.
ll_hci_adv_set_params gets a six-byte direct address instead of NULL (the library reads six bytes), and the context
switch saves r4-r15, the registers the compiler preserves. The failure line gains two stage markers (radio replay
returned, baseband interrupts requested) and, at a timeout, the object the controller task waits on.
Normal firmware is byte-identical. Not run on a pedal.
…p Bluetooth off after a failed boot

The third hardware run froze in the first ble_poll: the library asserts the handle low_power_get returns, our empty
stand-in returned null, and a failed assertion parks in P33_SYSTEM_RESET (an endless loop) until the watchdog resets.
low_power_get now returns a non-null handle. A stall watch in the 10 kHz timer tick turns a ble_poll that has not
returned after 4 s into the normal crash screen (VEC 63, PC and RETS of the stuck code). Bluetooth is not started on a
boot that follows a failed one, so the boot guard cannot send the pedal to the loader. Normal firmware is byte-identical.
…ash skill

The flasher reuses docs/pxota.js and docs/pxpkg.js, the modules the installer page runs.
It requires --confirm-flash for any write, supports --dry-run, checks the package
against the manifest SHA-256, refuses the wrong model, verifies the version the pedal
reports, and prints the SD-card recovery pointer on failure. Tested against the
simulated pedal.
… spec check of the advertisement

The fourth flash showed ADVERTISING but no scanner saw the pedal. While advertising, the status now scrolls counters of
the two baseband interrupts (counted by ble_isr.S before the library's handlers run), HCI packets returned, library
timers fired, slot-timer stand-in calls, and the baseband, interrupt-config and block-enable registers. The controller's
public address was never set (all zeros): it is now a fixed non-zero locally administered address. The advertisement is
in ble_adv.h and tests/test_ble_status.c checks it against the Core Specification. Normal firmware is byte-identical.
… host, and a toggle map for footswitches

The pedal now advertises ADV_IND as OpenPocket and runs JieLi's host: a GATT server with the BLE MIDI service, Just Works pairing with bonding, and the link layer's real members in place of their stand-ins. A write to the characteristic goes through a BLE MIDI parser into the same handler as USB. CC 8 to 19 flip a block on value 0. None of this has run on a pedal yet.
…and-ins

The event mask setter was a stand-in, so LE meta events never reached the host and a connected central looked like nothing. Link it, and add the link and trace lines to the log channel.
@steponme82

Copy link
Copy Markdown
Contributor

@VinSpee @isod89

Nice work on the radio fix! One thing about where ble_vm_load looks for the radio records: it may miss them on a BlackBox that ran BlackBox_021.

The VM is not in the same place in every official version (region table of each package):
1: 016: VM at 0xA3000 (108K), BTIF at 0xBE000
2: 020: VM at 0xA5000 (100K), BTIF at 0xBE000
3: 021: VM at 0x9F000 (104K), BTIF at 0xB9000

OpenPocket's install writes [0x4000, 0xA3000). On 016 and 020 that stops before the VM. On a pedal that ran 021 it overwrites the first 16K of the VM (0x9F000 to 0xA3000).

ble_rf.c scans 0xA3000 to 0xBE000, so on a 021 pedal:
1: it never looks at 0x9F000, where 021's VM starts
2: it goes past the end of 021's VM (0xB9000) into its BTIF
3: if the pedal ran 020 before 021, the old VM at 0xA5000 may still be there, and the scan could pick up those records instead

I don't know if the trims change between versions, maybe they're factory values that get copied over. But it's worth knowing which firmware your test BlackBox ran before OpenPocket. If it was 016 or 020, the 021 case isn't covered yet.

Two ideas:
1: show ble_vm_area on the screen or in the log, so every tester reports where the records came from
2: for the installer: the OpenPocket app ends at about 0x9DB00 today. Could the BlackBox package stop at 0x9F000, like 021's own? Then 021's VM stays whole on new installs

I can test #16 on my BlackBox. Mine ran 020 before OpenPocket, so it can't cover the 021 case, but it's a second pedal. I also have a Chocolate Plus and a plain Chocolate, so I can try the plain one too. Tell me which image to flash and what to check.

@isod89

isod89 commented Nov 7, 2026

Copy link
Copy Markdown
Owner

Hey everyone, @VinSpee @steponme82

Sorry for going quiet lately. I haven't forgotten about you or your work. I've been deep in v37, and it took over everything.

Here's what's going on. v37 brings real Neural Amp Modeler (NAM A2) captures to the pedal: 12 factory amp models, every factory preset rebuilt around them, and a web editor panel to load your own A2 captures from TONE3000. All of that works. What's left is making the network run in real time on the pedal. Right now it costs more CPU than the pedal has, and it drops to bypass. To fix that, we're studying how the stock M-VAVE firmware manages it. It uses the chip's vector unit in the background and both cores in parallel. I'm rewriting our engine around that idea, with our own code.

What this means for you: the code has changed a lot in the last few days. The NAM engine, the audio path and the memory layout are all different, and more big changes are coming before this settles. Anything built on the current main will probably conflict, so it may be worth holding off on big changes in those areas for now.

Vince, thank you for all the PRs: MIDI over USB-C, the CLI flasher, the hosted simulator, the rescue tool and especially the BLE test image. Your BLE findings (the proof counters, the advertising spec check, the low-power handle fix) are exactly what I need for the next step, which is Bluetooth working like the original pedal, including the Chocolate controller. I've read through them and I'm planning around them. Thank you for the work and the patience.

As soon as the CPU problem is solved, I'll merge your PRs. I'll take care of the conflicts on my side, since our latest changes are what caused them, and I'll ping you if anything needs a second look.

Thanks again, and sorry for the silence.
Sam

@isod89
isod89 merged commit a638a87 into isod89:main Nov 8, 2026
1 check passed
isod89 added a commit that referenced this pull request Nov 8, 2026
…d sound, pedal log reader, scan helper (VinSpee)

Conflicts: the log channel as #16 made it (5 lines, DIAG = 23, link and trace);
Makefile, docs, tests/run_tests.sh, tools/op.mjs (everything kept: flash --rescue,
wasm, log, rescue, sim). The simulator's WebAssembly engine is rebuilt from v37's
engine (it now plays the NAM amps); engine/wasm.sh and tests/test_audio.mjs count the
NAM sources (px_dsp.c includes them) in the engine's checksum.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LFaxqW3CpFrLCvqoteTUv9
@VinSpee

VinSpee commented Nov 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, I'm excited for NAM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants