#include "Arduino.h" #include #include #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "usb/cdc_acm_host.h" #include "usb/vcp_ch34x.hpp" #include "usb/vcp_cp210x.hpp" #include "usb/vcp_ftdi.hpp" #include "usb/vcp.hpp" #include "usb/usb_host.h" using namespace esp_usb; // Change these values to match your needs #define EXAMPLE_BAUDRATE (115200) #define EXAMPLE_STOP_BITS (0) // 0: 1 stopbit, 1: 1.5 stopbits, 2: 2 stopbits #define EXAMPLE_PARITY (0) // 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space #define EXAMPLE_DATA_BITS (8) namespace { static const char *TAG = "VCP example"; static SemaphoreHandle_t device_disconnected_sem; /** * @brief Data received callback * * Just pass received data to stdout * * @param[in] data Pointer to received data * @param[in] data_len Length of received data in bytes * @param[in] arg Argument we passed to the device open function * @return * true: We have processed the received data * false: We expect more data */ static bool handle_rx(const uint8_t *data, size_t data_len, void *arg) { printf("%.*s", data_len, data); return true; } /** * @brief Device event callback * * Apart from handling device disconnection it doesn't do anything useful * * @param[in] event Device event type and data * @param[in] user_ctx Argument we passed to the device open function */ static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) { switch (event->type) { case CDC_ACM_HOST_ERROR: ESP_LOGE(TAG, "CDC-ACM error has occurred, err_no = %d", event->data.error); break; case CDC_ACM_HOST_DEVICE_DISCONNECTED: ESP_LOGI(TAG, "Device suddenly disconnected"); xSemaphoreGive(device_disconnected_sem); break; case CDC_ACM_HOST_SERIAL_STATE: ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val); break; case CDC_ACM_HOST_NETWORK_CONNECTION: default: break; } } /** * @brief USB Host library handling task * * @param arg Unused */ static void usb_lib_task(void *arg) { while (1) { // Start handling system events uint32_t event_flags; usb_host_lib_handle_events(portMAX_DELAY, &event_flags); if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { ESP_ERROR_CHECK(usb_host_device_free_all()); } if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { ESP_LOGI(TAG, "USB: All devices freed"); // Continue handling USB events to allow device reconnection } } } } void setup() { device_disconnected_sem = xSemaphoreCreateBinary(); assert(device_disconnected_sem); // Install USB Host driver. Should only be called once in entire application ESP_LOGI(TAG, "Installing USB Host"); const usb_host_config_t host_config = { .skip_phy_setup = false, .intr_flags = ESP_INTR_FLAG_LEVEL1, }; ESP_ERROR_CHECK(usb_host_install(&host_config)); // Create a task that will handle USB library events BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, NULL); assert(task_created == pdTRUE); ESP_LOGI(TAG, "Installing CDC-ACM driver"); ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); // Register VCP drivers to VCP service VCP::register_driver(); VCP::register_driver(); VCP::register_driver(); } void loop() { const cdc_acm_host_device_config_t dev_config = { .connection_timeout_ms = 5000, // 5 seconds, enough time to plug the device in or experiment with timeout .out_buffer_size = 512, .in_buffer_size = 512, .event_cb = handle_event, .data_cb = handle_rx, .user_arg = NULL, }; // You don't need to know the device's VID and PID. Just plug in any device and the VCP service will load correct (already registered) driver for the device ESP_LOGI(TAG, "Opening any VCP device..."); auto vcp = std::unique_ptr(VCP::open(&dev_config)); if (vcp == nullptr) { ESP_LOGI(TAG, "Failed to open VCP device"); // continue; } vTaskDelay(10); ESP_LOGI(TAG, "Setting up line coding"); cdc_acm_line_coding_t line_coding = { .dwDTERate = EXAMPLE_BAUDRATE, .bCharFormat = EXAMPLE_STOP_BITS, .bParityType = EXAMPLE_PARITY, .bDataBits = EXAMPLE_DATA_BITS, }; ESP_ERROR_CHECK(vcp->line_coding_set(&line_coding)); /* Now the USB-to-UART converter is configured and receiving data. You can use standard CDC-ACM API to interact with it. E.g. ESP_ERROR_CHECK(vcp->set_control_line_state(false, true)); ESP_ERROR_CHECK(vcp->tx_blocking((uint8_t *)"Test string", 12)); */ // Send some dummy data ESP_LOGI(TAG, "Sending data through CdcAcmDevice"); uint8_t data[] = "test_string"; ESP_ERROR_CHECK(vcp->tx_blocking(data, sizeof(data))); ESP_ERROR_CHECK(vcp->set_control_line_state(true, true)); // We are done. Wait for device disconnection and start over ESP_LOGI(TAG, "Done. You can reconnect the VCP device to run again."); xSemaphoreTake(device_disconnected_sem, portMAX_DELAY); }