somewhat working file sync

This commit is contained in:
2025-10-19 23:07:20 +02:00
parent b4f11851d7
commit 3ab2a7bf26
12 changed files with 5934 additions and 67 deletions

View File

@@ -25,5 +25,65 @@ This SwiftUI app connects to the Cardboy device over Bluetooth Low Energy and up
2. Ensure the `CoreBluetooth` capability is enabled for the `cardboy-companion` target and keep the *Uses Bluetooth LE accessories* background mode on (preconfigured in this project).
3. Build & run on a real device (BLE is not available in the simulator).
4. Allow Bluetooth permissions when prompted. The app keeps scanning in the background, so the Cardboy can request a sync even while the companion is not foregrounded. Tap **Sync Now** any time you want to trigger a manual refresh.
5. Switch to the **Files** tab to browse the LittleFS volume on the Cardboy: you can upload ROMs from the Files picker, create/remove folders, rename entries, delete files, and download items back to the phone for sharing.
## BLE File Service Protocol
The ESP firmware exposes a custom GATT service (UUID `00000010-CA7B-4EFD-B5A6-10C3F4D3F230`) with two characteristics:
| Characteristic | UUID | Properties | Direction | Description |
| --- | --- | --- | --- | --- |
| File Command | `00000011-CA7B-4EFD-B5A6-10C3F4D3F231` | Write / Write Without Response | iOS → ESP | Sends file management requests |
| File Response | `00000012-CA7B-4EFD-B5A6-10C3F4D3F232` | Notify | ESP → iOS | Streams command results (responses or data) |
All payloads share the same framing. Commands written to the File Command characteristic use:
```
Offset | Size | Description
-------+------+------------
0 | 1 | Opcode (see table below)
1 | 1 | Reserved (set to 0)
2 | 2 | Little-endian payload length in bytes (N)
4 | N | Command payload
```
Notifications from the File Response characteristic use:
```
Offset | Size | Description
-------+------+------------
0 | 1 | Opcode (echoed from command)
1 | 1 | Status byte (bit 7 = completion flag; lower 7 bits = error code)
2 | 2 | Little-endian payload length (N)
4 | N | Response payload (command-specific)
```
Status byte semantics:
- Bit 7 (0x80) set → final packet for the current command (no further fragments).
- Lower 7 bits = error code (`0` = success, otherwise `errno`-style code echoed back).
- On error the response payload may contain a UTF-8 message.
### Opcodes and Payloads
| Opcode | Name | Command Payload | Response Payload |
| --- | --- | --- | --- |
| `0x01` | List Directory | `uint16 path_len` + UTF-8 path | One or more fragments, each entry encoded as:<br> - `uint8 type` (0=file, 1=dir)<br> - `uint8 reserved`<br> - `uint16 name_len`<br> - `uint32 size` (0 for dirs)<br> - `name_len` bytes UTF-8 name<br> Final notification has completion bit set. |
| `0x02` | Upload Begin | `uint16 path_len` + UTF-8 path + `uint32 file_size` | Empty payload on success. Starts upload session (expects `UploadChunk` packets). |
| `0x03` | Upload Chunk | Raw file bytes | Empty payload ack for each chunk. |
| `0x04` | Upload End | No payload | Empty payload confirming completion. |
| `0x05` | Download Request | `uint16 path_len` + UTF-8 path | First notification: 4-byte little-endian total file size; subsequent notifications stream raw file data fragments. Completion bit marks the final chunk. |
| `0x06` | Delete File | `uint16 path_len` + UTF-8 path | Empty payload on success. |
| `0x07` | Create Directory | `uint16 path_len` + UTF-8 path | Empty payload on success. |
| `0x08` | Delete Directory | `uint16 path_len` + UTF-8 path | Empty payload on success. |
| `0x09` | Rename Path | `uint16 src_len` + UTF-8 source path + `uint16 dst_len` + UTF-8 destination path | Empty payload on success. |
### Notes
- Paths are absolute within the LittleFS volume; the firmware normalizes them and rejects entries containing `..`.
- Large responses (directory lists, downloads) may arrive in multiple notifications; the iOS client aggregates fragments until it sees the completion flag.
- Uploads are initiated with `Upload Begin` (including total size), followed by one or more `Upload Chunk` writes, and `Upload End` when done.
- Errors from the firmware propagate via the status byte; when `status & 0x7F != 0`, the notification payload typically includes a UTF-8 error message (e.g., `"stat failed"`).
This protocol mirrors the implementation in `components/backend-esp/src/time_sync_service.cpp` and the Swift client in `TimeSyncManager.swift`. Update both sides if new commands are added.
Optionally bundle this code into your existing app—`TimeSyncManager` is selfcontained and can be reused.