diff --git a/.github/workflows/build_arduinoide.yaml b/.github/workflows/build_arduinoide.yaml index 33c20f1..72e9de8 100644 --- a/.github/workflows/build_arduinoide.yaml +++ b/.github/workflows/build_arduinoide.yaml @@ -25,6 +25,8 @@ jobs: source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json sketch-paths: | - examples/loopback + - examples/native_host + - examples/native_peripheral libraries: | - name: USBHostSerial source-path: ./ \ No newline at end of file diff --git a/.github/workflows/build_platformio.yaml b/.github/workflows/build_platformio.yaml index ba0179b..01d94ff 100644 --- a/.github/workflows/build_platformio.yaml +++ b/.github/workflows/build_platformio.yaml @@ -9,9 +9,10 @@ jobs: container: ghcr.io/bertmelis/pio-test-container strategy: matrix: - example: [ - examples/loopback/loopback.ino - ] + example: + - examples/loopback/loopback.ino + - examples/native_host/native_host.ino + - examples/native_peripheral/native_peripheral.ino steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 diff --git a/examples/native_host/native_host.ino b/examples/native_host/native_host.ino new file mode 100644 index 0000000..b6f9634 --- /dev/null +++ b/examples/native_host/native_host.ino @@ -0,0 +1,48 @@ +/** + * @file native_host.ino + * @brief Example sketch for USBHostSerial library demonstrating native USB host functionality. + * @date 4/1/26 + * @author Aldem Pido + * + * Tested with 2ESP32 S3 Wroom 2 Boards with UART and USB ports. + * + * Host Board + * USB -> Peripheral USB + * UART -> Computer Serial Monitor + * + * Peripheral Board + * USB -> Peripheral USB + * UART -> Computer Serial Monitor + */ +#define USBHOSTSERIAL_BUFFERSIZE 512 +#include +#include + +USBHostSerial hostSerial; + +// Logger function for USBHostSerial +void usbHostLogger(const char* msg) { + Serial.print("[USBHostSerial] "); + Serial.println(msg); +} + +void setup() { + Serial.begin(115200); + delay(1000); + + hostSerial.begin(115200, 0, 0, 8); // baudrate, stopbits, parity, databits + hostSerial.setLogger(usbHostLogger); // Enable logging +} + +void loop() { + // Host to Serial + if (hostSerial.available()) { + Serial.write(hostSerial.read()); + Serial.flush(); + } + + // Serial to Host + if (Serial.available()) { + hostSerial.write(Serial.read()); + } +} \ No newline at end of file diff --git a/examples/native_peripheral/native_peripheral.ino b/examples/native_peripheral/native_peripheral.ino new file mode 100644 index 0000000..adfb7a5 --- /dev/null +++ b/examples/native_peripheral/native_peripheral.ino @@ -0,0 +1,32 @@ +/** + * @file native_peripheral.ino + * @brief Flushes data between Native USB Serial and UART Serial + * @date 4/1/26 + * @author Aldem Pido + */ + +#include "USB.h" +#include "USBCDC.h" + +USBCDC USBSerial; + +void setup() { + Serial.begin(115200); + USBSerial.begin(); + USB.begin(); + delay(1000); +} + +void loop() { + // USB to Serial + if (USBSerial.available()) { + Serial.write(USBSerial.read()); + Serial.flush(); + } + + // Serial to USB + if (Serial.available()) { + USBSerial.write(Serial.read()); + USBSerial.flush(); + } +} \ No newline at end of file diff --git a/src/USBHostSerial.cpp b/src/USBHostSerial.cpp index e04ea2f..5716d2f 100644 --- a/src/USBHostSerial.cpp +++ b/src/USBHostSerial.cpp @@ -177,6 +177,7 @@ bool USBHostSerial::_handle_rx(const uint8_t *data, size_t data_len, void *arg) void USBHostSerial::_handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) { if (event->type == CDC_ACM_HOST_DEVICE_DISCONNECTED) { + static_cast(user_ctx)->_log("USB device disconnected event"); xSemaphoreGive(static_cast(user_ctx)->_device_disconnected_sem); } } @@ -207,9 +208,11 @@ void USBHostSerial::_USBHostSerial_task(void *arg) { cdc_acm_dev_hdl_t cdc_dev = NULL; auto vcp = std::unique_ptr(VCP::open(&dev_config)); if (vcp == nullptr) { + thisInstance->_log("USB VCP open returned null, falling back to CDC"); // try to fallback to CDC err = cdc_acm_host_open(thisInstance->_vid, thisInstance->_pid, 0, &dev_config, &cdc_dev); if (err != ESP_OK) { + vTaskDelay(pdMS_TO_TICKS(500)); continue; } thisInstance->_fallback = true; @@ -219,21 +222,58 @@ void USBHostSerial::_USBHostSerial_task(void *arg) { thisInstance->_log("USB VCP device opened"); } - // mark connected - xSemaphoreTake(thisInstance->_device_disconnected_sem, portMAX_DELAY); + // Clear any stale disconnect token from a previous cycle. + while (xSemaphoreTake(thisInstance->_device_disconnected_sem, 0) == pdTRUE) { + } - // set line coding + // set line coding and control line states err = ESP_OK; if (thisInstance->_fallback) { - err = cdc_acm_host_line_coding_get(cdc_dev, &(thisInstance->_line_coding)); + err = cdc_acm_host_line_coding_set(cdc_dev, &(thisInstance->_line_coding)); + if (err != ESP_OK) { + thisInstance->_log("USB line coding set error"); + if (cdc_dev != NULL) { + cdc_acm_host_close(cdc_dev); + cdc_dev = NULL; + } + vTaskDelay(pdMS_TO_TICKS(500)); + continue; + } + thisInstance->_log("USB line coding set"); + + err = cdc_acm_host_set_control_line_state(cdc_dev, true, true); + if (err == ESP_ERR_NOT_SUPPORTED) { + thisInstance->_log("USB control line state not supported"); + } else if (err != ESP_OK) { + thisInstance->_log("USB control line state set error"); + if (cdc_dev != NULL) { + cdc_acm_host_close(cdc_dev); + cdc_dev = NULL; + } + vTaskDelay(pdMS_TO_TICKS(500)); + continue; + } else { + thisInstance->_log("USB control line state set"); + } } else { err = vcp->line_coding_set(&(thisInstance->_line_coding)); - } - if (err == ESP_OK) { + if (err != ESP_OK) { + thisInstance->_log("USB line coding set error"); + vTaskDelay(pdMS_TO_TICKS(500)); + continue; + } thisInstance->_log("USB line coding set"); - } else { - thisInstance->_log("USB line coding error"); - continue; + + err = vcp->set_control_line_state(true, true); + if (err == ESP_ERR_NOT_SUPPORTED) { + thisInstance->_log("USB control line state not supported"); + } else if (err != ESP_OK) { + thisInstance->_log("USB control line state set error"); + vTaskDelay(pdMS_TO_TICKS(500)); + continue; + } else { + thisInstance->_log("USB control line state set"); + } } // all set, enter loop to start sending @@ -255,11 +295,23 @@ void USBHostSerial::_USBHostSerial_task(void *arg) { if (err == ESP_OK) { vRingbufferReturnItem(thisInstance->_tx_buf_handle, data); } else { + vRingbufferReturnItem(thisInstance->_tx_buf_handle, data); thisInstance->_log("Error writing to USB"); + // Device likely reset/disconnected; break to cleanup/reopen path. + if (err == ESP_ERR_INVALID_STATE || err == ESP_ERR_INVALID_RESPONSE || err == ESP_FAIL) { + break; + } } } taskYIELD(); } + + // Ensure CDC handles are released before attempting to reopen. + if (thisInstance->_fallback && cdc_dev != NULL) { + cdc_acm_host_close(cdc_dev); + cdc_dev = NULL; + } + vTaskDelay(pdMS_TO_TICKS(500)); } }