Apps Bluetooth support & 1.0 breaking changes#67
Draft
microbit-matt-hillsdon wants to merge 75 commits intomainfrom
Draft
Apps Bluetooth support & 1.0 breaking changes#67microbit-matt-hillsdon wants to merge 75 commits intomainfrom
microbit-matt-hillsdon wants to merge 75 commits intomainfrom
Conversation
This contains breaking changes (e.g. flash progress interface, connect
signature, device error codes) and will form part of a v1 at some point.
Migrating USB-only code should be trivial though.
Changes:
- Switch to capacitor-ble for bluetooth
- Quite a simplication as the service/characteristic lookups are deferred to
point of use and the interactions are internally queued.
- Support DFU and partial flashing on iOS/Android platforms
- Drop a bunch of workarounds that need reevaluating after the switch
- Temporarily drop uBit name support due to capacitor-ble limitation
- Don't try to start notifications on absent services. Prevents issues when not
in application mode for a flash.
- Improve connect interface (which had a misleading return value) and
connect/flash progress.
This branch is going to be long lived for a month or two during apps work, then
we'll loop back around and see what it means for Web Bluetooth - does it
replace it or do we have both implementations.
Design issues:
- Should connecting and connecting for flashing both be the same flow? Or are
the ideas different enough that we split them? It's nice in that it matches
USB but it's also quite different because of pairing.
0d58d9d to
fa96588
Compare
- Use .js extension for imports consistently - Fix typo in exports for types (also on main, but at least VS Code seems to cope)
This might be a behaviour change for bluetooth but is consistent with USB.
For USB there's not really anything to do but let's keep it consistent
This has caused app-level issues because disconnected doesn't let the app understand that a reconnect will automatically happen.
You can want to pause due to a hidden tab, but defer it due to flashing then become visible before you ever did pause. But we went ahead with the pause incorrectly.
Remove them from the connection status. Check them before connect but also provide pre-flight API for UX flows. This better matches the iOS/Android permission model and is easier for client code to manage.
This is trivial for the connections and saves apps from additional state management as they often need to understand which transition happened.
…arting notifications (#76) - Attempt to connect four times before throwing error. - Throw immediately if disconnect occurs whilst connecting instead of waiting for timeout. - Ensure latest services are found.
Fixes connecting to micro:bit on Android after full flashing. Otherwise there is a "Characteristic not found" error from trying to get board version.
This seems to help significantly with reconnect on Android, where as the retry solution has encountered issues (unexpected disconnects, trouble discovering services that just increasing the delay didn't help with). Remove the retries for now - we might well reinstate but we need to do so with more care as it's led to very long retries in the case where there is no device due to app-level retries doubling the retry count. Tested on Android and iOS with a branch of ml-trainer that removes the delay after flashing.
In ml-trainer you can see this if you: 1. connect, pair, DFU 2. trigger connection errors until start-over state 3. edit the pattern away from the correct one 4. note you go on to partial flash In Web Bluetooth this wasn't an issue as we always did a clearDevice but we should fix it here for other library scenarios.
- Add a new error code for pairing information lost on peripheral. - Add new native-only progress stages (ProgressStage.CheckingBond and ProgressStage.ResettingDevice). - Add abort signal to allow for stopping of scanning (and potentially other steps in future)
…g of device on disconnect (#85) - Remove auto reconnect logic and `ConnectionStatus.Reconnecting` status in favour of the consuming app handling reconnections where necessary. - Remove discarding of connection/device on disconnect in favour of the consuming app calling `clearDevice`. So unless the app calls `clearDevice`, we assume the same device is used for the connection. - Extend radio bridge connection timeout to 10_000ms to match Bluetooth. - Remove no longer needed `ignoreDelegateStatus` - Keep serial session open when connecting to allow for reconnection after switch tabs
1. Skip 64-byte blocks that are entirely 0xFF during BLE partial flashing, matching the iOS native app's approach. The device erases each flash page when it receives a write at a page-aligned address, so interior 0xFF blocks are redundant. Flash page size is determined from the board version (V1: 0x400, V2: 0x1000). 2. Handle MakeCode hex files that nrf-intel-hex rejects. Older thin (non-universal) hex files have trailing blank lines or embedded source in custom Intel HEX record type 0x0E after the EOF record. Added truncateHexAfterEof() in hex-flash-data-source.ts, used by both BLE (bluetooth.ts) and USB (usb-partial-flashing.ts) code paths. Region bounds were verified against the iOS app's hexDataToAppRegion calculation which derives [SoftDevice end, bootloader start). Added tests covering MakeCode v0–v8 hex files (both universal and thin) with exact expected region assertions (start, end, hash).
…lative delays (#87) - Add `deviceBondState` option to Bluetooth connect. - Set device as bonded when connection is established successfully. - Set device as not bonded when connection fails. - Remove speculative delays. - Ensure partial flashing errors are raised so that the app can handle them.
Simple monorepo with npm workspaces. We'll always build everything.
This reduces partial flashing time. The `waitForDisconnect` after partial flashing has been moved to `connect`. Most of the time, the disconnect can happen in the background, except when connecting straight after partial flashing. The disconnect interferes with connecting on Android. CreateAI project hex is added as a Capacitor app file option for testing purposes. The Capacitor app has been updated to include a 'Connect' and 'Disconnect' button. Live accelerometer data is displayed if available and connected. --------- Co-authored-by: Matt Hillsdon <matt.hillsdon@microbit.org>
Compare the MakeCode application hash from the hex file (SHA-256 truncated to 8 bytes, at magic+24) against the device's MakeCode region hash. When they match the program is already on the device, so we reset to application mode and return AlreadyUpToDate instead of reflashing identical data. This mirrors the optimisation in PXT's own Web BLE partial flashing code. Fixes #651
Capacitor BLE, Filesystem, Core, and Nordic DFU are now peer dependencies so consuming apps control versioning and avoid duplicates. This follows the pattern used by Nordic DFU and other similar plugins.
Breaking: Replace EventTarget with custom typed event system
…tion-state Document post-flash connection state differences
Breaking: Move serialWrite from DeviceConnection to USB only
Document radio bridge as experimental / limited
The partial flashing code already duplicated significant portions of the dapjs protocol stack. Consolidate everything into a single cmsis-dap.ts module covering the subset we use: SWD transport, Cortex-M debug, and DAPLink serial/flash vendor commands. Also renames DAPWrapper to USBDeviceWrapper and disambiguates device field names (bleDevice/usbDevice).
Fix stale serial data appearing after flash by addressing a race in the serial stop/start cycle. When stopSerial() is called, an in-flight serialRead() can still deliver data after the stop, refilling any consumer line buffer that was cleared on reset. Fix by suppressing data delivery once polling is set to false, and by firing the serialreset event after the polling loop actually exits rather than when stop is requested. Drain DAPLink's serial buffer before flash to discard output that accumulated between the last serial read and the halt. After flash, start serial listening before resetting (partial flash only) so early program output is captured in DAPLink's 512-byte ring buffer. Simplify flashAsync: remove the disconnect/reconnect cycle (invalidate DAP state instead, keeping USB open) and remove the timeout-based fallback from partial to full flash which masked connection problems. Minor: - Strip \r in demo app serial listener (for CRLF) - Fix vitest exclude pattern for build directory
Rename reconnectDaplink for clarity. Add ability to inject faults into flashing via the demo app to test this flow.
Move board info caching and logging up to connection.ts, reducing device-wrapper.ts to a thin composition root. Rename fields for clarity: device→usbDevice/bleDevice for raw devices, connection→device for wrapper state.
We now control all sources of errors other than the browser cases matched by regexp. Review the browser ones and link to the Chromium source, which fortunately proves they're not localized.
DAPLink's CDC serial path races with WebUSB vendor serial reads for the same UART ring buffer, causing truncated serial output after flash. Work around this by pushing NUL bytes through the target's UART to fill DAPLink's internal buffers before flashing. Once full, CDC stops consuming and post-flash output is preserved for vendor reads. V1 (nRF51) runs a Thumb blob for the legacy byte-at-a-time UART. V2 (nRF52833) uses SWD register writes to trigger a UARTE DMA transfer. Both paths fully configure the UART from scratch (pins, baud rate) since the previous program may not have set it up. Only applied on the first flash after physical USB connection and only when serial listeners are attached. See: ARMmbed/DAPLink#903
Tests basic flashing flows including checking serial output. Move the hex files as they're useful more widely than the capacitor project. Remove fault injection from the demo now hardware-test has it.
- Inline what we need from the CMSIS-DAP protocol handling, removing the dapjs dependency
- Removes a bunch of duplicate code for partial flashing with very confused layering
- Removes vendored copy for ESM packaging reasons
- Enables more structured error handling / no string matching and errors from timeouts
- Allows us to push logging into this code
- Fix partial flash retry by reinitialising SWD (potentially fine before, maybe refactor broke it)
- Saturate DAPLink CDC buffers before flash to prevent serial data loss
- We'll see how this goes on the apps branch before shipping it for real.
- Improve serial data handling around flash
- Simplify device wrapper and clarify naming across USB and Bluetooth
- Simplify error handler; review error landing and logging
- Deduplicate ABORT_ALL constant between cmsis-dap and arm-debug
- Add test coverage for extracting interfaces
- Add a human-in-the-loop hardware test runner (USB flash, fallback paths, serial integrity, replug)
- Minor demo improvement: the ability to easily reflash the same hex without faff
This targets the apps branch because of the scale of the change and the fact it's targeting 1.0 release rather than because of the apps as such.
…#111) Delete the shared constants.ts file, inlining DAPLink vendor commands as bare constants in daplink.ts and FICR register addresses in device-wrapper.ts. Both were only used by USB code.
- Move bluetooth code into src/bluetooth/ with services/ subdirectory - Move radio-bridge code into src/radio-bridge/ - Move universal-hex code into src/universal-hex/ - Move USB-only files (serial-events, board-serial-info, promise-queue) into src/usb/ - Move bluetooth-only file (device-bond-state) into src/bluetooth/ - Consolidate sensor type files into service-events.ts - Move DeviceBondState export from main entrypoint to bluetooth entrypoint - Move serial event exports from main entrypoint to usb entrypoint - Update package.json exports and app imports accordingly DeviceBondState and the serial events have had their public export changed (to make them bluetooth and USB specific respectively)
Chrome 105 shipped August 2022. The affected Chromebooks (those whose auto-update expiration landed exactly on 105) are a vanishingly small population. Drop the user-agent check so those devices can at least attempt a USB connection.
Convert ConnectionStatus, ProgressStage, DeviceSelectionMode, and ButtonState from TypeScript enums to `as const` objects with companion union types. This avoids isolatedModules footguns for consumers and allows plain string/number literals to be used without importing the object.
- Remove BoardId from public exports (internal-only, consumers use BoardVersion) - Make BoardId.id readonly - Make radio bridge logging option optional (consistent with USB/Bluetooth) - Remove stale comment about extracting a library
- Remove BoardId usage (no longer exported) - Fix status value casing to match as-const values (e.g. "Connected" not "CONNECTED") - Fix connect() examples that incorrectly showed a return value (it returns void)
The same program continues running on the micro:bit during a pause, so clearing the terminal is unnecessary and frustrating for users. Also documents the serial event map.
Partial flashing skips addresses >= 0x10000000, so UICR is never written. After an interrupted full flash (which chip-erases UICR), the fallback partial flash leaves UICR blank, bricking the device. Now ensureUicr() runs after every partial flash to compare device UICR against the hex file. Write-without-erase when only 1→0 bit changes are needed (both V1/V2). On V2, use NVMC.ERASEUICR when 0→1 changes are required. On V1 (no independent UICR erase), fall back to full flash. Also refactors hex parsing: toMemoryMap() + extractFlashAndUicr() replace the old convertDataToPaddedBytes chain, parsing the input once to produce both flash bytes and UICR entries. Fixes #112
Running on Mac, testing on Windows via `npm run dev -- --host` and click through warnings. We need the secure origin for Web Bluetooth/USB.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This contains breaking changes (e.g. flash progress interface, connect
signature, device error codes) and will form part of a v1 at some point.
Migrating USB-only code should be trivial though.
Changes:
Switch to capacitor-ble for bluetooth
point of use and the interactions are internally queued.
Support DFU and partial flashing on iOS/Android platforms
Drop a bunch of workarounds that need reevaluating after the switch
Don't try to start notifications on absent services. Prevents issues when not
in application mode for a flash.
Improve connect interface (which had a misleading return value) and
connect/flash progress.
Design issues:
the ideas different enough that we split them? It's nice in that it matches
USB but it's also quite different because of pairing.
TODO:
Fixes #20
Fixes #57
Fixes #71