Teensy 4.1 doesn’t receive serial data when used remotely through VirtualHere (server)

Hi everyone,


I’ve been trying to use a Teensy 4.1 remotely through VirtualHere (USB over network), and I’ve found something interesting that looks like it’s coming from the Teensy side rather than VirtualHere itself.

The setup: the Teensy 4.1 is connected by USB to a Linux machine (rbpi) running the VirtualHere server, and I connect to it from a Windows 10 computer using the VirtualHere client.
When I do this, I can read data from the Teensy just fine — everything it prints appears on the client. But if I try to send data to the Teensy, nothing happens. It just never receives it.
This behaviour happens on the raspberrypi and also if i connecto the teensy to a server on widnows over a 2..0 connector on the laptop. BUT it does work correctly when connecting the teensy to a usb 3.0 port on the same laptop.

If I plug the same Teensy directly into my PC (no VirtualHere), the exact same sketch works perfectly — full bidirectional communication.

I reached out on the VirtualHere forum thinking it was something on their end, but the developer there actually tested it with a Teensy 4.0 and confirmed the same behaviour. He said it seems to be a latency issue — the Teensy’s USB serial implementation might be sensitive to timing when the USB host isn’t local.

Here’s the original thread for reference:
👉 https://www.virtualhere.com/node/4727


I also tested with other boards (like an Arduino UNO), and those work fine through VirtualHere, so it definitely seems to be something specific to how the Teensy handles USB serial.

Has anyone seen this before? Or does anyone know if the Teensy’s USB serial driver has some kind of tight timing expectation that could break if the USB packets are delayed slightly over a network?

I’d love to know if there’s a workaround — maybe using Serial.send_now(), or slowing down writes with small delays, or increasing buffer sizes somehow.

Another important detail is that when plugged on the 3.0 usb port and uploading remotley a sketch to it it seems to work fine but the program that has been loaded appers as if it has plenti of software bug showing erratic behaviour. Definetly somewthinng going wrong while transferring data. Does teensy loader check for data integrity after it has upload the sketch, or is there a way to enable it?


Any ideas or experiences with this would be really appreciated!


Thanks
 
By the way, the code I have on the teensy that reads the serial port is:

C++:
//////////////////////////////////////////////////////////
//    Proces COMMANDS from Serial
//////////////////////////////////////////////////////////
// Non-blocking serial input support
static String serialLineBuffer = "";

void loopSerialMenu () {    // Non-blocking serial input: accumulate until newline
  processDebugMenuRecurrentFunctions();  // process debug functions (the only fucntion that needs to run at high frequency)

  const long function_FPS = 30;     // run this function only at a 30fps
  static elapsedMillis lastRunTime = 0;
  if (lastRunTime < 1000/function_FPS) return;  // Skip if not enough time has passed
  lastRunTime = 0;
 
  while (Serial.available() > 0) {
    char c = (char)Serial.read();
    // blinkLED(LED_BUILTIN);  // Blink built-in LED to show activity for testing (no activity when in remote)
    // if (c == '\r') continue; // ignore CR
    // if (c == '\n') {
    if ((c == '\n') || (c == '\r')) {
      String input = serialLineBuffer;
      serialLineBuffer = "";
      input.trim();
      if (input.length() > 0) processCommand(input);   // Porcess command for current menu
    } else if (c == 8 || c == 127) { // backspace/delete
      if (serialLineBuffer.length() > 0) serialLineBuffer.remove(serialLineBuffer.length() - 1);
    } else {
      serialLineBuffer += c;
    }
  }

  // process recurrent functions //////////////////////////////////////////////////////////
  processMotorMenuRecurrentFunctions();  // process motor functions
  processDmxMenuRecurrentFunctions();    // process DMX functions
  processServoMenuRecurrentFunctions();  // process servo functions
 
  /////////////////////////////////////////////////////////////////////////////////////////
}
 
From a quick cursory look the problem seems to be with static elapsedMillis lastRunTime = 0;. Each time you enter the function it is set to zero which will cause you to exit the fn in the next line of code.
To overcome the problem put the elapsedMillis creation outside the fn.
Code:
//////////////////////////////////////////////////////////
//    Proces COMMANDS from Serial
//////////////////////////////////////////////////////////
// Non-blocking serial input support
static String serialLineBuffer = "";
static elapsedMillis lastRunTime = 0;

void loopSerialMenu () {    // Non-blocking serial input: accumulate until newline
  processDebugMenuRecurrentFunctions();  // process debug functions (the only fucntion that needs to run at high frequency)

  const long function_FPS = 30;     // run this function only at a 30fps
  if (lastRunTime < 1000/function_FPS) return;  // Skip if not enough time has passed
  lastRunTime = 0;
 
  while (Serial.available() > 0) {
    char c = (char)Serial.read();
    // blinkLED(LED_BUILTIN);  // Blink built-in LED to show activity for testing (no activity when in remote)
    // if (c == '\r') continue; // ignore CR
    // if (c == '\n') {
    if ((c == '\n') || (c == '\r')) {
      String input = serialLineBuffer;
      serialLineBuffer = "";
      input.trim();
      if (input.length() > 0) processCommand(input);   // Porcess command for current menu
    } else if (c == 8 || c == 127) { // backspace/delete
      if (serialLineBuffer.length() > 0) serialLineBuffer.remove(serialLineBuffer.length() - 1);
    } else {
      serialLineBuffer += c;
    }
  }

  // process recurrent functions //////////////////////////////////////////////////////////
  processMotorMenuRecurrentFunctions();  // process motor functions
  processDmxMenuRecurrentFunctions();    // process DMX functions
  processServoMenuRecurrentFunctions();  // process servo functions
 
  /////////////////////////////////////////////////////////////////////////////////////////
}
 
Hi @BriComp , thanks for your answer, I agree with @jmarsh , when defined as static it will be initialized once.
And actually the code works perfectly running locally with the teensy connected trough USB. The problem of the teensy not entering the "while (Serial.available() > 0) { " appears only when connecting remotely using the virtualhere software.
 
Back
Top