Add chunked PhoneAPI transport payloads - #10928
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR updates the protobufs submodule and adds chunked transport for oversized ChangesChunked Payload Support
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant PhoneAPI
participant ChunkedClient
Client->>PhoneAPI: send chunked ToRadio payload
PhoneAPI->>ChunkedClient: allocate or locate reassembly slot
PhoneAPI->>ChunkedClient: append chunk bytes
alt payload complete
PhoneAPI->>PhoneAPI: forward reassembled bytes to handleToRadio
else invalid or overflow
PhoneAPI->>ChunkedClient: clear slot
end
Client->>PhoneAPI: call getFromRadio
PhoneAPI->>ChunkedClient: check pending outbound fragment
alt fragment pending
PhoneAPI-->>Client: return fragment
else no fragment pending
PhoneAPI->>PhoneAPI: encode FromRadio payload
PhoneAPI-->>Client: return original or chunked payload
end
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
@kernel-oops, Welcome to Meshtastic!Thanks for opening your first pull request. We really appreciate it. We discuss work as a team in discord, please join us in the #firmware channel. Welcome to the team 😄 |
…-phoneapi # Conflicts: # protobufs
|
Local validation after rebasing onto current
The protobuf PR is now ready for review as well: meshtastic/protobufs#980. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/mesh/PhoneAPI.cpp (1)
179-186: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
Throttlefor the stale-slot timeout instead of rawmillis()arithmetic.This stale-transfer expiry is a "did N ms pass since X" check and should go through the
Throttlehelper.♻️ Proposed change
-static ChunkedToRadioSlot *findChunkedToRadioSlot_LH(PhoneAPI *api) -{ - const uint32_t now = millis(); - for (auto &slot : g_chunkedToRadioSlots) { - if (slot.who != nullptr && now - slot.updatedMillis > CHUNKED_TORADIO_TIMEOUT_MS) { - clearChunkedToRadioSlot_LH(slot); - } - } +static ChunkedToRadioSlot *findChunkedToRadioSlot_LH(PhoneAPI *api) +{ + for (auto &slot : g_chunkedToRadioSlots) { + if (slot.who != nullptr && !Throttle::isWithinTimespanMs(slot.updatedMillis, CHUNKED_TORADIO_TIMEOUT_MS)) { + clearChunkedToRadioSlot_LH(slot); + } + }As per coding guidelines: "Use
Throttlefor time-based rate limiting instead of rawmillis()arithmetic; preferThrottle::isWithinTimespanMs(lastMs, intervalMs)... for 'did N ms pass since X' checks."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/mesh/PhoneAPI.cpp` around lines 179 - 186, The stale-slot expiry check in findChunkedToRadioSlot_LH is using raw millis() subtraction to decide whether CHUNKED_TORADIO_TIMEOUT_MS has elapsed. Replace that logic with the Throttle helper, using the existing slot timestamp field and Throttle::isWithinTimespanMs(...) for the “has N ms passed since X” check before calling clearChunkedToRadioSlot_LH. Keep the behavior the same, but route the timeout decision through Throttle to match the time-based rate-limiting guideline.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/mesh/PhoneAPI.cpp`:
- Around line 179-186: The stale-slot expiry check in findChunkedToRadioSlot_LH
is using raw millis() subtraction to decide whether CHUNKED_TORADIO_TIMEOUT_MS
has elapsed. Replace that logic with the Throttle helper, using the existing
slot timestamp field and Throttle::isWithinTimespanMs(...) for the “has N ms
passed since X” check before calling clearChunkedToRadioSlot_LH. Keep the
behavior the same, but route the timeout decision through Throttle to match the
time-based rate-limiting guideline.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8ccf108a-0720-4d93-a686-99de0a8e10ee
⛔ Files ignored due to path filters (1)
src/mesh/generated/meshtastic/mesh.pb.his excluded by!**/generated/**,!src/mesh/generated/**
📒 Files selected for processing (2)
protobufssrc/mesh/PhoneAPI.cpp
…-phoneapi # Conflicts: # protobufs
|
Flash footprint update after optimizing only the chunked PhoneAPI implementation:
The optimization saves 1,232 bytes of flash compared with the initial integrated implementation. The final feature cost relative to the same The board definition's nominal flash limit is 815,104 bytes, which would suggest 13,520 bytes remain. The meaningful constraint on this target is tighter, however: the 12 KiB WarmNodeStore reservation starts at The savings came from combining inbound/outbound per-client state and locks, removing redundant state and buffer clearing, removing chunk-only log strings, and replacing nanopb encoding of the small outbound wrapper with a fixed canonical wire encoder. Boundary tests at 21, 127, 128, 511, and 512-byte payload totals matched nanopb byte-for-byte and reconstructed the original payload exactly. Protocol behavior is unchanged: two clients remain supported, inbound timeout and bounds checks remain, outbound chunking is still opt-in per client, and legacy clients still receive unchunked messages. No unrelated Pro Micro functionality or target configuration was removed. |
…-phoneapi # Conflicts: # protobufs
Summary
This adds a compact chunked PhoneAPI transport so clients with a 20-byte GATT payload limit can exchange complete Meshtastic client API protobufs without ATT long reads or writes.
The immediate use case is Garmin Connect IQ BLE. Garmin limits application writes to 20 bytes and does not provide ATT long reads, while normal
ToRadioandFromRadiomessages can be substantially larger.Depends on the corresponding protobuf schema PR: meshtastic/protobufs#980
What changed
ClientApiChunkedPayloadtransport wrapper withuint32 payload_size = 1andbytes payload_chunk = 2.ToRadio.chunked_payload = 8andFromRadio.chunked_payload = 20.ChunkedPayloadandChunkedPayloadResponsemessages unchanged for compatibility with their current users.ToRadiomessages before passing the original serialized message to the existingPhoneAPIhandler.FromRadiomessages only after that client has successfully completed a chunkedToRadiotransfer. Legacy clients retain the existing unchunked behavior.PhoneAPIclients in one lock-protected static table.mesh.pb.cppandmesh.pb.hsources for the schema change.Wire format
The transport assumes ordered, reliable delivery on one client connection. This matches Garmin's serialized BLE writes-with-response and reads, and avoids spending bytes on transfer IDs and chunk indices in every packet.
Only the first chunk carries the total serialized payload size. Every chunk carries the next payload bytes.
For a 20-byte GATT payload:
ToRadiofirst chunk: up to 13 payload bytes.ToRadiocontinuation: up to 16 payload bytes.FromRadiofirst chunk: up to 12 payload bytes.FromRadiocontinuation: up to 15 payload bytes.The
FromRadioencoder writes the fixed canonical protobuf wire representation directly. Boundary tests compare it byte-for-byte with nanopb output.Safety and compatibility
ChunkedPayloadwire schema remains unchanged.FromRadiomessages.MAX_TO_FROM_RADIO_SIZEor is smaller than the bytes already supplied.handleToRadio()only after releasing the mutex.FromRadiomessage is generated.This is intentionally not a retransmission protocol. It relies on the ordered, reliable behavior of the underlying client connection.
Flash footprint
Measured on
nrf52_promicro_diy_tcxoagainst the samedevelopbase:developThe final patch adds 768 bytes of flash and 2,096 bytes of RAM relative to
develop. Consolidating the client tables and locks, removing redundant state and chunk-only log strings, avoiding unnecessary buffer clearing, and directly encoding the small outbound wrapper recovered 1,232 bytes of flash from the initial integrated implementation.The target's nominal flash limit is 815,104 bytes, but its reserved WarmNodeStore region starts earlier at
0xEA000. The final image ends at0xE9B30, 1,232 bytes below that effective boundary, and passes the warm-region guard.No unrelated Pro Micro functionality or target configuration was removed.
Validation
nrf52_promicro_diy_tcxo; warm-region, ISR, and LTO guards passed.heltec-v4from the PR branch: 2,314,411 bytes flash and 103,760 bytes RAM.ChunkedPayloadserialization vector remains080110021803220104.master, built and flashed a Heltec V4 app image, and completed a live BLE round trip with a Garmin D2 Mach 2.FromRadiomessages including 116-byte node info and a 171-byte MQTT proxy message, plus a chunked 34-byteToRadiomessage; all BLE writes completed successfully and config transfer completed without errors.git diff --check.The remaining hardware test gap is simultaneous use by two physical PhoneAPI clients; the bounded two-client paths have been reviewed and tested at the serialization/state-machine level.
Summary by CodeRabbit
New Features
ToRadio/FromRadioprotobuf payloads, including outbound chunk sequencing and inbound reassembly.Bug Fixes
Chores