fix(bldc_haptics): auto-pause telemetry when the USB host stops draining - #773
Merged
Conversation
Symptom: after a while the CDC console spammed "Vendor TX FIFO full, dropping a 35-byte frame" (that 35-byte frame is haptics telemetry), and afterwards the web page could no longer connect. Cause: the telemetry task only gated on tud_mounted() (enumerated), never on whether the host was actually reading the vendor IN endpoint, and `streaming` stayed true from the previous session. When the browser tab is backgrounded / frozen / closed-without-disconnect it stops draining, the TX FIFO fills, and the device streams into it forever -- spamming drops and, crucially, dropping a reconnecting host's GET_INFO reply so initializeDevice() times out. Fix (device): usb_send() now returns whether the frame was queued; the telemetry task tracks continuous send failures and, after kTelemetryStallTimeout (2s), sets streaming=false and logs once. That stops the flood and lets the FIFO drain, so a new host connects cleanly and re-enables streaming (SET_STREAMING on connect). Fix (web): pause streaming on `visibilitychange` when the tab is hidden (a throttled tab can't drain the endpoint) and resume when shown again; the firmware auto-pause remains the safety net for a frozen/closed tab. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
This PR prevents the BLDC haptics example from flooding the USB vendor IN TX FIFO when the host stops reading, by auto-pausing telemetry on both the firmware and web console sides.
Changes:
- Web console pauses telemetry when the tab is hidden and resumes when visible again.
- Firmware
usb_send()now reports enqueue success/failure and telemetry auto-pauses after sustained send failures.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| components/bldc_haptics/example/webapp/index.html | Pause/resume streaming based on tab visibility to avoid host-side endpoint starvation. |
| components/bldc_haptics/example/main/bldc_haptics_example.cpp | Track vendor TX failures and auto-pause telemetry after a stall timeout to prevent FIFO saturation. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+790
to
+799
| if (usb_send(proto::build(proto::Msg::Telemetry, payload))) { | ||
| telemetry_stall_start = {}; // queued OK -> the host is draining | ||
| } else if (telemetry_stall_start == std::chrono::steady_clock::time_point{}) { | ||
| telemetry_stall_start = start; // first drop -> start the stall clock | ||
| } else if (start - telemetry_stall_start > kTelemetryStallTimeout) { | ||
| streaming = false; // host abandoned the stream: stop flooding a full FIFO | ||
| telemetry_stall_start = {}; | ||
| logger.warn("Telemetry auto-paused: the host stopped draining the USB vendor " | ||
| "endpoint (re-enable streaming from the web console)"); | ||
| } |
Comment on lines
+774
to
+775
| auto telemetry_stall_start = std::chrono::steady_clock::time_point{}; | ||
|
|
Comment on lines
+792
to
+793
| } else if (telemetry_stall_start == std::chrono::steady_clock::time_point{}) { | ||
| telemetry_stall_start = start; // first drop -> start the stall clock |
This was referenced Sep 5, 2026
finger563
added a commit
that referenced
this pull request
Sep 5, 2026
…on auto-pause (#775) * fix(bldc_haptics web): drain stale RX before the connect handshake Re-applies the RX-drain that missed the #773 squash-merge. On an abrupt tab close the device's vendor TX FIFO is not cleared, so a reconnecting page could read leftover telemetry / reply frames and mis-handle them ("first Connect returns immediately, second works"). initializeDevice() now flushes the pipe (set a `draining` flag, reset the parser, let the RX pump discard for ~150 ms) before the GET_INFO handshake; dispatchFrame drops frames while draining. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(bldc_haptics): clear the vendor TX backlog when telemetry auto-pauses When the telemetry stall guard fires (host stopped draining), also drop the queued-but-unsent telemetry via usb.vendor_write_clear() (added in #774). An abrupt tab close does not unmount the device, so the FIFO is not cleared for us; clearing it here means a reconnecting host reads a clean stream instead of a stale backlog it might mis-parse as the reply to its first command. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
✅Static analysis result - no issues found! ✅ |
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.
Symptom
After some time the haptics example spams the CDC console with:
(the 35-byte frame is a haptics telemetry frame), and once this starts, the web page can no longer connect to the device.
Cause
The telemetry task only gated on
usb.is_vendor_connected()— which is justtud_mounted()(enumerated) — and never on whether the host is actually reading the vendor IN endpoint.streamingalso staystruefrom the previous session. So when the browser tab is backgrounded / frozen / closed without a clean disconnect, it stops draining the endpoint, the TX FIFO fills, and the device streams into a full FIFO forever:GET_INFOreply is dropped because the FIFO is full of stale telemetry, soinitializeDevice()times out → "unable to connect."Fix
Device —
usb_send()now returns whether the frame was queued. The telemetry task tracks continuous send failures and, afterkTelemetryStallTimeout(2 s) of the host not draining, setsstreaming = falseand logs once. That stops the flood and lets the FIFO drain, so a new host connects cleanly and re-enables streaming (the web console sendsSET_STREAMINGon connect). Command/reply sends are unaffected (they ignore the return value).Web — pause streaming on
visibilitychangewhen the tab is hidden (a throttled/backgrounded tab can't drain the endpoint) and resume when it's shown again; thestreamTogglekeeps the user's intent. The firmware auto-pause remains the safety net for a frozen or closed tab where the JS can't run.Notes
UsbDevice"Vendor TX FIFO full" log inusb_device.cppcould be rate-limited so the ~2 s window before auto-pause is quieter — but that's a shared-component change affecting every user, so left out of this example-scoped fix.🤖 Generated with Claude Code