STM32Cube and CDC device - how do I detect that the host disconnects? - Page 1
Forum section: electronics / general

author: agehall
author_rank: Frequent Contributor
timestamp: April 12, 2021, 08:17:17 pm
content: So, long story short - I finally managed to get myself out of the Arduino framework which I never liked but it has been too convenient for most of the stuff I've had to do in embedded MCUs the past few years. I'm trying to get things up and running on a WeAct BlackPill which is based on a STM32F401CC controller.At this point, all I want to do is get a USB CDC device going so that I can communicate properly with my software using a serial terminal on my PC.The steps I follow to make this happen, is that I set up the device in STM32Cube, enable USB_OTG_FS as device only, no low power, no link power management, no VBUS sensing and no SOF. I then add a CDC class for it and make no changes to that. Finally I save and use stm32pio to generate a PlatformIO compatible project.So far, so good. The code compiles and runs fine on the board. There seems to be an issue with the generated code so CDC_SET_LINE_CODING and CDC_GET_LINE_CODING needs some massaging to get the device to enumerate but google was very helpful there. With that problem sorted, I can now haveCode:while (1){CDC_Transmit_FS((uint8_t *)".", 1);HAL_Delay(1000);}print a dot every second. The issue is that it the checks in the CDC_Transmit_FS() function doesn't seem to be able to detect if I'm connected or not until after a the first transmission. So if I wait a couple of seconds before I open the COM port from the PC, I will get ".." as the first printout and after that I get single period. Same thing if I disconnect and reconnect a few seconds later - I get ".." at the start.I've been trying to google this problem but the best solution I see is to monitor VBUS but that isn't connected on a BlackPill. Apparently the abstraction layers are capable of detecting that the first "." sent is not transmitted and doesn't queue another transmission until I'm actually connected again.I've done some debugging and pretty much concluded that the pClassData->TxState variable being set to 1 is the only thing that causes CDC_Transmit_FS() to bail out early after the first "." is in the queue until the host connects again.I feel like I'm missing something very basic here but I'm kind of lost as to what that is.Anyone have any ideas/pointers?

author: hans
author_rank: Super Contributor
timestamp: April 13, 2021, 02:42:12 pm
content: Perhaps you can look for the SOF interrupt of the USB device. SOF is used for timing of audio devices IIRC, but perhaps a timeout on that interrupt can also indicate that the host is not talking to the device anymore.E.g. have the SOF ISR write down the system time, and then if you want to check the status, check whether that time is still recent enough.

author: agehall
author_rank: Frequent Contributor
timestamp: April 13, 2021, 04:09:06 pm
content: Thanks for the suggestion. I saw suggestions along those lines on google last night. That just feels like a strange way of doing this. I might try this solution - if it works, I guess I can live with it.I also did some more digging today and found out that if I compile a very basic Arduino-based project, everything works just like I would expect it to so I’ve started comparing the code a bit to see if I can find any relevant differences.

author: pigrew
author_rank: Frequent Contributor
timestamp: April 13, 2021, 04:39:17 pm
content: Vbus-detect is the only "proper" method to detect if the host disconnects.A lack of SOF for 3ms signifies a request for the device to enter suspend mode (but shouldn't reset the device, AFAIK, so the USB stack should maintain its state).You probably should trigger your "sleep" (and perhaps I/O buffer clear) on a suspend event.With the ST USB stack, I don't see a callback, but you should be able to look at the dev_state member of the HAL USB device structure, and check if it is USBD_STATE_SUSPENDED. At that point, the USB stack should maintain its configuration and such (unless it received a reset from the host). You should be able to do this in the "application" event handler (but I don't have the code in front of me to verify).

author: agehall
author_rank: Frequent Contributor
timestamp: April 13, 2021, 05:39:02 pm
content: Yeah, checking dev_state makes a lot of sense to me. I did try that but I didn't see the results I expected. I'm thinking I was working too late on this last night so I'll try to revisit that with a clear head tomorrow.

author: agehall
author_rank: Frequent Contributor
timestamp: April 13, 2021, 06:12:39 pm
content: Actually, thinking of the VBUS solution - would that really work here? If the host just closes the communication but the device is still connected, then the VBUS would still indicate that communication should be up and running, right?In my case, I want to detect that the terminal emulator just closes the COM port on the PC.

author: harerod
author_rank: Frequent Contributor
timestamp: April 13, 2021, 06:40:52 pm
content: Quote from: agehall on April 13, 2021, 06:12:39 pmAsk the TO. He wanted to detect whether the device was disconnected from the host. VBUS is a surefire way to find that out. ;)Joke aside, USB connectors are designed in a way so that VBUS is the first to be connected, before data, and therefore the last to be deconnected, after VBUS.Or did you mean a logical disconnect, instead of a physical disconnect?

author: bugnate
author_rank: Regular Contributor
timestamp: April 13, 2021, 06:44:58 pm
content: Quote from: agehall on April 13, 2021, 06:12:39 pmRight. I think there is a confounding of issues here. The COM port thing you say you want and the lower level state of the USB (on/off/suspend and checking Vbus) are two completely different things.There is normally no "event" associated with firing up your terminal emulator. When you connect the USB itself the OS immediately starts buffering any incoming serial data. That buffer gets dumped to the terminal when you open it. The Device side has no knowledge of any of this unless things go wrong (e.g., the host buffer is full).There are a couple of recent posts here on CDC flow control that might interest you:https://www.eevblog.com/forum/microcontrollers/hardware-flow-control-in-usb-cdc/https://www.eevblog.com/forum/microcontrollers/usb-cdc-_flow-control_/I do use USB-CDC often on stm32 but haven't had to wade into this particular issue enough to offer much more advice. I'd suggest you could do something simple like not transmitting until you press a key on the host (if you have bi-directional set up).

author: agehall
author_rank: Frequent Contributor
timestamp: April 13, 2021, 06:54:57 pm
content: The simple stuff works fine. Echoing data is no problem, nor is just sending data.The issue is that there seems to be no detection whatsoever if there is an actual logical connection from the terminal emulator. Thus when I try to send the first batch of data, it will be passed into the buffers and the device will attempt to transmit it until I connect the terminal and the string is sent. So if I print a “.” every iteration in the main loop, I will always get “..” upon connection if I wait a short while before connecting.I was making the assumption that the endpoints are not open until I open the COM port in the terminal emulator but maybe that assumption is wrong?What really bugs me is that the same basic code based on the Arduino frameworks seems to accomplish exactly what I want but I just don’t see why it works with them. I was planning on spending a few more hours this evening comparing code but work got the better of me today so this will have to wait until tomorrow.I do appreciate the tips and hints so far though. I do realize that when it comes to USB, I still have much to learn.

author: bugnate
author_rank: Regular Contributor
timestamp: April 13, 2021, 06:57:38 pm
content: Quote from: agehall on April 13, 2021, 06:54:57 pmThis.

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 13, 2021, 10:31:54 pm
content: Quote from: agehall on April 13, 2021, 05:39:02 pmShould work. hUsbDeviceFS.dev_state is USBD_STATE_CONFIGURED (3) when plugged, and USBD_STATE_SUSPENDED(4) when unplugged.And always seems to change accordingly. I've cycled the connector like 20 times and did ok everytime.

author: agehall
author_rank: Frequent Contributor
timestamp: April 18, 2021, 08:12:38 pm
content: So, work got the better of me for most of the week. But today I've managed to do some more work on this.It seems that hUsbDeviceFS.dev_state isn't enough for me. That will just show if the device is plugged in or not, but I need to see if the host has actually opened the VCP and is ready to read from it.I tried to implement a simple timeout function that checks if a transmission takes longer than X ms and if it does, aborts the transfer. However, this is a bit backwards - I found an old post on the ST forums saying that Smoothieware implemented this by keeping tabs on the read tokens sent by the host. That seems like a better approach but I can't figure out how to do that in the ST USB stack.