Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build_arduinoide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: ./
7 changes: 4 additions & 3 deletions .github/workflows/build_platformio.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions examples/native_host/native_host.ino
Original file line number Diff line number Diff line change
@@ -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 <Arduino.h>
#include <USBHostSerial.h>

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());
}
}
32 changes: 32 additions & 0 deletions examples/native_peripheral/native_peripheral.ino
Original file line number Diff line number Diff line change
@@ -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();
}
}
70 changes: 61 additions & 9 deletions src/USBHostSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<USBHostSerial*>(user_ctx)->_log("USB device disconnected event");
xSemaphoreGive(static_cast<USBHostSerial*>(user_ctx)->_device_disconnected_sem);
}
}
Expand Down Expand Up @@ -207,9 +208,11 @@ void USBHostSerial::_USBHostSerial_task(void *arg) {
cdc_acm_dev_hdl_t cdc_dev = NULL;
auto vcp = std::unique_ptr<CdcAcmDevice>(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;
Expand All @@ -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
Expand All @@ -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));
}
}

Expand Down
Loading