Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions components/bldc_haptics/example/main/bldc_haptics_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,9 +360,12 @@ extern "C" void app_main(void) {
// Takes a span so callers can pass either an owning std::vector (built by
// proto::build / ota_stream make_*, converted implicitly, no copy) or a
// borrowed buffer (the discovery / coredump reply spans) without allocating.
auto usb_send = [&](std::span<const uint8_t> frame) {
// Returns true if the frame was queued, false if it was dropped (FIFO full /
// host not draining). Callers that don't care (command replies) ignore it;
// the telemetry task uses it to auto-pause a stream the host has abandoned.
auto usb_send = [&](std::span<const uint8_t> frame) -> bool {
if (frame.empty())
return;
return true;
std::lock_guard<std::mutex> lk(usb_tx_mutex);
std::error_code tx_ec;
if (!usb.write_vendor(frame, tx_ec)) {
Expand All @@ -372,7 +375,9 @@ extern "C" void app_main(void) {
// disconnected host streaming telemetry cannot flood the console.
logger.warn_rate_limited("USB vendor TX failed, dropped {}-byte frame: {}", frame.size(),
tx_ec.message());
return false;
}
return true;
};

// RX bytes arrive in the TinyUSB task context: queue them and dispatch from
Expand Down Expand Up @@ -760,6 +765,14 @@ extern "C" void app_main(void) {
// --------------------------------------------------------------------------
// Telemetry streaming task
// --------------------------------------------------------------------------
// Auto-pause guard: if telemetry sends fail continuously for this long, the
// host has stopped draining the vendor IN endpoint (tab closed / backgrounded
// / frozen). Streaming into a full FIFO just spams drops and, worse, buries a
// reconnecting host's GET_INFO reply so it can never connect -- so pause the
// stream until a host re-enables it (SET_STREAMING on connect).
constexpr auto kTelemetryStallTimeout = 2s;
auto telemetry_stall_start = std::chrono::steady_clock::time_point{};

Comment on lines +774 to +775
espp::Task telemetry_task(
{.callback = [&](std::mutex &m, std::condition_variable &cv) -> bool {
const auto start = std::chrono::steady_clock::now();
Expand All @@ -774,7 +787,16 @@ extern "C" void app_main(void) {
proto::put_f32(payload, continuous_value());
proto::put_f32(payload, motor->get_shaft_angle());
proto::put_f32(payload, motor->get_shaft_velocity());
usb_send(proto::build(proto::Msg::Telemetry, payload));
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
Comment on lines +792 to +793
} 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 +790 to +799
}
{
std::unique_lock<std::mutex> lk(m);
Expand Down
16 changes: 16 additions & 0 deletions components/bldc_haptics/example/webapp/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,22 @@ <h2>Log</h2>
els.streamToggle.addEventListener("change", applyStreaming);
els.rateSelect.addEventListener("change", () => { if (els.streamToggle.checked) applyStreaming(); });

// Pause telemetry while the tab is hidden. A backgrounded tab is throttled
// and stops draining the USB IN endpoint, so the device's TX FIFO fills and
// it drops frames (the firmware also auto-pauses after a couple seconds of
// this). Tell the device to stop now, and resume when the tab is shown again
// (the streamToggle keeps the user's intent). Best-effort: if the tab is
// frozen the send may not land, but the firmware auto-pause covers that.
document.addEventListener("visibilitychange", () => {
if (!device) return;
if (document.hidden) {
transact(TYPE.SET_STREAMING, streamingPayload(false, parseInt(els.rateSelect.value, 10)),
[TYPE.OK], CMD_TIMEOUT_MS).catch(() => {});
} else if (els.streamToggle.checked) {
applyStreaming();
}
});

window.addEventListener("resize", drawDial);
logLine("sys", "espp BLDC Haptics Console ready. Connect a device running the bldc_haptics USB example.");
</script>
Expand Down
Loading