XRAD'S Teensy 4.1 USB host reading garbage from ESP32, need guidance to fix...

XRAD

Well-known member
Hello, Working on a project using RPLidar C1 tof scanner. BUT FIRST, I am just trying to read simple esp32 'counting' data output on myusb teensy 4.1. I am reading data via Teensy 4.1 myusb from a wroom esp32 dev module. I wired the USB wires to the teensy per teensy instructions. I have tried multiple methods, but I keep getting garbage. The serial output speed on serial'0' (the native USB hardware on ESP32) has to 'eventually' be 460800 for my hardware. Any thoughts?

NOTE: 'eventually' means that the C1 lidar unit runs at 460800 baud and I could not see any data in my Arduino IDE unless both baud rates matched. I have the esp32 C1 lidar working fine on its own and can see all measurements and angles in the arduino IDE.

I have checked the wiring multiple times. USB case ground, internal plug ground, D+(yellow), D- (green), 5v (red). This is my arduino IDE serial output:


�U��M�E��e��E��E��%�A��������%�A%��E��e����������������AE��������������
��M��-���
��
�����-��-��M�m��
�AM�A-��
��
�����-��-��M��������U��5��������5���5��U��u����]��}�����A��A]��=��]�A}���A������A�����������������U�����U��U��U��U����AU������u��u�����u��u��u���u��U�������������������A�A�������A���������������������A����������U�A����5��U�AU�A�������AU�



simple send counter esp32 via USB plug...

Code:
#include <Arduino.h>
int counter = 0;

void setup() {
  // Initialize the native USB Serial 
  Serial.begin(460800);

  //Wait 5 seconds for the Serial Monitor to open
  while (!Serial && millis() < 5000);

  Serial.println("Native USB Serial Initialized!");
}

void loop() {
  // Send data to the USB host
  Serial.print("Data packet: ");
  Serial.println(counter);
  counter++;
  delay(1000);
}



and the USB host code on the teensy 4.1:

Code:
#include "USBHost_t36.h"

// Initialize USB Host
USBHost myusb;

// USB Serial object for the connected device
USBSerial userial(myusb);

void setup() {
 // USB1_PORTSC1 |= USB_PORTSC1_PFSC; // force 12 Mbit/sec
 
  Serial.begin(460800); // Debug to arduino IDE
  delay(1000);
  // Start USB Host
  myusb.begin();
}

void loop() {
  myusb.Task(); // Essential: Processes USB events

  // Check if ESP32 is connected
  if (userial) {
    if (userial.available()) {
      // Read data from ESP32
      char c = userial.read();
      Serial.print(c); // Print to debug PC
    }
  }
}
 
You're setting the baud rate of the Teensy's USB port, not the userial object (which is the USB serial converter used by the ESP32).
But why connect them via USB anyway, when you could connect the UARTs of the ESP32 and Teensy directly?
 
Sooooo...UARTs on my wroom module: ...'0' is the native USB, '1' is the native memory writer, and the last one '2' is being used by the RPlidar C1.....so, without using softwareSerial, i'm left with reading off the native USB (which is a challenge I would like to solve). The reason the teensy baud rate is set at 460800 is because I thought the teensy USB host was acting as a pass through, and that the data would be best seen by arduino IDE using same baud rate as the esp32....I get that the USB 2.0 is much faster than the serial baud....
 
And, BTW, the Teensy myusb sees the data every 1 second like it should, it seems to be a matter of parsing/bit timing, and not that I am not getting data....

and the arduino IDE serial reads this at 460800 from the esp32, no issues to at least 5324 seconds :
Data packet: 5317
Data packet: 5318
Data packet: 5319
Data packet: 5320
Data packet: 5321
Data packet: 5322
Data packet: 5323
Data packet: 5324
 
The reason the teensy baud rate is set at 460800 is because I thought the teensy USB host was acting as a pass through
No. They're completely separate. Setting the speed of Serial doesn't actually do anything at all because it's an emulated UART.

Your "USBSerial userial(myusb)" object is a driver to talk to some sort of USB-to-UART chip on the ESP32 - it'll be something like a CH341 or a CP2101 - and you need to configure what baud rate it runs at, the same as how you have to select the correct speed in the Arduino IDE when you have the ESP32 directly connected to the PC. You're supposed to do this using the begin() function which you don't call at all for userial, so it's using the default value of 115200. The bits are coming in 4 times faster than that, that's why all the data is corrupted.
 
Thank you. OK, but I think as a USB host, the teensy can read data at "The Teensy 4.1's USB host port supports speeds of 1.5Mbit/sec, 12Mbit/sec, or 480Mbit/sec, depending on the device connected" which is way faster than my esp32 UART is getting data from the lidar device. I don't think I should have to change the teensy USB 2.0 speed. also, The begin() function for the teensy USB does not have a call to set a baud rate. My wroom esp32 dev board uses the CP2102 USB chip (a variable full speed USB 2.0 rate). The ESP32 USB to Teensy USB connection seems OK. I want to find a way to read the incoming esp32 USB bits in my teensy code. I think they are 8 bit packets from the esp32..... I will try to connect some other USB devices to teensy..
 
It's obviously not ok - it's using the default 115200 baud, which is why all the characters are corrupted. You need a call to userial.begin(460800); to set the correct baud rate on the CP2102.
 
I set "To set your ESP32 (using a CP2102 converter) to 460800 baud, use Serial.begin(460800); in your setup() function. This initializes the hardware UART0 (connected to the CP2102) to the specified rate" ..there is no call espressif for userial.begin()... My arduino IDE sees the esp32/CP2102 USB output just fine at 460800...
 
"To set your ESP32 (using a CP2102 converter) to 460800 baud, use Serial.begin(460800); in your setup() function. This initializes the hardware UART0 (connected to the CP2102) to the specified rate"
That sets the speed of the ESP32's UART, not the CP2102. The CP2102 is controlled by the USB host that it gets connected to,
..there is no call espressif for userial.begin()... My arduino IDE sees the esp32/CP2102 USB output just fine at 460800...
Because the Arduino IDE is setting the baud rate of the CP2102. If you connect the ESP32 to the Teensy instead, it has to do the same thing.

This is the final reply I'm going to make here - I've told you explicitly what to do, and for some reason you're just unwilling to accept it. It feels like I'm trying to argue with an AI.
 
Last edited:
there is no call espressif for userial.begin()

Of course there's nothing in espressif code for this.

When you connect one of those ESP boards to your PC, you have to set the serial baud rate in your PC software to match whatever baud rate the code running on the ESP32 uses. The espressif code doesn't have anything that affects the setting on your PC.

When you connect one of those ESP boards to Teensy 4.1's USB Host port, you also have to set the serial baud rate with userial.baud(460800) on the Teensy USB Host side to match whatever baud rate the code running on the ESP32 uses.


My arduino IDE sees the esp32/CP2102 USB output just fine at 460800...

Your Arduino IDE only sees the ESP output just fine at 460800 only after you select that baud rate in the Arduino Serial Monitor.

You had to click the drop down menu and scroll down and click "460800 baud". Nothing on the ESP side does this click for you!

1776208935541.png


On Teensy with USB Host, if you want communication to occur on userial to that ESP board at 460800 baud, you have to put userial.begin(460800) in you program. Nothing on the ESP does this for you. It is the same situation as using Arduino Serial Monitor. Both sides must have the same baud rate configured. For your application, using userial.begin(460800) on Teensy is the same as clicking the baud rate in Arduino Serial Monitor.

Your program on Teensy does have Serial.begin(460800), but that has no effect on userial. You use Serial to communicate with your PC via Teensy's USB device port. You use userial to communicate with the ESP board which plugs into Teensy's USB Host port. The 2 are completely separate. Just imagine how silly and limiting Teensy would be if we had only 1 place to set baud rate for everything?! If that were true, you couldn't connect a GPS receiver that uses 4800 or 9600 baud while using faster speed to communicate with other things. Of course that's not the way the system is designed. Each communication port gets it own speed setting, so you can communicate with multiple places, each at its own speed.


Or in other words, @jmarsh was correct about everything. Hopefully also hearing me also explain it will help you see that.
 
Last edited:
Thx jmarsh! Key statement: Because the Arduino IDE is setting the baud rate of the CP2102. If you connect the ESP32 to the Teensy instead, it has to do the same thing.

Even though you think your explanation is explicit, not explicit for everyone. I thought I was learning from AI! Thx Paul, for your reply too. It makes more so much more sense now. userial is on the teeny HOST side! I did not realize that via the arduino IDE, setting the baud affected the USB port on my desktop. I thought that the arduino IDE baud setting was only changing the data timing in the arduino program AFTER the USB hub data was received at standard USB 2.0/3.0 speeds......
 
Back in the early days of computers, when they had real hardware serial ports with 9 or 25 pin D-sub connectors, setting the baud rate in PC software actually did change hardware settings completely within the metal case of your PC.

Now in these modern times of USB serial adapters, when you click the baud rate menu in software like Arduino IDE, the device driver on your PC sends a USB control message to the CP2102 (or whatever other USB serial hardware is used) instructing it to use your desired baud rate.

Communication between your PC or Teensy USB host and the serial adapter is always at USB speed, usually 12 Mbit for CP2102 and most others, but some use 480 Mbit speed. The baud rate setting only affects the speed between the CP2102 and the ESP chip. It's essentially the same situation as if you had a real UART in an ancient PC. The baud rate setting changes the speed the UART chip uses on the serial wires, but your PC still communicates with the UART chip over PCI bus (and probably a PCI-ISA bridge) at whatever speed the native buses on your motherboard communicate. Baud rate setting doesn't alter PCI / PCIe bus speed, and neither does it change anything about actual USB communication speed.

So from a software point of view, on your ESP board the ESP32 chip is controlled by the code you write, and as far as PC software is concerned, the CP2102 chip soldered to the ESP board is effectively part of your PC.

As if that's not surreal enough, the situation becomes even more conceptually challenging with processors that have native USB ports, like all Teensy (since 2009) and most of Arduino's newer products, and even some of the very latest ESP chips. On boards like Teensy, there is no CP2102 chip. Software compiled into your program does all the stuff a chip like CP2102 would have done. So a portion of the code you upload to Teensy is acting as if it is part of your PC, as far as PC software is concerned.

Because native USB serial is done entirely with software, the baud rate number you give to Serial.begin(baud) is just put into a variable in case the PC wants to know it. And if software on the PC set sets the baud rate, the number the PC sent gets put into that variable when your PC sends the USB control message to change the baud rate. Remember, it's just like PCI bus in old PCs stays the same regardless of baud rate. The USB communication is always at USB speed, which is 480 Mbit for Teensy 4.x. With native USB boards like Teensy, it doesn't matter what baud rate you use with Serial.begin() or what you select in Arduino Serial Monitor. You will always get the output of Serial.print() correctly. And you can get it at a very fast pace because the communication really is 480 Mbit speed, so usually the real-world speed is limited only by software overhead on both sides.

Normally you would never need to use the baud rate info with native USB serial. But if you wanted Teensy to act as an expensive USB to serial adapter, you would need your program to read the PC's intended baud rate so you can use it to configure the Serial1 (or other port) on the Teensy side as the PC wanted. To see an example, in Arduino IDE click File > Examples > _Teensy > USB_Serial > USBtoSerial.
 
Great explanation! Thx Paul. That was part of my confusion....everything that I read said that the USB port speeds were basically set (why I said 'pass through'). I did not realize that setting host end userial baud sets the usb peripheral device onboard or dongle CP2102 connection to the ESP32.... i thought i was setting this in the ESP transmit code... This is why I buy teensy products....excellent devices and support!
 
Thank you both, works perfectly now. Teensy USB sees this from RPlidar C1:

A: 302 D: 9365
A: 312 D: 2652
A: 312 D: 2844
A: 322 D: 1756
A: 333 D: 2076
A: 333 D: 2076
A: 343 D: 1948
A: 322 D: 2205
A: 322 D: 1631
A: 333 D: 3871
A: 302 D: 8992
A: 85 D: 32
A: 136 D: 4064
A: 157 D: 1312
 
In case anyone is interested, here is the RPLidar C1 code to to parse data correctly without using esp32/USB/serial port:

Code:
#include <Arduino.h>

// Use Hardware Serial 1 on Teensy 4.1 (Pins 0 and 1)
#define LidarSerial Serial1

// Slamtec standard request byte markers
const uint8_t SYNC_BYTE = 0xA5;
const uint8_t START_SCAN = 0x20;

// Parser structure for the 5-byte data packet
uint8_t packetBuffer[5];
int packetIndex = 0;

// Function declarations
void sendStartScanCommand();

void setup() {
  // Initialize USB Serial Monitor
  Serial.begin(115200);
  while (!Serial && millis() < 3000); // Wait for Serial Monitor with timeout

  // C1 uses a strict 460800 baud rate over TTL UART
  LidarSerial.begin(460800);
 
  Serial.println("Teensy 4.1 Initializing RPLidar C1...");
  delay(1000);

  // The C1 motor starts and scans automatically via UART commands
  sendStartScanCommand();
}

void loop() {
  while (LidarSerial.available() > 0) {
    uint8_t currentByte = LidarSerial.read();

    // Look for a valid 5-byte measurement packet frame
    if (packetIndex == 0) {
      // Byte 0 contains the inverse of the start bit in bit 1, and bit 0 is always 1
      // Valid data point synchronization checks
      bool bit0 = currentByte & 0x01;
      bool bit1 = (currentByte >> 1) & 0x01;
      
      if (bit0 != bit1) { // Check for proper framing sync
        packetBuffer[packetIndex++] = currentByte;
      }
    }
    else if (packetIndex == 1) {
      // Byte 1 check: Bit 0 must always be 1 for a valid data point packet
      if (currentByte & 0x01) {
        packetBuffer[packetIndex++] = currentByte;
      } else {
        packetIndex = 0; // Invalid packet sequence, reset
      }
    }
    else {
      packetBuffer[packetIndex++] = currentByte;

      // Full 5-byte structure successfully populated
      if (packetIndex >= 5) {
        // Parse Quality, Angle, and Distance
        uint8_t quality = packetBuffer[0] >> 2;
        
        // Extract 15-bit Angle data
        uint16_t rawAngle = (packetBuffer[1] >> 1) | (packetBuffer[2] << 7);
        float angle = rawAngle / 64.0; // Scaled to degrees
        
        // Extract 16-bit Distance data
        uint16_t rawDistance = packetBuffer[3] | (packetBuffer[4] << 8);
        float distance = rawDistance / 4.0; // Scaled to millimeters

        // Output real-time coordinates if quality is reliable
        if (quality > 0 && distance > 0) {
          Serial.print("Angle: ");
          Serial.print(angle, 2);
          Serial.print("°\tDistance: ");
          Serial.print(distance, 1);
          Serial.print("mm\tQuality: ");
          Serial.println(quality);
        }

        packetIndex = 0; // Reset for next measurement frame
      }
    }
  }
}

// Forces the Lidar out of IDLE and into standard scanning mode
void sendStartScanCommand() {
  Serial.println("Sending SCAN request sequence to C1...");
  LidarSerial.write(SYNC_BYTE);
  LidarSerial.write(START_SCAN);
}
 
Back
Top