2026-09-27

When a motor logger outruns its reader

Why a 64-record Web Serial queue lost motor measurements, and how bounded transition capture changes the failure mode.

A Web Serial workbench completed a motor experiment but lost 7,046 measurements. The failure teaches two different lessons: average serial bandwidth must be sufficient, and a live queue still needs enough room for the reader’s worst pause. We changed the experiment to capture one bounded transition on the board and transfer it afterward.

Open the interactive animation in a full page. Select normal reads, pause reader 680 ms, and frozen segment to compare the three cases. The 680 ms pause is an illustration, not a measured browser stall in our test.

Part I — Why the live logger lost data

What the experiment needed

The workbench was meant to identify rotational inertia and friction from speed transitions. Its original rotating run covered six forward and reverse speed conditions, with three repetitions each. The board logged speed, current, position, status, and timestamps. A browser collected records over Web Serial and plotted the waveform. Static friction was a separate, stopped-rotor experiment.

Before fitting any parameter, the immediate requirement was simpler: complete a run and retain every measurement needed to inspect it. The motor motion finished, but the exported file held 87,575 records and reported 7,046 dropped records. There were 43 gaps in the saved sequence numbers, accounting for exactly that loss. The gaps began about 335 seconds into the saved interval and all appeared in the final −900 rpm condition. The largest gap between adjacent saved records was 0.68 seconds. The file establishes missing data; it does not reveal what delayed the reader.

A queue is a waiting room, not an archive

The control loop ran at 5 kHz and published one 56-byte logging record every 20 cycles. That is 250 records per second, or one every 4 ms. The board’s live queue had 64 slots. With no reading and an initially empty queue, it filled in:

1
64 slots × 4 ms per record = 256 ms

If 20 records were already waiting, only 44 slots remained: 176 ms until full at the same production rate. Once full, later logging records were discarded. The control loop could continue normally while the measurement history became incomplete.

This is the answer to “is the buffer too small for an interruption?”: yes, for any reader interruption longer than its remaining free-slot time. Enlarging the queue buys time, but it cannot guarantee a multi-minute live stream if the browser or USB path has no bound on pauses.

Version one: the wire could never keep up

The earliest framing returned one mechanical record bundled with a full status message. Measured replies were roughly 2,023–2,032 bytes. Sending the smallest measured reply for each 250 Hz record would require:

1
2,023 bytes × 250/s = 505,750 bytes/s

The serial link was configured for 2 Mbaud, 8N1. Ten transmitted bits per byte give a raw ceiling of 200,000 bytes/s in one direction. The record replies alone demanded over 2.5 times that ceiling. No polling adjustment could make this framing lossless. Its payload had to shrink and be batched.

Version two: adequate average rate, fragile timing

The revised protocol packed up to 24 records into one CRC-framed JSON reply. Each binary record was 56 bytes, represented by 112 hexadecimal characters; a representative full reply was 2,867 bytes with envelope and framing. The browser aimed to request one batch every 80 ms. The board made about 20 records in that interval, while a reply could remove 24: a nominal 300-record/s drain capacity against 250-record/s production. A full reply occupied about 14.3 ms of 2 Mbaud wire time. Including representative plot and status traffic, estimated board-to-host usage was roughly 97 kB/s, about 49% of the raw ceiling.

Batching repaired the average wire-budget error. It did not make the browser a real-time recorder. The 64-slot queue still allowed at most 256 ms without a read from empty, and less when partly occupied. A missed timer callback, delayed response, browser scheduling pause, or other interruption could still overflow it.

We also found and fixed two distinct software faults along the way. A forward-only parser had rejected valid negative speed targets, leaving reverse-run status stale and preventing draining. Separately, a reply crossing an 80 ms timer boundary could skip the next poll; the reader was changed to continue after a full batch. Those fixes removed known causes of pauses. They did not impose an upper bound on every future pause.

The final export had a long continuous prefix before late gaps. That argues against saying the batched protocol was chronically too slow for the entire run. The gaps indicate that the queue was not drained in time on some occasions, but the log does not distinguish browser scheduling, USB transport, serial service, or another delay source. As a scale example, 680 ms with no reads produces about 170 records; an empty 64-slot queue could keep only 64. The animation chooses such a pause to expose the mechanism. Its loss count is illustrative and is not the run’s 7,046-record loss.

Part II — Capture first, transfer later

Make one transition the recording unit

The new mode asks the operator to select one of six signed base speeds and one of four edges: accelerate from base speed, return from the higher speed, decelerate from base speed, or return from the lower speed. After reaching and stabilizing at the starting speed, the board performs only that edge. An acceleration or deceleration is observed for 8 seconds; a return is observed for 2 seconds. Output then turns off. The board does not automatically begin the next condition.

One 8-second transition at the old 250 Hz logging rate would need about 2,000 records, or 112,000 bytes for the record bodies alone. Instead, this mode reserves 128 records (7,168 bytes). It retains two pre-edge samples, one sample at the command edge, and regular samples every 64 ms. The 8-second transition fits exactly in 128 slots; a 2-second return uses 35. The regular cadence is 15.625 Hz. Real timestamps remain in each record, so the special edge samples are not pretended to be evenly spaced.

That lower rate is a deliberate tradeoff for the mechanical speed response. It does not preserve the original 250 Hz current-transient detail, and its adequacy for fitting J, B, and Tc must be assessed from real experiments.

Read the frozen record after output is off

During the selected transition, the board writes into space reserved for the entire segment. The browser may display a status preview, but it does not have to drain the logging queue during capture. After output turns off, it requests the frozen records in batches of up to 24:

1
MECH_TRACE <generation> <offset>

The response identifies the generation, offset, total count, interval, and record contents. An indexed read does not remove stored data. If a reply is lost, the browser can ask for the same offset again. The workbench keeps the next start disabled until readback finishes and offers each completed segment for viewing and JSON export.

Now a browser pause during capture does not cause 250 Hz records to pile up behind a 64-slot live queue. A pause during readback delays completion but does not consume the frozen segment. The board preserves only its current segment: an explicit new start or a reset replaces it. Refreshing the browser also loses its accumulated list unless the data were exported.

The method removes the browser’s 256 ms continuous-read deadline for this rotating-transition mode; it does not claim that every motor parameter is already identified. Offline tests covered all six bases and four edge choices, an entire capture with no reads, repeated reads at one offset, interrupted readback, and export of all 128 records. The firmware compiled and the workbench loaded. The mode has not yet been validated on the motor. The older continuous rotating and static-friction modes still use their earlier streaming behavior; automatic parameter fitting and project-file saving remain separate work.

The general design rule is to size a live queue for a bounded worst-case reader delay, not just average throughput. When that delay cannot be bounded, make the measurement finite and self-contained at the producer, then transfer it with repeatable indexed reads. Data completeness and parameter-fit quality must be checked separately.