cancel
Showing results for 
Search instead for 
Did you mean: 

How do I determine when Virtual Com Port is closed/opened in my STM32L476 firmware?

arnold_w
Senior II

I'm working with the Nucleo-64 development board with an STM32L476 microcontroller and I'm using the USB source code that STM32CubeMX has generated for me. I want to be able to find out when the Virtual Com Port is closed/opened on the PC that the Nucleo-64 board is connected to. I've googled quite a bit, some suggests the condition is (Bool_t)(hUsbDeviceFS.ep0_state == USBD_EP0_STATUS_OUT), others suggest I should evaluate req->wValue inside the CDC_SET_CONTROL_LINE_STATE case statement in CDC_Control_FS. However, none of these methods work reliably for me. Can anybody please help me?

12 REPLIES 12

>>However, none of these methods work reliably for me.

Applications PC side really don't signal you, they can come and go, read and write data as it suits them. This is not a strongly architected communication channel. I presume you a) don't control the app code, and b) aren't planning on writing any drivers to facilitate the functionality.

I have observed that Arduino boards typically do a reasonable job sensing a connection, so perhaps look at those, and perhaps how the nuance end-point transactions to see that a terminal opens. I'd perhaps expect some back-n-forth querying. When it stop/disconnects might be harder.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

You'll spare much headache if you work simply with the fact that there's no way to detect that generically.

The long story is that RS232 has a dedicated signal - DTR - to indicate from "computer" to "modem" that computer is generally ready to accept data. Fast forward through several decades of ignoring and violating standards, and it's what it is. Applications still can set or clear DTR even through virtual serial port, and the "free" ("class", again a heavily abused term) M$ driver forwards this to the device indeed, but Windows won't change it when the application aborts, making it totally useless. Other/minority hosts/OS have then their own quirks and idiosyncracies in this regard (as well as in all other USB-related issues).

The original intention of USB was not that it creates universally interworking hosts and devices, but that it provides a rough framework within which individual device vendors provide both ends (i.e. the device itself and drivers into the host(s)). That there exist some half-working class drivers providing rudimentary functionality, is mostly an unwanted side effect.

JW

gbm
Lead III

If you use standard terminal program on PC side, then normally such a program changes DTE_PRESENT flag in ControlLine State to active when starting the session and to inactive when closing it.

You should handle the SetControlLineState request and monitor the change of DTE_PRESENT flag from 0 to 1. Then wait at least 20 ms before sending any data to the host via VCOM. That's my experience - using this approach I am able to display the greeting message whenever the terminal opens the VCOM port.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

When I search for "DTE_PRESENT", "ControlLine" and "SetControlLineState" in my entire STM32CubeIDE project, I find nothing. Perhaps are you working with a different microcontroller family?

I communicate with my own C# application and I'm calling System.IO.Ports.SerialPort.Open and System.IO.Ports.SerialPort.Close. If I add the following to usbd_cdc_if.c:

static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) {
  /* USER CODE BEGIN 5 */
  switch(cmd) {
    .
    .
    .
    case CDC_SET_CONTROL_LINE_STATE: {
        USBD_SetupReqTypedef * req = (USBD_SetupReqTypedef *)pbuf;
        LOG_STRING("\r\nreq->wValue: 0x");
        LOG_HEX((uint16_t)req->wValue, 4);
        break;
    }
    .
    .
    .
   return (USBD_OK);
   /* USER CODE END 5 */
}

then I can see the following printout every time I open/close the connection:

req->wValue: 0x0000  

If I instead make the following changes in usbd_ioreq.c:

Bool_t hostComPortOpen = FALSE;
USBD_StatusTypeDef USBD_CtlSendStatus(USBD_HandleTypeDef *pdev) {
  /* Set EP0 State */
  pdev->ep0_state = USBD_EP0_STATUS_IN;
 
  if (hostComPortOpen) {
      LOG_STRING("\r\nThe COM-port was closed");
  }
  hostComPortOpen = FALSE;
 
  /* Start the transfer */
  (void)USBD_LL_Transmit(pdev, 0x00U, NULL, 0U);
  return USBD_OK;
}
 
USBD_StatusTypeDef USBD_CtlReceiveStatus(USBD_HandleTypeDef *pdev) {
  /* Set EP0 State */
  pdev->ep0_state = USBD_EP0_STATUS_OUT;
 
  if ((!hostComPortOpen) && (pdev->dev_state == USBD_STATE_CONFIGURED)) {
      LOG_STRING("\r\nThe COM-port was opened");
      hostComPortOpen = TRUE;
  }
 
  /* Start the transfer */
  (void)USBD_LL_PrepareReceive(pdev, 0U, NULL, 0U);
   return USBD_OK;
}

then I get "The COM-port was closed" (once) when I close the connection and I get the following when I open the connection:

The COM-port was opened

The COM-port was closed

The COM-port was opened

The COM-port was closed

The COM-port was opened

The COM-port was closed

The COM-port was opened

The COM-port was closed

The COM-port was opened

The COM-port was closed

The COM-port was opened

The COM-port was closed

The COM-port was opened

The COM-port was closed

The COM-port was opened

This looks promising (after all, it end up with the correct state), but the problem is that after power-up it thinks the connection is opened, even though I haven't opened it yet.

gbm
Lead III

SetLineState method works for me in many devices based on many different STM32 microcontrollers (F0, F1, L4 series) and it does not depend on the microcontroller type - it's all in the CDC class requests, so I think there is some problem with your implementation or the version of ST USB device software you use (there were some problems with them; I use custom, heavily modified ST USB stack with my own composite device support).

Connection detection based on device connection status does not generally work - you can't see when the device was disconnected.

DTE_PRESENT is bit 0 of wValue of SET_CONTROL_LINE_STATE request. Make sure you use the working terminal emulator, like TeraTerm. Otherwise check if DTR and/or RTS/CTS handshake is turned on in the terminal emulator.

Added:

I found the problem in your code. You are not accessing the request packet. This is my working version:

    case CDC_SET_CONTROL_LINE_STATE:
		if (hUsbDeviceFS.request.wValue != cdc_data[0].controllinestate)
		{
			cdc_data[0].controllinestate = hUsbDeviceFS.request.wValue;
			cdc_data[0].ctrllinestate_changed = 1;
		}
        break;

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

I get the following compile error when I try that code:

'cdc_data' undeclared (first use in this function) usbd_cdc_if.c

gbm
Lead III

I am not surprised, as cdc_data is my own variable and this is just a small excerpt from my program. The code above illustrates how you could handle SetControlLineState request to get connection status from wValue and what to do with it.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
Javier1
Principal

I normally do (from the pc side) a ping every second, this way if the ping is not received i know something is wrong or the com port is closed

we dont need to firmware by ourselves, lets talk

How exactly do you send a ping (what functions do you call)? What do you do in case the ping fails to cleanup (do you just call USB_FlushTxFifo(USB_OTG_FS, 15) or do you do anything else)?