Detecting USB Serial Connection State on Teensy 4.1

samm_flynn

Active member
Hey everyone,

I have a Teensy 4.1 controlling two motors, and I need a way to disarm them if the USB serial connection is lost. Right now, in my control loop, which runs every 1ms, I check Serial.dtr() before sending any commands to the motors.

However, I was wondering if there’s a way to avoid polling Serial.dtr() every cycle and instead have an event-based approach, like setting a variable to true or false automatically when the USB serial is opened or closed.

The main reason for this is safety. My PC app (written in Python) sends commands over USB, and a USB disconnection is a major concern. If the USB disconnects unexpectedly, I need the motors to immediately stop without waiting for the next loop iteration to check Serial.dtr().

Is there a way to achieve this using USB events or interrupts instead of polling? Maybe something in the USB stack that I can hook into? Any guidance would be greatly appreciated!

I swear I saw a thread discussing this, but can't find it. Sorry if this post is a duplicate.
 
I have a working solution, but I'm unsure about the potential implications of these modifications.If anyone with more experience could take a look, I’d really appreciate it.

I modified the Teensy 4.1 core files to call a callback function whenever the USB serial connection opens or closes
Changes:
usb_serial.h – Declare an external function pointer:
C:
extern void (*usb_callback)(void);
usb_serial.c -Initialize it to NULL:
C:
void (*usb_callback)(void) = 0;  // initialize to NULL
usb.c - Call usb_callback() inside CDC_SET_CONTROL_LINE_STATE (0x2221):
C:
      case 0x2221: // CDC_SET_CONTROL_LINE_STATE
        #ifdef CDC_STATUS_INTERFACE
        if (setup.wIndex == CDC_STATUS_INTERFACE) {
            usb_cdc_line_rtsdtr_millis = systick_millis_count;
            usb_cdc_line_rtsdtr = setup.wValue;
            if (usb_callback) {
                usb_callback();  // Call externally defined callback
            }
        }
and the main.cpp -Define the callback:
C++:
#include <Arduino.h>
volatile uint8_t callback_counter = 0;
volatile bool isConnected = false;
elapsedMillis timer;
const uint32_t interval = 200;
void my_usb_callback()
{
  callback_counter++;
  isConnected = Serial.dtr();
  if (isConnected)
    digitalWrite(13, HIGH);
  else
    digitalWrite(13, LOW);
}
void setup(void)
{
  usb_callback = my_usb_callback;
  pinMode(13, OUTPUT);
  while (!Serial)
    ;
}
void loop(void)
{
  if (timer >= interval)
  {
    timer = 0; // Reset the timer
    Serial.printf("Counter :%u| state:%d\n", callback_counter, isConnected);
  }
}
The LED on pin 13 in this case represents if the Serial port is opened by a PC application or not.

I don’t know if this approach is the best way to do it, or if it could cause issues elsewhere. I searched but couldn’t find a built-in way to handle this. Since my programming experience is limited, I’d really appreciate any advice or suggestions. Thanks in advance!
 
Last edited:
with different applications, the callback counter is incremented by different amount.
EDIT: Sorted some of the issues
 
Last edited:
On a few T4.1 synth projects I check (device) usb status like this. Not sure if useful for you:
Code:
  if (!bitRead(USB1_PORTSC1,7)) usbPcStatus = true;
  else usbPcStatus = false;
 
On a few T4.1 synth projects I check (device) usb status like this. Not sure if useful for you:
Code:
  if (!bitRead(USB1_PORTSC1,7)) usbPcStatus = true;
  else usbPcStatus = false;
I am monitoring the value of !bitRead(USB1_PORTSC1,7) with a hardware serial port.
for some reason, it's always 1 for me
 
I am monitoring the value of !bitRead(USB1_PORTSC1,7) with a hardware serial port.
for some reason, it's always 1 for me

What do you mean when you say that you are monitoring that bit "with a hardware serial port"? It's always good to show your code rather than having to guess what you're doing.
 
Like I get the following output


C++:
#include <Arduino.h>
volatile bool isConnected = false;
void my_usb_callback()
{
  isConnected = Serial.dtr();
  digitalWrite(13, isConnected);
  Serial1.printf("%s\n", isConnected ? "C" : "D");
  Serial1.println(!bitRead(USB1_PORTSC1,7));
}
elapsedMillis timer;

void setup(void)
{
  usb_callback = my_usb_callback;
  pinMode(13, OUTPUT);
  Serial1.begin(115200);
}
void loop(void)
{

}
void serialEvent() {
  int availableBytes = Serial.available();
      char buffer[availableBytes];
      Serial.readBytes(buffer, availableBytes);
      Serial1.write(buffer, availableBytes);
}


Code:
D
1
C
1
C
1
D
1
 
Check the definition of the fields of PORTSC1 on pages 2477-2478 of the IMXRT1060 reference manual. I've never tested, but it looks like you might want b0 = CCS (Current Connect Status).
 
Back
Top