It might also be useful for readers...
UART over USB is not quite robust in a way, that on the transport level the 100% delivery of all bytes is not guaranteed. I came up with a simple frame protocol to transfer with 3kHz sample rate:
int requestedSamples = -1;
void setup() {
Serial.begin(115200);
while (!Serial) {}
}
void loop() {
if (requestedSamples == 0) {
requestedSamples = -1;
Serial.write((uint8_t)(0x0)); // Start
return;
}
if (requestedSamples == -1 && Serial.available() >= 2) {
uint8_t c0 = Serial.read(), c1 = Serial.read();
requestedSamples = (int)c0 + (int)(c1 << 8);
while (Serial.available() > 0) (void)Serial.read(); // clean up garbage
Serial.write((uint8_t)(0xC0)); // End
}
if (requestedSamples == -1) return;
uint16_t v = analogRead(A0);
Serial.write((uint8_t)(((v >> 0) & 0xF) | 0x40));
Serial.write((uint8_t)(((v >> 4) & 0xF) | 0x40));
Serial.write((uint8_t)(((v >> 8) & 0xF) | 0x40));
Serial.write((uint8_t)(((v >> 12) & 0xF) | 0x40));
requestedSamples--;
}
And then for WL side read the data in N-samples per request:
readDevice[dev_, packetSize_:64] := Module[{},
If[!(DeviceOpenQ[dev]//TrueQ), Return[$Failed, Module]];
DeviceReadBuffer[dev];
DeviceWrite[dev, ExportByteArray[packetSize, "UnsignedInteger16"]//Normal];
With[{result = TimeConstrained[DeviceReadBuffer[dev, packetSize 4 + 2], 0.5, $Failed]},
If[FailureQ[result], Return[$Failed, Module]];
If[result[[1]] != 192, Return[$Failed, Module]];
If[result[[-1]] != 0, Return[$Failed, Module]];
With[{payload = result[[2;;-2]]},
If[Sum[BitGet[b, 6], {b, payload}] != packetSize 4, Return[$Failed, Module]];
Map[Function[p,
BitClear[p[[1]], 6] + BitShiftLeft[BitClear[p[[2]], 6], 4] + BitShiftLeft[BitClear[p[[3]],6], 8] + BitShiftLeft[BitClear[p[[4]],6], 12]
], Partition[payload, 4]]
]
]
]
Some more details are covered in a blog post there: https://wljs.io/blog/2025/08/25/analogRead