diff --git a/components/bldc_haptics/example/main/bldc_haptics_example.cpp b/components/bldc_haptics/example/main/bldc_haptics_example.cpp
index 6576fa19c..24cdc79f4 100644
--- a/components/bldc_haptics/example/main/bldc_haptics_example.cpp
+++ b/components/bldc_haptics/example/main/bldc_haptics_example.cpp
@@ -794,6 +794,10 @@ extern "C" void app_main(void) {
} else if (start - telemetry_stall_start > kTelemetryStallTimeout) {
streaming = false; // host abandoned the stream: stop flooding a full FIFO
telemetry_stall_start = {};
+ // Drop the queued backlog now (a tab close does not unmount, so it
+ // is not cleared for us) so a reconnecting host reads a clean stream
+ // instead of stale telemetry it might mis-parse as a command reply.
+ usb.vendor_write_clear();
logger.warn("Telemetry auto-paused: the host stopped draining the USB vendor "
"endpoint (re-enable streaming from the web console)");
}
diff --git a/components/bldc_haptics/example/webapp/index.html b/components/bldc_haptics/example/webapp/index.html
index 048761e33..fcdeb533f 100644
--- a/components/bldc_haptics/example/webapp/index.html
+++ b/components/bldc_haptics/example/webapp/index.html
@@ -480,6 +480,9 @@
Log
let device = null, ifaceNumber = null, epIn = null, epOut = null;
let manualDisconnect = false;
const parser = new StreamParser();
+ // True during the brief pre-handshake RX drain (see initializeDevice): stale
+ // frames left in the device's TX FIFO by a previous session are discarded.
+ let draining = false;
// Single in-flight transaction: resolved by the RX pump when a frame of an
// accepted reply type (or ERROR) arrives.
@@ -643,6 +646,9 @@ Log
}
function dispatchFrame(frame) {
+ // Discard everything during the brief pre-handshake RX drain: these are
+ // stale frames from a previous session, not replies to anything we sent.
+ if (draining) return;
// Discovery reply (reserved module 0xFF): hand the TLV payload to a pending
// module probe. Not this device's haptics module, so handle it before the
// module filter below drops it.
@@ -793,6 +799,15 @@ Log
async function initializeDevice() {
try {
+ // Flush any stale frames the device queued for a previous session (its TX
+ // FIFO is not cleared on an abrupt tab close) before we send anything, so
+ // an old telemetry / reply frame can't be mis-read as the response to our
+ // first command. RX keeps running; dispatchFrame drops frames while
+ // draining is set.
+ draining = true;
+ parser.reset();
+ await new Promise((resolve) => setTimeout(resolve, 150));
+ draining = false;
// Confirm this device serves the haptics module (best-effort warning only;
// we still attempt init so the user can force it if discovery is stale).
await checkModulePresent();