Unverified Commit 4465cacf authored by Rodrigo Garcia's avatar Rodrigo Garcia Committed by GitHub

backports HWCDC to v2.0.15 (#9462)

* feat: backports HWCDC

* fix: statement order

* feat: Apply suggestions from code review

* Create HWCDC_Events.ino

* Create .skip.esp32

* Create .skip.esp32s2

* Delete libraries/ESP32/examples/HWSerial_Events/.skip.esp32s2

* Delete libraries/ESP32/examples/HWSerial_Events/.skip.esp32

* Delete libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino
parent 94746abe
// 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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with 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; ...@@ -30,12 +30,14 @@ static RingbufHandle_t tx_ring_buf = NULL;
static xQueueHandle rx_queue = NULL; static xQueueHandle rx_queue = NULL;
static uint8_t rx_data_buf[64] = {0}; static uint8_t rx_data_buf[64] = {0};
static intr_handle_t intr_handle = NULL; static intr_handle_t intr_handle = NULL;
static volatile bool initial_empty = false; static volatile bool connected = false;
static xSemaphoreHandle tx_lock = NULL; static xSemaphoreHandle tx_lock = NULL;
// workaround for when USB CDC is not connected static volatile unsigned long lastSOF_ms;
static uint32_t tx_timeout_ms = 0; static volatile uint8_t SOF_TIMEOUT;
static bool tx_timeout_change_request = false;
// 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; 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) { ...@@ -73,21 +75,17 @@ static void hw_cdc_isr_handler(void *arg) {
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) { if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) {
// Interrupt tells us the host picked up the data we sent. // 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. // 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); 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; size_t queued_size;
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR(tx_ring_buf, &queued_size, 64); 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. // If the hardware fifo is avaliable, write in it. Otherwise, do nothing.
...@@ -97,7 +95,7 @@ static void hw_cdc_isr_handler(void *arg) { ...@@ -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_write_txfifo(queued_buff, queued_size);
usb_serial_jtag_ll_txfifo_flush(); usb_serial_jtag_ll_txfifo_flush();
vRingbufferReturnItemFromISR(tx_ring_buf, queued_buff, &xTaskWoken); 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? //send event?
//ets_printf("TX:%u\n", queued_size); //ets_printf("TX:%u\n", queued_size);
event.tx.len = queued_size; event.tx.len = queued_size;
...@@ -119,18 +117,20 @@ static void hw_cdc_isr_handler(void *arg) { ...@@ -119,18 +117,20 @@ static void hw_cdc_isr_handler(void *arg) {
break; break;
} }
} }
//send event?
//ets_printf("RX:%u/%u\n", i, rx_fifo_len);
event.rx.len = i; 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); 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) { if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
usb_serial_jtag_ll_clr_intsts_mask(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); 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) { if (xTaskWoken == pdTRUE) {
...@@ -138,26 +138,115 @@ static void hw_cdc_isr_handler(void *arg) { ...@@ -138,26 +138,115 @@ 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
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) { 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()){ if(xPortInIsrContext()){
xRingbufferSendFromISR(tx_ring_buf, (void*) (&c), 1, NULL); xRingbufferSendFromISR(tx_ring_buf, (void*) (&c), 1, NULL);
} else { } else {
xRingbufferSend(tx_ring_buf, (void*) (&c), 1, tx_timeout_ms / portTICK_PERIOD_MS); 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() { HWCDC::HWCDC() {
lastSOF_ms = 0;
SOF_TIMEOUT = 5;
} }
HWCDC::~HWCDC(){ HWCDC::~HWCDC(){
end(); end();
} }
// It should return <true> just when USB is plugged and CDC is connected.
HWCDC::operator bool() const HWCDC::operator bool() const
{ {
return initial_empty; return HWCDC::isCDC_Connected();
} }
void HWCDC::onEvent(esp_event_handler_t callback){ void HWCDC::onEvent(esp_event_handler_t callback){
...@@ -168,6 +257,16 @@ void HWCDC::onEvent(arduino_hw_cdc_event_t event, esp_event_handler_t callback){ ...@@ -168,6 +257,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); 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) void HWCDC::begin(unsigned long baud)
{ {
if(tx_lock == NULL) { if(tx_lock == NULL) {
...@@ -185,20 +284,35 @@ void HWCDC::begin(unsigned long baud) ...@@ -185,20 +284,35 @@ void HWCDC::begin(unsigned long baud)
log_e("HW CDC TX Buffer error"); 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_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){ 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"); isr_log_e("HW USB CDC failed to init interrupts");
end(); end();
return; return;
} }
usb_serial_jtag_ll_txfifo_flush();
} }
void HWCDC::end() 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_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); esp_intr_free(intr_handle);
intr_handle = NULL; intr_handle = NULL;
if(tx_lock != NULL) { if(tx_lock != NULL) {
...@@ -211,13 +325,13 @@ void HWCDC::end() ...@@ -211,13 +325,13 @@ void HWCDC::end()
esp_event_loop_delete(arduino_hw_cdc_event_loop_handle); esp_event_loop_delete(arduino_hw_cdc_event_loop_handle);
arduino_hw_cdc_event_loop_handle = NULL; arduino_hw_cdc_event_loop_handle = NULL;
} }
deinit();
setDebugOutput(false);
connected = false;
} }
void HWCDC::setTxTimeoutMs(uint32_t timeout){ void HWCDC::setTxTimeoutMs(uint32_t timeout){
tx_timeout_ms = 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 +374,63 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size) ...@@ -260,35 +374,63 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size)
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
return 0; return 0;
} }
size_t max_size = xRingbufferGetMaxItemSize(tx_ring_buf); if(!isCDC_Connected()) {
size_t space = xRingbufferGetCurFreeSize(tx_ring_buf); // just pop/push RingBuffer and apply FIFO policy
size_t to_send = size, so_far = 0; flushTXBuffer(buffer, size);
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;
} else { } else {
to_send -= space; size_t space = xRingbufferGetCurFreeSize(tx_ring_buf);
so_far += space; size_t to_send = size, so_far = 0;
// 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(to_send){ if(space > size){
if(max_size > to_send){ space = size;
max_size = to_send; }
} // Non-Blocking method, Sending data to ringbuffer, and handle the data in ISR.
// Blocking method, Sending data to ringbuffer, and handle the data in ISR. if(space > 0 && xRingbufferSend(tx_ring_buf, (void*) (buffer), space, 0) != pdTRUE){
if(xRingbufferSend(tx_ring_buf, (void*) (buffer+so_far), max_size, tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE){ size = 0;
size = so_far; } else {
break; to_send -= space;
} so_far += space;
so_far += max_size;
to_send -= max_size;
// Now trigger the ISR to read data from the ring buffer. // 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); xSemaphoreGive(tx_lock);
...@@ -308,15 +450,34 @@ void HWCDC::flush(void) ...@@ -308,15 +450,34 @@ void HWCDC::flush(void)
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
return; return;
} }
UBaseType_t uxItemsWaiting = 0; if(!isCDC_Connected()) {
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); flushTXBuffer(NULL, 0);
if(uxItemsWaiting){ } else {
// Now trigger the ISR to read data from the ring buffer. UBaseType_t uxItemsWaiting = 0;
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
while(uxItemsWaiting){
delay(5);
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); 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); xSemaphoreGive(tx_lock);
} }
...@@ -407,4 +568,4 @@ HWCDC USBSerial; ...@@ -407,4 +568,4 @@ HWCDC USBSerial;
#endif #endif
#endif #endif
#endif /* CONFIG_TINYUSB_CDC_ENABLED */ #endif /* CONFIG_IDF_TARGET_ESP32C3 */
...@@ -42,6 +42,10 @@ typedef union { ...@@ -42,6 +42,10 @@ typedef union {
class HWCDC: public Stream class HWCDC: public Stream
{ {
private:
static void deinit();
static bool isCDC_Connected();
public: public:
HWCDC(); HWCDC();
~HWCDC(); ~HWCDC();
...@@ -64,6 +68,11 @@ public: ...@@ -64,6 +68,11 @@ public:
size_t write(const uint8_t *buffer, size_t size); size_t write(const uint8_t *buffer, size_t size);
void flush(void); void flush(void);
static bool isPlugged(void);
inline static bool isConnected(void)
{
return isCDC_Connected();
}
inline size_t read(char * buffer, size_t size) inline size_t read(char * buffer, size_t size)
{ {
return read((uint8_t*) buffer, size); return read((uint8_t*) buffer, size);
......
/*
* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment