-
Notifications
You must be signed in to change notification settings - Fork 7.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
USB CDC can receive but cannot transmit data (IDFGH-12740) #13722
Comments
Hi @StevenMacias thanks for reporting. |
Hi @peter-marcisovsky, |
Hi @StevenMacias, sorry for a delay. After some extensive testing, following your steps to reproduce, I have found a repeating pattern, causing an error. I observed, that the USB device sometimes sends a 0 length CDC IN transfer to the USB host, right after re-plugging the USB device. The device sends the transfer before the USB host is ready to receive one, which in general might cause transfer problems. (The host indicates readiness for data reception by Once the 0 length CDC in transfer appears before the host is ready to receive any transfer, the error is triggered and the USB device behaves just like you are explaining. To fix the issue, I canceled all the pending transfers after the USB device is mounted, by stalling and un-stalling the endpoints. I could not reproduce the error anymore after that. A pseudo function to cancel the pending transfers, would be placed into the TinyUSB device stack
The function shall be invoked before any data transfer is made, or right after unplugging the USB device. In my case, I used mount callback from TinyUSB The TinyUSB log of stalling and un-stalling the endpoints shall look like this:
What could also be a problem in your case especially, is the way how the I hope this workaround resolves the issue you are having. |
Thanks a lot for your reply! We will test it immediately and see if it solves the issue. Thanks again 😄 |
Hi @StevenMacias any update on this issue? |
@peter-marcisovsky so sorry for the delay, we have been a quite overloaded lately. This does not seems to solve our issue since it also fails during runtime, not only when plugging and unplugging the cable. This is how our TX callback looks like: esp_err_t usb_cdc_serial_tx(const char* buffer, size_t length)
{
ESP_LOGD(TAG, "Send:\t%s", buffer);
size_t bytes_written = tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, (uint8_t*)buffer, length);
if (bytes_written != length)
ESP_LOGW(TAG, "USB serial TX FIFO lacked space, dropping outgoing message; bytes_written: %d, length: %d", bytes_written, length);
return tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, pdMS_TO_TICKS(200));
} When the issue is triggered it looks like this, please pay attention to the "USB serial TX FIFO lacked space" log and correlate it with the above function.
Basically we are sending a JSON message over CDC from Host to ESP32 (Downstream), a message is reported upstream, you can also see tha we are using the RMT peripheral at the same time (I don't know if that can be related). When message Can you see something wrong in our callback? Not sure if you shared the new version of the examples already. Thanks again and sorry for the late feedback. |
Hi @StevenMacias sorry to hear that the issue has not been resolved. Is there any reason, why are you using timeout other than 0 in your Regarding the audio support. We just released our UAC driver (USB Host side) here. The USB Device side is not a priority right now for us. |
Hi @peter-marcisovsky, esp_err_t usb_cdc_serial_tx(const char* buffer, size_t length)
{
ESP_LOGD(TAG, "Send:\t%s", buffer);
size_t bytes_written = tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, (uint8_t*)buffer, length);
if (bytes_written != length) {
ESP_LOGW(TAG, "USB CDC failed to write queue. Written %d out of %d bytes.", bytes_written, length);
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, length, ESP_LOG_WARN);
}
esp_err_t err = tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, pdMS_TO_TICKS(200));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Write flush failed. Reason: %s", esp_err_to_name(err));
}
return err;
} Since we are expecting JSON strings that end with void usb_cdc_rx_callback(int interface_index, cdcacm_event_t*)
{
size_t rx_size = 0;
esp_err_t ret = tinyusb_cdcacm_read(interface_index, usb_cdc_rx_buf,
CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size);
if (ret == ESP_OK) {
if (msg_idx <= (USB_CDC_MSG_MAX_SIZE - rx_size)) {
memcpy(serial_msg + msg_idx, usb_cdc_rx_buf, rx_size);
msg_idx += rx_size;
} else {
ESP_LOGE(TAG, "Message too big (%zu+%zu bytes) for buffer size (%d bytes)",
msg_idx,
rx_size,
USB_CDC_MSG_MAX_SIZE);
msg_idx = 0;
usb_cdc_clear_serial_buffers();
}
if (wanted_char_flag) {
wanted_char_flag = false;
// ESP_LOG_BUFFER_HEXDUMP(TAG, serial_msg, msg_idx, ESP_LOG_INFO);
if (msg_buffer_handle != NULL) {
size_t written_bytes = xMessageBufferSend(msg_buffer_handle, serial_msg, msg_idx, 0);
if (written_bytes < msg_idx) {
ESP_LOGW(TAG, "RX Message buffer is full.");
}
} else {
ESP_LOGW(TAG, "Received data but message buffer not registered.");
}
usb_cdc_clear_serial_buffers();
}
} else {
ESP_LOGE(TAG, "Read error");
}
} No special reason to use a timeout in
Thanks again for your time and support! 😃 |
|
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/message_buffer.h"
#include "esp_err.h"
#define USB_CDC_TERMINATION_CHAR 0x0A
#define USB_CDC_MSG_MAX_SIZE 2048u
esp_err_t usb_cdc_init(void);
esp_err_t usb_cdc_serial_tx(const char* buffer, size_t length);
void usb_cdc_serial_register_message_buffer(MessageBufferHandle_t msg_buffer); The goal of At the other end of the message buffer it will be a task ( static char message_router_rx_msg[USB_CDC_MSG_MAX_SIZE] = { 0 };
static char message_router_tx_msg[USB_CDC_MSG_MAX_SIZE] = { 0 };
static MessageBufferHandle_t message_router_tx_msg_buf_handle;
void message_router_application_task(void* pvParams)
{
// This is the same handler used in usb_cdc_serial_register_message_buffer
MessageBufferHandle_t message_router_rx_msg_buf_handle = (MessageBufferHandle_t)pvParams;
// This tx handler is used internally by this task
message_router_tx_msg_buf_handle = xMessageBufferCreate(USB_CDC_MSG_MAX_SIZE);
size_t rx_message_size = 0;
size_t tx_message_size = 0;
for (;;) {
rx_message_size = xMessageBufferReceive(
message_router_rx_msg_buf_handle,
message_router_rx_msg,
sizeof(message_router_rx_msg),
pdMS_TO_TICKS(MESSAGE_ROUTER_TX_WAIT_MS));
if (rx_message_size > 0) {
message_router_process_incoming_message(message_router_rx_msg, rx_message_size, NULL);
// Limit downstream message throughput
vTaskDelay(pdMS_TO_TICKS(MESSAGE_ROUTER_TX_SENT_DELAY_MS));
ESP_LOGD(TAG, "Sending message downstream");
ESP_LOG_BUFFER_HEXDUMP(TAG, message_router_rx_msg, rx_message_size, ESP_LOG_DEBUG);
}
tx_message_size = xMessageBufferReceive(
message_router_tx_msg_buf_handle,
message_router_tx_msg,
sizeof(message_router_tx_msg),
pdMS_TO_TICKS(MESSAGE_ROUTER_RX_WAIT_MS));
if (tx_message_size > 0) {
usb_cdc_serial_tx(message_router_tx_msg, tx_message_size);
// Limit upstream message throughput
vTaskDelay(pdMS_TO_TICKS(MESSAGE_ROUTER_RX_SENT_DELAY_MS));
ESP_LOGD(TAG, "Sending message upstream");
ESP_LOG_BUFFER_HEXDUMP(TAG, message_router_tx_msg, tx_message_size, ESP_LOG_DEBUG);
}
}
} The task loop basically checks two message buffers, the one shared with the USB CDC component (
I do not have a message buffer from Message Router to USB CDC because I do not have a task that can block waiting for a message buffer to be written. Do you think that is an issue? As I mentioned at the beginning we did not find the need of the USB CDC component to be a task.
static MessageBufferHandle_t message_router_msg_buf_handle;
static void init_tasks()
{
message_router_msg_buf_handle = xMessageBufferCreate(USB_CDC_MSG_MAX_SIZE);
xTaskCreate(message_router_application_task,
"message_router_application_task",
(1024 * 6),
(void*)message_router_msg_buf_handle,
tskIDLE_PRIORITY + 1,
NULL);
}
void main_application_task(void* pvParams)
{
[...]
init_tasks();
init_led();
[..]
usb_cdc_serial_register_message_buffer(message_router_msg_buf_handle);
[...]
err = usb_init(); // calls tinyusb_driver_install and usb_cdc_init
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error initializing USB: %s", esp_err_to_name(err));
}
for (;;) {
[...]
}
}
As always, thank you so much for your time and help! 😄 |
Answers checklist.
IDF version.
v5.1.2
Espressif SoC revision.
ESP32-S3
Operating System used.
Linux
How did you build your project?
Command line with idf.py
If you are using Windows, please specify command line type.
None
Development Kit.
ESP32-S3-DevKitC-1 v1.0
Power Supply used.
USB
What is the expected behavior?
Reliable bi-directional USB CDC communication.
What is the actual behavior?
The board can receive data but cannot send data back to the host.
Steps to reproduce.
get_idf
cp -r $IDF_PATH/examples/peripherals/usb/device/tusb_serial_device Workspace/local/espressif/serial_issue
cd Workspace/local/espressif/serial_issue
idf.py set-target esp32s3
UART
connector on the DevKitidf.py flash monitor
USB
connector on the DevKitpython serial_spammer.py
USB
cable while spamming. The spammer program will exit.USB
cable and execute spamming script again.minicom -D /dev/ttyACM0
. Bytes are received and plotted in theidf.py monitor
but not echoed to minicom....
Debug Logs.
No response
More Information.
Our project only has one USB (not self-powered device) but this issue appears by just using the interface. However, the symptoms are exactly the same than when the USB is disconnected in the DevKit. The USB disconnection helps us to trigger the issue quickly but from our experience we can say it can happen at any moment. Right after reset or after an hour of usage.
This issue seems related to #13518, however we believe that reproducing it with a vanilla example and a DevKit might be helpful to triangulate the root cause. Following some of the comments, we are using the latest version of espressif/tinyusb (0.15.0~8) but the issue persists.
The text was updated successfully, but these errors were encountered: