When you’re wiring together fast embedded systems (like an ESP32 feeding data into a Teensy 4.1) it’s easy to assume USB will “just work.” After all, USB is fast. Way faster than UART, right?
That assumption is exactly what led to a subtle (and very common) bug: perfectly timed data turning into unreadable garbage.
Let’s walk through what happened, why it happens, and the one-line fix that makes everything click.
The goal was straightforward:
The ESP32 test code was a simple counter printed once per second at 460800 baud.
On the PC? Everything looked perfect.
On the Teensy? Total nonsense:
�U��M�E��e��E��E��%�A��������...
Yet the Teensy was receiving data at the correct timing intervals. That’s the key clue.
At first glance, the reasoning makes sense:
But that’s not how USB serial adapters work.
The ESP32 dev board doesn’t expose raw UART over USB. Instead, it uses a USB-to-serial bridge chip (like the CP2102).
That chip sits between two worlds:
That baud rate must be set by the USB host.
When you use the Arduino Serial Monitor, selecting “460800 baud” isn’t just cosmetic—it sends a USB control message telling the CP2102:
“Talk to the ESP32 at 460800 baud.”
So when you unplug from your PC and plug into the Teensy, the Teensy becomes the host that has to configure that chip.
From the original code:
USBSerial userial(myusb);
void setup() {
Serial.begin(460800); // Debug output
myusb.begin();
}
The mistake is subtle:
Serial.begin(460800) sets the Teensy’s USB device port (to your PC)userial (the USB host connection)So the CP2102 defaults to 115200 baud, while the ESP32 is sending at 460800 baud.
That mismatch = corrupted data.
Add this:
userial.begin(460800);
That’s it.
Now both sides agree on the UART speed, and the garbage disappears.
This issue trips people up because there are three layers involved:
The important takeaway:
USB speed and UART baud rate are completely independent.
Even though data travels over USB at megabits per second, the bridge chip still needs to know how fast to talk to the microcontroller.
Think of the CP2102 like a translator:
Setting the baud rate is like telling the translator how fast to speak on the UART side.
If you don’t tell it? It guesses (usually 115200). And that guess is often wrong.
This distinction also explains something subtle:
SerialThat’s why your Teensy debug output worked fine regardless of settings while the ESP32 link didn’t.
After fixing the baud rate on the host side, the system behaved exactly as expected:
A: 302 D: 9365
A: 312 D: 2652
A: 312 D: 2844
...
Clean, structured data from the LiDAR without any corruption.
Serial.begin() and userial.begin() control completely different interfacesThis is one of those bugs that feels like signal integrity or timing trouble—but turns out to be configuration.
Once you understand where the baud rate actually lives (hint: not just on the transmitting device), a whole class of “mystery corruption” problems becomes easy to diagnose.