172 lines
5.4 KiB
C++
172 lines
5.4 KiB
C++
#include "Arduino.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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<FT23x>();
|
|
VCP::register_driver<CP210x>();
|
|
VCP::register_driver<CH34x>();
|
|
}
|
|
|
|
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<CdcAcmDevice>(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);
|
|
}
|