From 5f48d9a3f2281c1327f8135a1cd30c90ad41dd6c Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 8 Apr 2024 00:15:11 -0300 Subject: [PATCH 1/9] feat: backports HWCDC --- cores/esp32/HWCDC.cpp | 293 ++++++++++++++---- cores/esp32/HWCDC.h | 9 + .../examples/HWSerial_Events/.skip.esp32 | 0 .../examples/HWSerial_Events/.skip.esp32s2 | 0 .../HWSerial_Events/HWSerial_Events.ino | 95 ++++++ 5 files changed, 329 insertions(+), 68 deletions(-) create mode 100644 libraries/ESP32/examples/HWSerial_Events/.skip.esp32 create mode 100644 libraries/ESP32/examples/HWSerial_Events/.skip.esp32s2 create mode 100644 libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino diff --git a/cores/esp32/HWCDC.cpp b/cores/esp32/HWCDC.cpp index b97e32daf4c..4b2b2fafb81 100644 --- a/cores/esp32/HWCDC.cpp +++ b/cores/esp32/HWCDC.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -30,12 +30,14 @@ static RingbufHandle_t tx_ring_buf = NULL; static xQueueHandle rx_queue = NULL; static uint8_t rx_data_buf[64] = {0}; static intr_handle_t intr_handle = NULL; -static volatile bool initial_empty = false; +static volatile bool connected = false; static xSemaphoreHandle tx_lock = NULL; -// workaround for when USB CDC is not connected -static uint32_t tx_timeout_ms = 0; -static bool tx_timeout_change_request = false; +static volatile unsigned long lastSOF_ms; +static volatile uint8_t SOF_TIMEOUT; + +// timeout has no effect when USB CDC is unplugged +static uint32_t tx_timeout_ms = 100; static esp_event_loop_handle_t arduino_hw_cdc_event_loop_handle = NULL; @@ -73,21 +75,17 @@ static void hw_cdc_isr_handler(void *arg) { if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) { // Interrupt tells us the host picked up the data we sent. - if (usb_serial_jtag_ll_txfifo_writable() == 1) { + if(!HWCDC::isPlugged()) { + connected = false; + usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + // USB is unplugged, nothing to be done here + return; + } else { + connected = true; + } + if (tx_ring_buf != NULL && usb_serial_jtag_ll_txfifo_writable() == 1) { // We disable the interrupt here so that the interrupt won't be triggered if there is no data to send. usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); - if(!initial_empty){ - initial_empty = true; - // First time USB is plugged and the application has not explicitly set TX Timeout, set it to default 100ms. - // Otherwise, USB is still unplugged and the timeout will be kept as Zero in order to avoid any delay in the - // application whenever it uses write() and the TX Queue gets full. - if (!tx_timeout_change_request) { - tx_timeout_ms = 100; - } - //send event? - //ets_printf("CONNECTED\n"); - arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_CONNECTED_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); - } size_t queued_size; uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR(tx_ring_buf, &queued_size, 64); // If the hardware fifo is avaliable, write in it. Otherwise, do nothing. @@ -97,7 +95,7 @@ static void hw_cdc_isr_handler(void *arg) { usb_serial_jtag_ll_write_txfifo(queued_buff, queued_size); usb_serial_jtag_ll_txfifo_flush(); vRingbufferReturnItemFromISR(tx_ring_buf, queued_buff, &xTaskWoken); - usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); //send event? //ets_printf("TX:%u\n", queued_size); event.tx.len = queued_size; @@ -119,18 +117,20 @@ static void hw_cdc_isr_handler(void *arg) { break; } } - //send event? - //ets_printf("RX:%u/%u\n", i, rx_fifo_len); event.rx.len = i; arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_RX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); + connected = true; } if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) { usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_BUS_RESET); - initial_empty = false; - usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); - //ets_printf("BUS_RESET\n"); arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_BUS_RESET_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); + connected = false; + } + + if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SOF) { + usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SOF); + lastSOF_ms = millis(); } if (xTaskWoken == pdTRUE) { @@ -138,26 +138,117 @@ static void hw_cdc_isr_handler(void *arg) { } } +inline bool HWCDC::isPlugged(void) +{ + return (lastSOF_ms + SOF_TIMEOUT) >= millis(); +} + +bool HWCDC::isCDC_Connected() +{ + static bool running = false; + + // USB may be unplugged + if (!isPlugged()) { + connected = false; + running = false; + SOF_TIMEOUT = 5; // SOF timeout when unplugged + return false; + } else { + SOF_TIMEOUT = 50; // SOF timeout when plugged + } + + if (connected) { + running = false; + return true; + } + + if (running == false && !connected) { // enables it only once! + usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + } + // this will feed CDC TX FIFO to trigger IN_EMPTY + //uint8_t c = '\0'; + //usb_serial_jtag_ll_write_txfifo(&c, sizeof(c)); + usb_serial_jtag_ll_txfifo_flush(); + running = true; + return false; +} + +static void flushTXBuffer(const uint8_t *buffer, size_t size) +{ + if (!tx_ring_buf) return; + UBaseType_t uxItemsWaiting= 0; + vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); + size_t freeSpace = xRingbufferGetCurFreeSize(tx_ring_buf); + size_t ringbufferLength = freeSpace + uxItemsWaiting; + + if(buffer == NULL) { + // just flush the whole ring buffer and exit - used by HWCDC::flush() + size_t queued_size = 0; + uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo(tx_ring_buf, &queued_size, 0, ringbufferLength); + if (queued_size && queued_buff != NULL) { + vRingbufferReturnItem(tx_ring_buf, (void *)queued_buff); + } + return; + } + if(size == 0) return; // nothing to do + if(freeSpace >= size){ + // just add the data to the ring buffer and exit + if(xRingbufferSend(tx_ring_buf, (void*)buffer, size, 0) != pdTRUE){ + return; + } + } else { + // how many byte should be flushed to make space for the new data + size_t to_flush = size - freeSpace; + if(to_flush > ringbufferLength) to_flush = ringbufferLength; + size_t queued_size = 0; + uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo(tx_ring_buf, &queued_size, 0, to_flush); + if (queued_size && queued_buff != NULL) { + vRingbufferReturnItem(tx_ring_buf, (void *)queued_buff); + } + // now add the new data that fits to the ring buffer + uint8_t *bptr = (uint8_t *)buffer; + if (size >= ringbufferLength) { + size = ringbufferLength; + bptr = (uint8_t *)buffer + (size - ringbufferLength); + } + if(xRingbufferSend(tx_ring_buf, (void *)bptr, size, 0) != pdTRUE){ + return; + } + } + // flushes CDC FIFO + usb_serial_jtag_ll_txfifo_flush(); +} + static void ARDUINO_ISR_ATTR cdc0_write_char(char c) { + if(tx_ring_buf == NULL) { + return; + } + if(!HWCDC::isConnected()) { + // just pop/push RingBuffer and apply FIFO policy + flushTXBuffer((const uint8_t*)&c, 1); + return; + } if(xPortInIsrContext()){ xRingbufferSendFromISR(tx_ring_buf, (void*) (&c), 1, NULL); } else { xRingbufferSend(tx_ring_buf, (void*) (&c), 1, tx_timeout_ms / portTICK_PERIOD_MS); } - usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + usb_serial_jtag_ll_txfifo_flush(); } HWCDC::HWCDC() { - + lastSOF_ms = 0; + SOF_TIMEOUT = 5; } HWCDC::~HWCDC(){ end(); } +// It should return just when USB is plugged and CDC is connected. HWCDC::operator bool() const { - return initial_empty; + return HWCDC::isCDC_Connected(); } void HWCDC::onEvent(esp_event_handler_t callback){ @@ -168,6 +259,16 @@ void HWCDC::onEvent(arduino_hw_cdc_event_t event, esp_event_handler_t callback){ arduino_hw_cdc_event_handler_register_with(ARDUINO_HW_CDC_EVENTS, event, callback, this); } +void HWCDC::deinit() +{ + // Setting USB D+ D- pins + // Force the host to re-enumerate (BUS_RESET) + pinMode(USB_DM_GPIO_NUM, OUTPUT_OPEN_DRAIN); + pinMode(USB_DP_GPIO_NUM, OUTPUT_OPEN_DRAIN); + digitalWrite(USB_DM_GPIO_NUM, LOW); + digitalWrite(USB_DP_GPIO_NUM, LOW); +} + void HWCDC::begin(unsigned long baud) { if(tx_lock == NULL) { @@ -185,20 +286,35 @@ void HWCDC::begin(unsigned long baud) log_e("HW CDC TX Buffer error"); } } + + // the HW Serial pins needs to be first deinited in order to allow `if(Serial)` to work :-( + deinit(); + delay(10); // USB Host has to enumerate it again + + // Configure PHY + // USB_Serial_JTAG use internal PHY + USB_SERIAL_JTAG.conf0.phy_sel = 0; + // Disable software control USB D+ D- pullup pulldown (Device FS: dp_pullup = 1) + USB_SERIAL_JTAG.conf0.pad_pull_override = 0; + // Enable USB D+ pullup + USB_SERIAL_JTAG.conf0.dp_pullup = 1; + // Enable USB pad function + USB_SERIAL_JTAG.conf0.usb_pad_enable = 1; usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK); - usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET); + usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT + | USB_SERIAL_JTAG_INTR_BUS_RESET | USB_SERIAL_JTAG_INTR_SOF); if(!intr_handle && esp_intr_alloc(ETS_USB_SERIAL_JTAG_INTR_SOURCE, 0, hw_cdc_isr_handler, NULL, &intr_handle) != ESP_OK){ isr_log_e("HW USB CDC failed to init interrupts"); end(); return; } - usb_serial_jtag_ll_txfifo_flush(); } void HWCDC::end() { - //Disable tx/rx interrupt. + //Disable/clear/free tx/rx interrupt. usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK); + usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_LL_INTR_MASK); esp_intr_free(intr_handle); intr_handle = NULL; if(tx_lock != NULL) { @@ -211,13 +327,13 @@ void HWCDC::end() esp_event_loop_delete(arduino_hw_cdc_event_loop_handle); arduino_hw_cdc_event_loop_handle = NULL; } + deinit(); + setDebugOutput(false); + connected = false; } void HWCDC::setTxTimeoutMs(uint32_t timeout){ tx_timeout_ms = timeout; - // it registers that the user has explicitly requested to use a value as TX timeout - // used for the workaround with unplugged USB and TX Queue Full that causes a delay on every write() - tx_timeout_change_request = true; } /* @@ -260,35 +376,63 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size) if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ return 0; } - size_t max_size = xRingbufferGetMaxItemSize(tx_ring_buf); - size_t space = xRingbufferGetCurFreeSize(tx_ring_buf); - size_t to_send = size, so_far = 0; - - if(space > size){ - space = size; - } - // Non-Blocking method, Sending data to ringbuffer, and handle the data in ISR. - if(xRingbufferSend(tx_ring_buf, (void*) (buffer), space, 0) != pdTRUE){ - size = 0; + if(!isCDC_Connected()) { + // just pop/push RingBuffer and apply FIFO policy + flushTXBuffer(buffer, size); } else { - to_send -= space; - so_far += space; - // Now trigger the ISR to read data from the ring buffer. - usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + size_t space = xRingbufferGetCurFreeSize(tx_ring_buf); + size_t to_send = size, so_far = 0; - while(to_send){ - if(max_size > to_send){ - max_size = to_send; - } - // Blocking method, Sending data to ringbuffer, and handle the data in ISR. - if(xRingbufferSend(tx_ring_buf, (void*) (buffer+so_far), max_size, tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE){ - size = so_far; - break; - } - so_far += max_size; - to_send -= max_size; + if(space > size){ + space = size; + } + // Non-Blocking method, Sending data to ringbuffer, and handle the data in ISR. + if(space > 0 && xRingbufferSend(tx_ring_buf, (void*) (buffer), space, 0) != pdTRUE){ + size = 0; + } else { + to_send -= space; + so_far += space; // Now trigger the ISR to read data from the ring buffer. - usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + usb_serial_jtag_ll_txfifo_flush(); + if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + // tracks CDC trasmission progress to avoid hanging if CDC is unplugged while still sending data + size_t last_toSend = to_send; + uint32_t tries = tx_timeout_ms; // waits 1ms per sending data attempt, in case CDC is unplugged + while(connected && to_send){ + space = xRingbufferGetCurFreeSize(tx_ring_buf); + if(space > to_send){ + space = to_send; + } + // Blocking method, Sending data to ringbuffer, and handle the data in ISR. + if(xRingbufferSend(tx_ring_buf, (void*) (buffer+so_far), space, tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE){ + size = so_far; + log_w("write failed due to ring buffer full - timeout"); + break; + } + so_far += space; + to_send -= space; + // Now trigger the ISR to read data from the ring buffer. + usb_serial_jtag_ll_txfifo_flush(); + if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + if(last_toSend == to_send) { + // no progress in sending data... USB CDC is probably unplugged + tries--; + delay(1); + } else { + last_toSend = to_send; + tries = tx_timeout_ms; // reset the timeout + } + if (tries == 0) { // CDC isn't connected anymore... + size = so_far; + log_w("write failed due to waiting USB Host - timeout"); + connected = false; + } + } + } + // CDC was diconnected while sending data ==> flush the TX buffer keeping the last data + if(to_send && !usb_serial_jtag_ll_txfifo_writable()) { + connected = false; + flushTXBuffer(buffer + so_far, to_send); } } xSemaphoreGive(tx_lock); @@ -308,15 +452,28 @@ void HWCDC::flush(void) if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ return; } - UBaseType_t uxItemsWaiting = 0; - vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); - if(uxItemsWaiting){ - // Now trigger the ISR to read data from the ring buffer. - usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); - } - while(uxItemsWaiting){ - delay(5); + if(!isCDC_Connected()) { + flushTXBuffer(NULL, 0); + } else { + UBaseType_t uxItemsWaiting = 0; vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); + if(uxItemsWaiting){ + // Now trigger the ISR to read data from the ring buffer. + usb_serial_jtag_ll_txfifo_flush(); + if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + } + uint32_t tries = tx_timeout_ms; // waits 1ms per ISR sending data attempt, in case CDC is unplugged + while(connected && tries && uxItemsWaiting){ + delay(1); + UBaseType_t lastUxItemsWaiting = uxItemsWaiting; + vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); + if (lastUxItemsWaiting == uxItemsWaiting) tries--; + if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + } + if (tries == 0) { // CDC isn't connected anymore... + connected = false; + flushTXBuffer(NULL, 0); // flushes all TX Buffer + } } xSemaphoreGive(tx_lock); } @@ -407,4 +564,4 @@ HWCDC USBSerial; #endif #endif -#endif /* CONFIG_TINYUSB_CDC_ENABLED */ +#endif /* CONFIG_IDF_TARGET_ESP32C3 */ diff --git a/cores/esp32/HWCDC.h b/cores/esp32/HWCDC.h index 5878ad0377f..734e0cd5888 100644 --- a/cores/esp32/HWCDC.h +++ b/cores/esp32/HWCDC.h @@ -42,6 +42,10 @@ typedef union { class HWCDC: public Stream { +private: + static void deinit(); + static bool isCDC_Connected(); + public: HWCDC(); ~HWCDC(); @@ -64,6 +68,11 @@ class HWCDC: public Stream size_t write(const uint8_t *buffer, size_t size); void flush(void); + static bool isPlugged(void); + inline static bool isConnected(void) + { + return isCDC_Connected(); + } inline size_t read(char * buffer, size_t size) { return read((uint8_t*) buffer, size); diff --git a/libraries/ESP32/examples/HWSerial_Events/.skip.esp32 b/libraries/ESP32/examples/HWSerial_Events/.skip.esp32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/ESP32/examples/HWSerial_Events/.skip.esp32s2 b/libraries/ESP32/examples/HWSerial_Events/.skip.esp32s2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino b/libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino new file mode 100644 index 00000000000..5faa79f2cdc --- /dev/null +++ b/libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino @@ -0,0 +1,95 @@ +/* + * This Example demonstrates how to receive Hardware Serial Events + * This USB interface is available for the ESP32-S3 and ESP32-C3 + * + * It will log all events and USB status (plugged/unplugged) into UART0 + * Any data read from UART0 will be sent to the USB CDC + * Any data read from USB CDC will be sent to the UART0 + * + * A suggestion is to use Arduino Serial Monitor for the UART0 port + * and some other serial monitor application for the USB CDC port + * in order to see the exchanged data and the Hardware Serial Events + * + */ + +#ifndef ARDUINO_USB_MODE +#error This ESP32 SoC has no Native USB interface +#elif ARDUINO_USB_MODE == 0 +#warning This sketch should be used when USB is in Hardware CDC and JTAG mode +void setup(){} +void loop(){} +#else + +// Makes it work always using Serial0 as UART0 and USBSerial as the HW Serial USB CDC port +#if ARDUINO_USB_CDC_ON_BOOT +// HardwareSerial::Serial0 is declared but HWCDC::USBSerial not +// Serial is the HWCDC USB CDC port +#define USBSerial Serial +#else +// HWCDC::USBSerial is declared but HardwareSerial::Serial0 not +// Serial is HardwareSerial UART0 +#define Serial0 Serial // redefine the symbol Serial0 to the default Arduino +#endif + +// USB Event Callback Function that will log CDC events into UART0 +static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + if (event_base == ARDUINO_HW_CDC_EVENTS) { + switch (event_id) { + case ARDUINO_HW_CDC_CONNECTED_EVENT: + Serial0.println("CDC EVENT:: ARDUINO_HW_CDC_CONNECTED_EVENT"); + break; + case ARDUINO_HW_CDC_BUS_RESET_EVENT: + Serial0.println("CDC EVENT:: ARDUINO_HW_CDC_BUS_RESET_EVENT"); + break; + case ARDUINO_HW_CDC_RX_EVENT: + Serial0.println("\nCDC EVENT:: ARDUINO_HW_CDC_RX_EVENT"); + // sends all bytes read from USB Hardware Serial to UART0 + while (USBSerial.available()) Serial0.write(USBSerial.read()); + break; + case ARDUINO_HW_CDC_TX_EVENT: + Serial0.println("CDC EVENT:: ARDUINO_HW_CDC_TX_EVENT"); + break; + + default: + break; + } + } +} + +const char* _hwcdc_status[] = { + " USB Plugged but CDC is NOT connected\r\n", + " USB Plugged and CDC is connected\r\n", + " USB Unplugged and CDC is NOT connected\r\n", + " USB Unplugged BUT CDC is connected :: PROBLEM\r\n", +}; + +const char* HWCDC_Status() { + int i = USBSerial.isPlugged() ? 0 : 2; + if(USBSerial.isConnected()) i += 1; + return _hwcdc_status[i]; +} + +void setup() { + USBSerial.begin(); + USBSerial.onEvent(usbEventCallback); + + Serial0.begin(115200); + Serial0.setDebugOutput(true); + Serial0.println("Starting..."); +} + +void loop() { + static uint32_t counter = 0; + + Serial0.print(counter); + Serial0.print(HWCDC_Status()); + + if (USBSerial) { + USBSerial.printf(" [%ld] connected\n\r", counter); + } + // sends all bytes read from UART0 to USB Hardware Serial + while (Serial0.available()) USBSerial.write(Serial0.read()); + delay(1000); + counter++; +} +#endif From 4ba9517a5f13c636557292832e54af8c5067b014 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 8 Apr 2024 19:17:29 -0300 Subject: [PATCH 2/9] fix: statement order --- libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino b/libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino index 5faa79f2cdc..bb5f6507422 100644 --- a/libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino +++ b/libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino @@ -70,8 +70,8 @@ const char* HWCDC_Status() { } void setup() { - USBSerial.begin(); USBSerial.onEvent(usbEventCallback); + USBSerial.begin(); Serial0.begin(115200); Serial0.setDebugOutput(true); From fd43355c90d727fb4dfad019d59ff77c453bbd40 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 8 Apr 2024 19:23:28 -0300 Subject: [PATCH 3/9] feat: Apply suggestions from code review --- cores/esp32/HWCDC.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cores/esp32/HWCDC.cpp b/cores/esp32/HWCDC.cpp index 4b2b2fafb81..956095e6ccc 100644 --- a/cores/esp32/HWCDC.cpp +++ b/cores/esp32/HWCDC.cpp @@ -166,8 +166,6 @@ bool HWCDC::isCDC_Connected() usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); } // this will feed CDC TX FIFO to trigger IN_EMPTY - //uint8_t c = '\0'; - //usb_serial_jtag_ll_write_txfifo(&c, sizeof(c)); usb_serial_jtag_ll_txfifo_flush(); running = true; return false; @@ -460,15 +458,21 @@ void HWCDC::flush(void) if(uxItemsWaiting){ // Now trigger the ISR to read data from the ring buffer. usb_serial_jtag_ll_txfifo_flush(); - if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + if(connected) { + usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + } } uint32_t tries = tx_timeout_ms; // waits 1ms per ISR sending data attempt, in case CDC is unplugged while(connected && tries && uxItemsWaiting){ delay(1); UBaseType_t lastUxItemsWaiting = uxItemsWaiting; vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); - if (lastUxItemsWaiting == uxItemsWaiting) tries--; - if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + if (lastUxItemsWaiting == uxItemsWaiting) { + tries--; + } + if(connected) { + usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + } } if (tries == 0) { // CDC isn't connected anymore... connected = false; From cf5fe3fec55941a982e39c3f2d7249a35a70e251 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 8 Apr 2024 19:31:00 -0300 Subject: [PATCH 4/9] Create HWCDC_Events.ino --- .../examples/HWCDC_Events/HWCDC_Events.ino | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 libraries/ESP32/examples/HWCDC_Events/HWCDC_Events.ino diff --git a/libraries/ESP32/examples/HWCDC_Events/HWCDC_Events.ino b/libraries/ESP32/examples/HWCDC_Events/HWCDC_Events.ino new file mode 100644 index 00000000000..bb5f6507422 --- /dev/null +++ b/libraries/ESP32/examples/HWCDC_Events/HWCDC_Events.ino @@ -0,0 +1,95 @@ +/* + * This Example demonstrates how to receive Hardware Serial Events + * This USB interface is available for the ESP32-S3 and ESP32-C3 + * + * It will log all events and USB status (plugged/unplugged) into UART0 + * Any data read from UART0 will be sent to the USB CDC + * Any data read from USB CDC will be sent to the UART0 + * + * A suggestion is to use Arduino Serial Monitor for the UART0 port + * and some other serial monitor application for the USB CDC port + * in order to see the exchanged data and the Hardware Serial Events + * + */ + +#ifndef ARDUINO_USB_MODE +#error This ESP32 SoC has no Native USB interface +#elif ARDUINO_USB_MODE == 0 +#warning This sketch should be used when USB is in Hardware CDC and JTAG mode +void setup(){} +void loop(){} +#else + +// Makes it work always using Serial0 as UART0 and USBSerial as the HW Serial USB CDC port +#if ARDUINO_USB_CDC_ON_BOOT +// HardwareSerial::Serial0 is declared but HWCDC::USBSerial not +// Serial is the HWCDC USB CDC port +#define USBSerial Serial +#else +// HWCDC::USBSerial is declared but HardwareSerial::Serial0 not +// Serial is HardwareSerial UART0 +#define Serial0 Serial // redefine the symbol Serial0 to the default Arduino +#endif + +// USB Event Callback Function that will log CDC events into UART0 +static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + if (event_base == ARDUINO_HW_CDC_EVENTS) { + switch (event_id) { + case ARDUINO_HW_CDC_CONNECTED_EVENT: + Serial0.println("CDC EVENT:: ARDUINO_HW_CDC_CONNECTED_EVENT"); + break; + case ARDUINO_HW_CDC_BUS_RESET_EVENT: + Serial0.println("CDC EVENT:: ARDUINO_HW_CDC_BUS_RESET_EVENT"); + break; + case ARDUINO_HW_CDC_RX_EVENT: + Serial0.println("\nCDC EVENT:: ARDUINO_HW_CDC_RX_EVENT"); + // sends all bytes read from USB Hardware Serial to UART0 + while (USBSerial.available()) Serial0.write(USBSerial.read()); + break; + case ARDUINO_HW_CDC_TX_EVENT: + Serial0.println("CDC EVENT:: ARDUINO_HW_CDC_TX_EVENT"); + break; + + default: + break; + } + } +} + +const char* _hwcdc_status[] = { + " USB Plugged but CDC is NOT connected\r\n", + " USB Plugged and CDC is connected\r\n", + " USB Unplugged and CDC is NOT connected\r\n", + " USB Unplugged BUT CDC is connected :: PROBLEM\r\n", +}; + +const char* HWCDC_Status() { + int i = USBSerial.isPlugged() ? 0 : 2; + if(USBSerial.isConnected()) i += 1; + return _hwcdc_status[i]; +} + +void setup() { + USBSerial.onEvent(usbEventCallback); + USBSerial.begin(); + + Serial0.begin(115200); + Serial0.setDebugOutput(true); + Serial0.println("Starting..."); +} + +void loop() { + static uint32_t counter = 0; + + Serial0.print(counter); + Serial0.print(HWCDC_Status()); + + if (USBSerial) { + USBSerial.printf(" [%ld] connected\n\r", counter); + } + // sends all bytes read from UART0 to USB Hardware Serial + while (Serial0.available()) USBSerial.write(Serial0.read()); + delay(1000); + counter++; +} +#endif From a2cab56d7424debfcb2f193ffb6ed9113bbcbbad Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 8 Apr 2024 19:31:31 -0300 Subject: [PATCH 5/9] Create .skip.esp32 --- libraries/ESP32/examples/HWCDC_Events/.skip.esp32 | 1 + 1 file changed, 1 insertion(+) create mode 100644 libraries/ESP32/examples/HWCDC_Events/.skip.esp32 diff --git a/libraries/ESP32/examples/HWCDC_Events/.skip.esp32 b/libraries/ESP32/examples/HWCDC_Events/.skip.esp32 new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/libraries/ESP32/examples/HWCDC_Events/.skip.esp32 @@ -0,0 +1 @@ + From d9edd761d46e428a6c343604c9b6d971e63e62dd Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 8 Apr 2024 19:31:45 -0300 Subject: [PATCH 6/9] Create .skip.esp32s2 --- libraries/ESP32/examples/HWCDC_Events/.skip.esp32s2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 libraries/ESP32/examples/HWCDC_Events/.skip.esp32s2 diff --git a/libraries/ESP32/examples/HWCDC_Events/.skip.esp32s2 b/libraries/ESP32/examples/HWCDC_Events/.skip.esp32s2 new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/libraries/ESP32/examples/HWCDC_Events/.skip.esp32s2 @@ -0,0 +1 @@ + From 1de2b152f1eb85b9016fe96b0e2f3abcc8b5926e Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 8 Apr 2024 19:32:37 -0300 Subject: [PATCH 7/9] Delete libraries/ESP32/examples/HWSerial_Events/.skip.esp32s2 --- libraries/ESP32/examples/HWSerial_Events/.skip.esp32s2 | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 libraries/ESP32/examples/HWSerial_Events/.skip.esp32s2 diff --git a/libraries/ESP32/examples/HWSerial_Events/.skip.esp32s2 b/libraries/ESP32/examples/HWSerial_Events/.skip.esp32s2 deleted file mode 100644 index e69de29bb2d..00000000000 From dea1317f7de6678a018efb10a56c8b92e71fc002 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 8 Apr 2024 19:33:02 -0300 Subject: [PATCH 8/9] Delete libraries/ESP32/examples/HWSerial_Events/.skip.esp32 --- libraries/ESP32/examples/HWSerial_Events/.skip.esp32 | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 libraries/ESP32/examples/HWSerial_Events/.skip.esp32 diff --git a/libraries/ESP32/examples/HWSerial_Events/.skip.esp32 b/libraries/ESP32/examples/HWSerial_Events/.skip.esp32 deleted file mode 100644 index e69de29bb2d..00000000000 From b3a81c3e05d80b088e9f766a8258c1572a6e9364 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 8 Apr 2024 19:33:22 -0300 Subject: [PATCH 9/9] Delete libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino --- .../HWSerial_Events/HWSerial_Events.ino | 95 ------------------- 1 file changed, 95 deletions(-) delete mode 100644 libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino diff --git a/libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino b/libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino deleted file mode 100644 index bb5f6507422..00000000000 --- a/libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This Example demonstrates how to receive Hardware Serial Events - * This USB interface is available for the ESP32-S3 and ESP32-C3 - * - * It will log all events and USB status (plugged/unplugged) into UART0 - * Any data read from UART0 will be sent to the USB CDC - * Any data read from USB CDC will be sent to the UART0 - * - * A suggestion is to use Arduino Serial Monitor for the UART0 port - * and some other serial monitor application for the USB CDC port - * in order to see the exchanged data and the Hardware Serial Events - * - */ - -#ifndef ARDUINO_USB_MODE -#error This ESP32 SoC has no Native USB interface -#elif ARDUINO_USB_MODE == 0 -#warning This sketch should be used when USB is in Hardware CDC and JTAG mode -void setup(){} -void loop(){} -#else - -// Makes it work always using Serial0 as UART0 and USBSerial as the HW Serial USB CDC port -#if ARDUINO_USB_CDC_ON_BOOT -// HardwareSerial::Serial0 is declared but HWCDC::USBSerial not -// Serial is the HWCDC USB CDC port -#define USBSerial Serial -#else -// HWCDC::USBSerial is declared but HardwareSerial::Serial0 not -// Serial is HardwareSerial UART0 -#define Serial0 Serial // redefine the symbol Serial0 to the default Arduino -#endif - -// USB Event Callback Function that will log CDC events into UART0 -static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { - if (event_base == ARDUINO_HW_CDC_EVENTS) { - switch (event_id) { - case ARDUINO_HW_CDC_CONNECTED_EVENT: - Serial0.println("CDC EVENT:: ARDUINO_HW_CDC_CONNECTED_EVENT"); - break; - case ARDUINO_HW_CDC_BUS_RESET_EVENT: - Serial0.println("CDC EVENT:: ARDUINO_HW_CDC_BUS_RESET_EVENT"); - break; - case ARDUINO_HW_CDC_RX_EVENT: - Serial0.println("\nCDC EVENT:: ARDUINO_HW_CDC_RX_EVENT"); - // sends all bytes read from USB Hardware Serial to UART0 - while (USBSerial.available()) Serial0.write(USBSerial.read()); - break; - case ARDUINO_HW_CDC_TX_EVENT: - Serial0.println("CDC EVENT:: ARDUINO_HW_CDC_TX_EVENT"); - break; - - default: - break; - } - } -} - -const char* _hwcdc_status[] = { - " USB Plugged but CDC is NOT connected\r\n", - " USB Plugged and CDC is connected\r\n", - " USB Unplugged and CDC is NOT connected\r\n", - " USB Unplugged BUT CDC is connected :: PROBLEM\r\n", -}; - -const char* HWCDC_Status() { - int i = USBSerial.isPlugged() ? 0 : 2; - if(USBSerial.isConnected()) i += 1; - return _hwcdc_status[i]; -} - -void setup() { - USBSerial.onEvent(usbEventCallback); - USBSerial.begin(); - - Serial0.begin(115200); - Serial0.setDebugOutput(true); - Serial0.println("Starting..."); -} - -void loop() { - static uint32_t counter = 0; - - Serial0.print(counter); - Serial0.print(HWCDC_Status()); - - if (USBSerial) { - USBSerial.printf(" [%ld] connected\n\r", counter); - } - // sends all bytes read from UART0 to USB Hardware Serial - while (Serial0.available()) USBSerial.write(Serial0.read()); - delay(1000); - counter++; -} -#endif