Status: DRAFT · Last updated: 2026-07-29
The PyBLE app is a Flutter, tablet-first IDE for iPad and Android. It connects to a compatible MicroPython board over BLE, speaks PBLE/1, and presents an editor, console, file explorer, block editor, and plots. Discovery and feature availability are service- and capability-driven, never gated by a silicon vendor or a fixed chip allowlist. ESP32, ESP32-S3, and ESP32-C3 are the initial firmware targets.
Detailed app docs: this page is the overview. The full requirements live in
App/specs.mdand the technical design inApp/TDD.md(Technical Design Document), both derived from PRD §9.
UI widgets (editor · console · files · blocks · plots · connect)
│ (callbacks only — no transport imports)
▼
PBLE/1 client lib/pble/ ── Connection API
│
▼
BLE adapter lib/ble/ ── byte stream in/out
│
▼
flutter_blue_plus
Strict rule: UI widgets never import lib/ble/, and only the lib/pble/ client knows the wire format. This keeps the editor/console/files transport-agnostic and testable against a fake Connection.
| Path | Responsibility |
|---|---|
lib/ble/ |
flutter_blue_plus wrapper: scan filtered to the PyBLE service UUID, connect, MTU 247, Stream<List<int>> in / write(bytes) out, connection-state + reconnect. |
lib/pble/ |
PBLE/1 client: frame codec + CRC, fragmentation/reassembly, request/response correlation, file-transfer state machine (window + resume), console stream, error→exception mapping. Exposes Connection. |
lib/editor/ |
Code editor (flutter_code_editor), Run/Save actions, file tabs. |
lib/console/ |
Live console view + stdin input; traceback rendering. |
lib/files/ |
Board file explorer: list/open/upload/download/rename/delete/mkdir, multi-select. |
lib/blocks/ |
Blockly (WebView) → inspectable MicroPython generation (including the current numeric-machine.Pin, time.sleep_ms, and standard neopixel.NeoPixel subset, initially validated on ESP32-family firmware) → seven offline editable beginner examples with explicit GPIO choice → shared Run/Save path. |
lib/plots/ |
fl_chart views over CSV/streamed values. |
lib/connect/ |
Scan/connect UI, saved boards. |
lib/github_import/ |
Pull a folder of .py from a public GitHub repo → Connection.putFile. |
lib/localization/ |
intl/ARB strings (en first; parity enforced for added languages). |
lib/data/ |
Local persistence: projects, settings, saved boards (offline-first). |
abstract interface class Connection {
ValueListenable<ConnState> get state; // disconnected/connecting/ready/running
Future<DeviceInfo> deviceInfo();
// run / console
Future<void> runFile(String path);
Future<void> runSource(String source);
Future<void> stop();
Future<void> softReboot();
Stream<ConsoleEvent> get console; // {stream: stdout|stderr|system, bytes}
Future<void> sendInput(String text);
// files
Future<List<RemoteEntry>> listDir(String path);
Future<Uint8List> getFile(String path, {ProgressCb? onProgress});
Future<void> putFile(String path, Uint8List bytes, {ProgressCb? onProgress});
Future<void> delete(String path);
Future<void> mkdir(String path);
Future<void> rename(String from, String to);
}Widgets receive a Connection (or narrow callbacks derived from it). Tests inject a FakeConnection. There is no QR pairing, no lease/heartbeat, and no board-specific gating — just scan, connect, use.
- Scan —
flutter_blue_plusscan filtered to the PyBLE service UUID; show advertisedPyBLE-XXXXnames + RSSI. - Connect — open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, show
DeviceInfo. - Use — editor/console/files bind to the
Connection. - Reconnect — on link loss, auto-reattempt; in-flight file transfers resume via PBLE/1 §5. Saved boards reconnect by remembered identifier.
The DeviceInfo.chip value is a port-defined technical identifier. The app
renders unknown future values verbatim and continues from advertised PBLE/1
capabilities; a missing in-app pin reference is informational and MUST NOT
block the connection.
- iPadOS + Android at parity. BLE permissions: iOS
NSBluetoothAlwaysUsageDescription; AndroidBLUETOOTH_SCAN/BLUETOOTH_CONNECT(Android 12+) and location handling on older versions. - No USB serial, no Wi-Fi onboarding — BLE only (this is what makes iPad first-class).
- Tablet-first responsive layout (split editor/console/files); must not break on a phone.
Several board-agnostic widgets (editor, console, file explorer, plots, Blockly bridge, GitHub import, tablet scaffold, localization) are re-implemented or relicensed-MIT by the author from their own prior art, retyped onto PyBLE's neutral types (Connection, ConsoleEvent, RemoteEntry). They carry no closed-source protocol client, board profiles, UUIDs, catalog, curriculum, or pedagogy — the PBLE/1 client, BLE adapter, and seven small generic starter workspaces are written fresh for PyBLE. See the clean-room boundary, ADR-0002, and ADR-0016.
lib/pble/— conformance tests against PBLE/1 (frame round-trips, CRC, fragmentation, file-transfer window/resume, error mapping) using an in-memory fake transport.lib/ble/— mocked-transport tests for scan filter, connect, reconnect.- UI — widget + golden tests against a
FakeConnection; locale parity check for ARB; Blocks tests restore/generate every catalog workspace, require explicit GPIO roles, and prove Preview/cancel/failure never mutate work or perform an implicit board action. - integration — end-to-end against a fake board, plus on-device smoke tests for every target claimed by a release (initially ESP32, ESP32-S3, and ESP32-C3); an example copy must survive WebView recreation/rotation as ordinary workspace JSON.