From 2c6d0bae90c8c9dfb5d327d8d86e6d4d11cd2807 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Sun, 19 Mar 2023 12:27:51 +0100 Subject: [PATCH] usb: Update host CDC examples --- .../en/api-reference/peripherals/usb_host.rst | 4 +- .../usb/host/cdc/cdc_acm_bg96/CMakeLists.txt | 8 - .../usb/host/cdc/cdc_acm_bg96/README.md | 99 --------- .../host/cdc/cdc_acm_bg96/main/CMakeLists.txt | 2 - .../host/cdc/cdc_acm_bg96/main/bg96_usb.hpp | 84 ------- .../cdc_acm_bg96/main/cdc_acm_host_bg96.cpp | 208 ------------------ .../cdc/cdc_acm_bg96/main/idf_component.yml | 5 - .../usb/host/cdc/cdc_acm_host/README.md | 9 - .../host/cdc/cdc_acm_host/main/CMakeLists.txt | 3 +- .../cdc/cdc_acm_host/main/idf_component.yml | 2 +- .../usb/host/cdc/cdc_acm_host/main/usb-cdc.c | 113 ---------- .../cdc_acm_host/main/usb_cdc_example_main.c | 179 +++++++++++++++ .../usb/host/cdc/cdc_acm_vcp/README.md | 6 +- .../main/cdc_acm_vcp_example_main.cpp | 34 ++- .../cdc/cdc_acm_vcp/main/idf_component.yml | 6 +- 15 files changed, 212 insertions(+), 550 deletions(-) delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c create mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c diff --git a/docs/en/api-reference/peripherals/usb_host.rst b/docs/en/api-reference/peripherals/usb_host.rst index 6ebce1a55d6..f3966550e29 100644 --- a/docs/en/api-reference/peripherals/usb_host.rst +++ b/docs/en/api-reference/peripherals/usb_host.rst @@ -363,8 +363,8 @@ CDC-ACM * A host class driver for the Communication Device Class (Abstract Control Model) is deployed to `IDF component registry `__. * The :example:`peripherals/usb/host/cdc/cdc_acm_host` example uses the CDC-ACM host driver component to communicate with CDC-ACM devices -* The :example:`peripherals/usb/host/cdc/cdc_acm_bg96` example uses the CDC-ACM host driver component to communicate with non-compliant CDC-ACM devices (i.e., vendor-specific classes that support a subset of CDC-ACM features) such as the Quectel BG96 modem. * The :example:`peripherals/usb/host/cdc/cdc_acm_vcp` example shows how can you extend the CDC-ACM host driver to interface Virtual COM Port devices. +* The CDC-ACM driver is also used in `esp_modem examples `__, where it is used for communication with cellular modems. MSC """ @@ -405,4 +405,4 @@ Maintainers Notes :hidden: :maxdepth: 0 - usb_host/usb_host_notes_index \ No newline at end of file + usb_host/usb_host_notes_index diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt deleted file mode 100644 index 726b6642357..00000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# For more information about build system see -# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html -# The following five lines of boilerplate have to be in your project's -# CMakeLists in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.16) - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(cdc_acm_host_bg96) diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md deleted file mode 100644 index 58d7c22d793..00000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md +++ /dev/null @@ -1,99 +0,0 @@ -| Supported Targets | ESP32-S2 | ESP32-S3 | -| ----------------- | -------- | -------- | - -# USB CDC-ACM Host Driver BG96 Example - -(See the README.md file in the upper level 'examples' directory for more information about examples.) - -This example shows how to set up ESP chip to interface with CDC-like device by using the CDC-ACM Host Driver. CDC-like devices implement a Vendor-specific class, and support a subset of the functions of a fully compliant CDC-ACM device. - -## How to use example - -### Hardware Required - -Any ESP board with USB-OTG supported and a Quectel BG96 LTE/GPS modem. - -Connect USB_D+, USB_D-, GND and +5V signals of ESP board to BG96. - -_Note:_ Quectel BG96 modem must be started after power-up by applying low pulse on PWRKEY (pin 15). - -#### Pin Assignment - -See common pin assignments for USB Device examples from [upper level](../../../README.md#common-pin-assignments). - -### Build and Flash - -Build the project and flash it to the board, then run monitor tool to view serial output: - -```bash -idf.py -p PORT flash monitor -``` - -(Replace PORT with the name of the serial port to use.) - -(To exit the serial monitor, type ``Ctrl-]``.) - -See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. - -## Example Output - -After the flashing you should see the output at idf monitor: - -``` -I (276) BG96: USB Host installed -I (24446) AT: ATE0 -I (24446) AT: -OK - -I (24526) AT: -+QIND: SMS DONE - -I (24646) AT: -APP RDY - -I (25446) BG96: Sending AT -I (25446) AT: -OK - -I (26446) BG96: Enabling GNSS -I (26446) AT: -OK - -GPVTG Sentence: - Track [deg]: 0.00 - Speed [kmph]: 0.00 - Speed [knots]: 0.00 -GPGSA Sentence: - Mode: A - Fix: 1 - PDOP: 0.00 - HDOP: 0.00 - VDOP: 0.00 -GPGGA sentence -Number of satellites: 0 -Altitude: 0.000000 -GPRMC sentence -Longitude: - Degrees: 0 - Minutes: 0.000000 - Cardinal: -Latitude: - Degrees: 0 - Minutes: 0.000000 - Cardinal: -Date & Time: 00 Jan 00:00:00 1900 -Speed, in Knots: 0.000000 -Track, in degrees: 0.000000 -Magnetic Variation: - Degrees: 0.000000 - Cardinal: -Invalid Magnetic Variation Direction! -Adjusted Track (heading): 0.000000 -I (27446) BG96: Sending AT+GSN -I (27446) AT: -860517045660414 - -OK -... - -``` diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt deleted file mode 100644 index 8027d6161d0..00000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "cdc_acm_host_bg96.cpp" - INCLUDE_DIRS ".") diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp deleted file mode 100644 index dc3cefa96c8..00000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#pragma once - -#include "usb/cdc_acm_host.h" -#include "esp_log.h" - -#define BG96_VID (0x2C7C) -#define BG96_PID (0x0296) -#define BG96_AT_INTERFACE (2) -#define BG96_NMEA_INTERFACE (1) - -class Bg96Usb { -public: - - explicit Bg96Usb() : at_opened(false) { - }; - - esp_err_t at_start(cdc_acm_data_callback_t data_cb, void *user_arg) - { - // This driver doesn't support CDC notifications. This can lead to silent failures - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 10000, - .out_buffer_size = 64, - .event_cb = NULL, - .data_cb = data_cb, - .user_arg = user_arg, - }; - ESP_ERROR_CHECK(this->at_port.open_vendor_specific(BG96_VID, BG96_PID, BG96_AT_INTERFACE, &dev_config)); - this->at_opened = true; - - // Some FW versions have Echo enabled by default. Disable it with ATE0 command - ESP_LOGD("BG96_USB", "Turning off echo with ATE0"); - ESP_ERROR_CHECK(this->at_port.tx_blocking((uint8_t *)"ATE0\r", 5, 1000)); - vTaskDelay(100); - return ESP_OK; - } - - void at_stop() - { - this->at_port.close(); - this->at_opened = false; - } - - esp_err_t at_write(uint8_t *data, size_t len) - { - ESP_LOG_BUFFER_HEXDUMP("BG96_USB", data, len, ESP_LOG_DEBUG); - return this->at_port.tx_blocking(data, len, 1000); - } - - esp_err_t gnss_start(cdc_acm_data_callback_t data_cb) - { - if (!this->at_opened) { - return ESP_ERR_INVALID_STATE; - } - - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 1000, - .out_buffer_size = 0, // Read-only - .event_cb = NULL, - .data_cb = data_cb, - .user_arg = this, - }; - ESP_ERROR_CHECK(this->nmea_port.open_vendor_specific(BG96_VID, BG96_PID, BG96_NMEA_INTERFACE, &dev_config)); - return this->at_port.tx_blocking((uint8_t*)"AT+QGPS=1\r", 10, 1000); - } - - esp_err_t gnss_stop() - { - esp_err_t ret = this->at_port.tx_blocking((uint8_t*)"AT+QGPSEND\r", 11, 1000); - this->nmea_port.close(); - return ret; - } - -protected: - CdcAcmDevice at_port; // Main control port for AT commands - CdcAcmDevice nmea_port; // Read only port for NMEA messages -private: - bool at_opened; -}; diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp deleted file mode 100644 index 5e7581788c2..00000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include -#include "esp_system.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" - -#include "usb/usb_host.h" -#include "bg96_usb.hpp" - -#include "nmea.h" -#include "gpgll.h" -#include "gpgga.h" -#include "gprmc.h" -#include "gpgsa.h" -#include "gpvtg.h" -#include "gptxt.h" -#include "gpgsv.h" - -#define EXAMPLE_USB_HOST_PRIORITY 20 - -static const char* TAG = "BG96"; - -static char fmt_buf[32]; - -/* ------------------------------- Callbacks -------------------------------- */ - -static void handle_rx(uint8_t *data, size_t data_len, void *user_arg) -{ - data[data_len] = '\0'; - ESP_LOGI("AT", "%s", data); -} - -static void handle_gps(uint8_t* data, size_t data_len, void *user_arg) -{ - // handle nmea_data - nmea_s *nmea_data = nmea_parse((char *)data, data_len, 0); - if (nmea_data == NULL) { - printf("Failed to parse the sentence!\n"); - printf(" Type: %.5s (%d)\n", data + 1, nmea_get_type((const char *)data)); - } else { - if (nmea_data->errors != 0) { - printf("WARN: The sentence struct contains parse errors!\n"); - } - - if (NMEA_GPGGA == nmea_data->type) { - printf("GPGGA sentence\n"); - nmea_gpgga_s *gpgga = (nmea_gpgga_s *)nmea_data; - printf("Number of satellites: %d\n", gpgga->n_satellites); - printf("Altitude: %f %c\n", gpgga->altitude, gpgga->altitude_unit); - } - - if (NMEA_GPGLL == nmea_data->type) { - printf("GPGLL sentence\n"); - nmea_gpgll_s *pos = (nmea_gpgll_s *)nmea_data; - printf("Longitude:\n"); - printf(" Degrees: %d\n", pos->longitude.degrees); - printf(" Minutes: %f\n", pos->longitude.minutes); - printf(" Cardinal: %c\n", (char)pos->longitude.cardinal); - printf("Latitude:\n"); - printf(" Degrees: %d\n", pos->latitude.degrees); - printf(" Minutes: %f\n", pos->latitude.minutes); - printf(" Cardinal: %c\n", (char)pos->latitude.cardinal); - strftime(fmt_buf, sizeof(fmt_buf), "%H:%M:%S", &pos->time); - printf("Time: %s\n", fmt_buf); - } - - if (NMEA_GPRMC == nmea_data->type) { - printf("GPRMC sentence\n"); - nmea_gprmc_s *pos = (nmea_gprmc_s *)nmea_data; - printf("Longitude:\n"); - printf(" Degrees: %d\n", pos->longitude.degrees); - printf(" Minutes: %f\n", pos->longitude.minutes); - printf(" Cardinal: %c\n", (char)pos->longitude.cardinal); - printf("Latitude:\n"); - printf(" Degrees: %d\n", pos->latitude.degrees); - printf(" Minutes: %f\n", pos->latitude.minutes); - printf(" Cardinal: %c\n", (char)pos->latitude.cardinal); - strftime(fmt_buf, sizeof(fmt_buf), "%d %b %T %Y", &pos->date_time); - printf("Date & Time: %s\n", fmt_buf); - printf("Speed, in Knots: %f\n", pos->gndspd_knots); - printf("Track, in degrees: %f\n", pos->track_deg); - printf("Magnetic Variation:\n"); - printf(" Degrees: %f\n", pos->magvar_deg); - printf(" Cardinal: %c\n", (char)pos->magvar_cardinal); - double adjusted_course = pos->track_deg; - if (NMEA_CARDINAL_DIR_EAST == pos->magvar_cardinal) { - adjusted_course -= pos->magvar_deg; - } else if (NMEA_CARDINAL_DIR_WEST == pos->magvar_cardinal) { - adjusted_course += pos->magvar_deg; - } else { - printf("Invalid Magnetic Variation Direction!\n"); - } - - printf("Adjusted Track (heading): %f\n", adjusted_course); - } - - if (NMEA_GPGSA == nmea_data->type) { - nmea_gpgsa_s *gpgsa = (nmea_gpgsa_s *)nmea_data; - - printf("GPGSA Sentence:\n"); - printf(" Mode: %c\n", gpgsa->mode); - printf(" Fix: %d\n", gpgsa->fixtype); - printf(" PDOP: %.2lf\n", gpgsa->pdop); - printf(" HDOP: %.2lf\n", gpgsa->hdop); - printf(" VDOP: %.2lf\n", gpgsa->vdop); - } - - if (NMEA_GPGSV == nmea_data->type) { - nmea_gpgsv_s *gpgsv = (nmea_gpgsv_s *)nmea_data; - - printf("GPGSV Sentence:\n"); - printf(" Num: %d\n", gpgsv->sentences); - printf(" ID: %d\n", gpgsv->sentence_number); - printf(" SV: %d\n", gpgsv->satellites); - printf(" #1: %d %d %d %d\n", gpgsv->sat[0].prn, gpgsv->sat[0].elevation, gpgsv->sat[0].azimuth, - gpgsv->sat[0].snr); - printf(" #2: %d %d %d %d\n", gpgsv->sat[1].prn, gpgsv->sat[1].elevation, gpgsv->sat[1].azimuth, - gpgsv->sat[1].snr); - printf(" #3: %d %d %d %d\n", gpgsv->sat[2].prn, gpgsv->sat[2].elevation, gpgsv->sat[2].azimuth, - gpgsv->sat[2].snr); - printf(" #4: %d %d %d %d\n", gpgsv->sat[3].prn, gpgsv->sat[3].elevation, gpgsv->sat[3].azimuth, - gpgsv->sat[3].snr); - } - - if (NMEA_GPTXT == nmea_data->type) { - nmea_gptxt_s *gptxt = (nmea_gptxt_s *)nmea_data; - - printf("GPTXT Sentence:\n"); - printf(" ID: %d %d %d\n", gptxt->id_00, gptxt->id_01, gptxt->id_02); - printf(" %s\n", gptxt->text); - } - - if (NMEA_GPVTG == nmea_data->type) { - nmea_gpvtg_s *gpvtg = (nmea_gpvtg_s *)nmea_data; - - printf("GPVTG Sentence:\n"); - printf(" Track [deg]: %.2lf\n", gpvtg->track_deg); - printf(" Speed [kmph]: %.2lf\n", gpvtg->gndspd_kmph); - printf(" Speed [knots]: %.2lf\n", gpvtg->gndspd_knots); - } - - nmea_free(nmea_data); - } -} - -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) { - printf("No more clients\n"); - ESP_ERROR_CHECK(usb_host_device_free_all()); - } - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { - break; - } - } - - //Short delay to allow task to be cleaned up - vTaskDelay(10); - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - vTaskDelete(NULL); -} - -/* ---------------------------------- Main ---------------------------------- */ -extern "C" void app_main(void) -{ - //Install USB Host driver. Should only be called once in entire application - ESP_LOGI(TAG, "Installing USB Host"); - 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 - xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, EXAMPLE_USB_HOST_PRIORITY, NULL); - - ESP_LOGI(TAG, "Installing CDC-ACM driver"); - ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); - - Bg96Usb *bg96 = new Bg96Usb(); - bg96->at_start(handle_rx, NULL); - - static char text1[] = "AT\r"; - static char text2[] = "AT+GSN\r"; - - ESP_LOGI(TAG, "Sending AT"); - bg96->at_write((uint8_t *)text1, strlen(text1)); - vTaskDelay(100); - - ESP_LOGI(TAG, "Enabling GNSS"); - bg96->gnss_start(handle_gps); - - vTaskDelay(100); - ESP_LOGI(TAG, "Sending AT+GSN"); - bg96->at_write((uint8_t *)text2, strlen(text2)); -} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml deleted file mode 100644 index 0455cf6acbd..00000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml +++ /dev/null @@ -1,5 +0,0 @@ -## IDF Component Manager Manifest File -dependencies: - idf: ">=4.4" - igrr/libnmea: "^0.1.1" - usb_host_cdc_acm: "1.*" diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md b/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md index b30ddb3d9ef..c4af5faabef 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md @@ -33,15 +33,6 @@ idf.py -p PORT flash monitor See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. -### Running with dual USB CDC device -USB CDC device example [tusb_serial_device example](../../../device/tusb_serial_device) -can be configured to act as dual CDC device. - -In the device example project, enter command `idf.py menuconfig` and set Component config->TinyUSB Stack->Communication Device Class (CDC)->CDC channel Count to `2`. - -This settings also changes device's PID, so `EXAMPLE_USB_DEVICE_PID` in [usb-cdc.c](./main/usb-cdc.c) must be changed to `0x4002`. - - ## Example Output After the flashing you should see the output at idf monitor: diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt index d2e76376b61..72a9796821c 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt @@ -1,3 +1,2 @@ -idf_component_register(SRCS "usb-cdc.c" +idf_component_register(SRCS "usb_cdc_example_main.c" INCLUDE_DIRS ".") -target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml index 96533b1fbcb..7865f535784 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml @@ -1,4 +1,4 @@ ## IDF Component Manager Manifest File dependencies: - usb_host_cdc_acm: "1.*" + usb_host_cdc_acm: "2.*" idf: ">=4.4" diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c deleted file mode 100644 index f0d831ce71f..00000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include -#include "esp_system.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "esp_err.h" -#include "usb/usb_host.h" -#include "usb/cdc_acm_host.h" - -#define EXAMPLE_USB_HOST_PRIORITY 20 -#define EXAMPLE_USB_DEVICE_VID 0x303A // 0x303A:0x4001 (TinyUSB CDC device) -#define EXAMPLE_USB_DEVICE_PID 0x4001 // Change this to 0x4002 for dual CDC device - -static const char *TAG = "USB-CDC"; - -/* ------------------------------- Callbacks -------------------------------- */ -static void handle_rx(uint8_t *data, size_t data_len, void *arg) -{ - ESP_LOGI(TAG, "Data received"); - ESP_LOG_BUFFER_HEXDUMP(TAG, data, data_len, ESP_LOG_INFO); -} - -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_LOGI(TAG, "All clients deregistered"); - ESP_ERROR_CHECK(usb_host_device_free_all()); - } - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { - break; - } - } - - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - vTaskDelete(NULL); -} - -/* ---------------------------------- Main ---------------------------------- */ -void app_main(void) -{ - //Install USB Host driver. Should only be called once in entire application - ESP_LOGI(TAG, "Installing USB Host"); - 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 - xTaskCreate(usb_lib_task, "usb_lib", 4096, xTaskGetCurrentTaskHandle(), EXAMPLE_USB_HOST_PRIORITY, NULL); - - ESP_LOGI(TAG, "Installing CDC-ACM driver"); - ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); - - ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID); - cdc_acm_dev_hdl_t cdc_dev; - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 5000, - .out_buffer_size = 64, - .user_arg = NULL, - .event_cb = NULL, - .data_cb = handle_rx - }; - ESP_ERROR_CHECK(cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID, 0, &dev_config, &cdc_dev)); - assert(cdc_dev); - cdc_acm_host_desc_print(cdc_dev); - vTaskDelay(100); - - // Test sending and receiving: Send AT commands, responses are handled in handle_rx callback - static char text1[] = "AT\r"; - ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(cdc_dev, (uint8_t *)text1, strlen(text1), 1000)); - vTaskDelay(100); - - static char text2[] = "AT+GSN\r"; - ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(cdc_dev, (uint8_t *)text2, strlen(text2), 1000)); - vTaskDelay(100); - - // Test Line Coding commands: Get current line coding, change it 9600 7N1 and read again - ESP_LOGI(TAG, "Setting up line coding"); - - cdc_acm_line_coding_t line_coding; - ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); - ESP_LOGI(TAG, "Line Get: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding.dwDTERate, - line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); - - line_coding.dwDTERate = 9600; - line_coding.bDataBits = 7; - line_coding.bParityType = 1; - line_coding.bCharFormat = 1; - ESP_ERROR_CHECK(cdc_acm_host_line_coding_set(cdc_dev, &line_coding)); - ESP_LOGI(TAG, "Line Set: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding.dwDTERate, - line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); - - ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); - ESP_LOGI(TAG, "Line Get: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding.dwDTERate, - line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); - - ESP_ERROR_CHECK(cdc_acm_host_set_control_line_state(cdc_dev, true, false)); - - ESP_LOGI(TAG, "Example finished successfully!"); -} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c new file mode 100644 index 00000000000..01a5862202e --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include +#include "esp_system.h" +#include "esp_log.h" +#include "esp_err.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "usb/usb_host.h" +#include "usb/cdc_acm_host.h" + +#define EXAMPLE_USB_HOST_PRIORITY (20) +#define EXAMPLE_USB_DEVICE_VID (0x303A) +#define EXAMPLE_USB_DEVICE_PID (0x4001) // 0x303A:0x4001 (TinyUSB CDC device) +#define EXAMPLE_USB_DEVICE_DUAL_PID (0x4002) // 0x303A:0x4002 (TinyUSB Dual CDC device) +#define EXAMPLE_TX_STRING ("CDC test string!") +#define EXAMPLE_TX_TIMEOUT_MS (1000) + +static const char *TAG = "USB-CDC"; +static SemaphoreHandle_t device_disconnected_sem; + +/** + * @brief Data received callback + * + * @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) +{ + ESP_LOGI(TAG, "Data received"); + ESP_LOG_BUFFER_HEXDUMP(TAG, data, data_len, ESP_LOG_INFO); + 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 = %i", event->data.error); + break; + case CDC_ACM_HOST_DEVICE_DISCONNECTED: + ESP_LOGI(TAG, "Device suddenly disconnected"); + ESP_ERROR_CHECK(cdc_acm_host_close(event->data.cdc_hdl)); + 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: + ESP_LOGW(TAG, "Unsupported CDC event: %i", event->type); + 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 + } + } +} + +/** + * @brief Main application + * + * Here we open a USB CDC device and send some data to it + */ +void app_main(void) +{ + 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, xTaskGetCurrentTaskHandle(), EXAMPLE_USB_HOST_PRIORITY, NULL); + assert(task_created == pdTRUE); + + ESP_LOGI(TAG, "Installing CDC-ACM driver"); + ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); + + const cdc_acm_host_device_config_t dev_config = { + .connection_timeout_ms = 1000, + .out_buffer_size = 512, + .in_buffer_size = 512, + .user_arg = NULL, + .event_cb = handle_event, + .data_cb = handle_rx + }; + + while (true) { + cdc_acm_dev_hdl_t cdc_dev = NULL; + + // Open USB device from tusb_serial_device example example. Either single or dual port configuration. + ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X...", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID); + esp_err_t err = cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID, 0, &dev_config, &cdc_dev); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X...", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_DUAL_PID); + err = cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_DUAL_PID, 0, &dev_config, &cdc_dev); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Failed to open device"); + continue; + } + } + cdc_acm_host_desc_print(cdc_dev); + vTaskDelay(pdMS_TO_TICKS(100)); + + // Test sending and receiving: responses are handled in handle_rx callback + ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(cdc_dev, (const uint8_t *)EXAMPLE_TX_STRING, strlen(EXAMPLE_TX_STRING), EXAMPLE_TX_TIMEOUT_MS)); + vTaskDelay(pdMS_TO_TICKS(100)); + + // Test Line Coding commands: Get current line coding, change it 9600 7N1 and read again + ESP_LOGI(TAG, "Setting up line coding"); + + cdc_acm_line_coding_t line_coding; + ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Get: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + line_coding.dwDTERate = 9600; + line_coding.bDataBits = 7; + line_coding.bParityType = 1; + line_coding.bCharFormat = 1; + ESP_ERROR_CHECK(cdc_acm_host_line_coding_set(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Set: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Get: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + ESP_ERROR_CHECK(cdc_acm_host_set_control_line_state(cdc_dev, true, false)); + + // We are done. Wait for device disconnection and start over + ESP_LOGI(TAG, "Example finished successfully! You can reconnect the device to run again."); + xSemaphoreTake(device_disconnected_sem, portMAX_DELAY); + } +} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md index 7b610bcf71a..71fdfb331d9 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md @@ -1,18 +1,18 @@ | Supported Targets | ESP32-S2 | ESP32-S3 | | ----------------- | -------- | -------- | -# USB CDC-ACM Virtual Com Port example +# USB CDC-ACM Virtual COM Port example (See the README.md file in the upper level 'examples' directory for more information about examples.) This example shows how to extend CDC-ACM driver for Virtual Communication Port (VCP) devices, -such as CP210x, FTDI FT23x or CP34x devices. +such as CP210x, FTDI FT23x or CH34x devices. The drivers are fetched from [IDF Component Registry](https://components.espressif.com/) together with VCP service that automatically loads correct driver for plugged-in device. ## How to use example -1. Connect your USB<->UART converter to ESP32-S2/S3, the device will be automatically enumerated and correct driver will be picked +1. Connect your USB<->UART converter to ESP32-S2/S3, the device will be automatically enumerated and correct driver will be loaded 2. Change baudrate and other line coding parameters in [cdc_acm_vcp_example_main.cpp](main/cdc_acm_vcp_example_main.cpp) to match your needs 3. Now you can use the usual CDC-ACM API to control the device and send data. Data are received in `handle_rx` callback 4. Try disconnecting and then reconnecting of the USB device to experiment with USB hotplugging diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp index c1ffcad16b4..f90620ec879 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ @@ -28,25 +28,36 @@ using namespace esp_usb; #define EXAMPLE_DATA_BITS (8) namespace { -const char *TAG = "VCP example"; -SemaphoreHandle_t device_disconnected_sem; +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 */ -void handle_rx(uint8_t *data, size_t data_len, void *arg) +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 */ -void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) +static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) { switch (event->type) { case CDC_ACM_HOST_ERROR: @@ -69,7 +80,7 @@ void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) * * @param arg Unused */ -void usb_lib_task(void *arg) +static void usb_lib_task(void *arg) { while (1) { // Start handling system events @@ -89,14 +100,14 @@ void usb_lib_task(void *arg) /** * @brief Main application * - * This function shows how you can use VCP drivers + * This function shows how you can use Virtual COM Port drivers */ extern "C" void app_main(void) { device_disconnected_sem = xSemaphoreCreateBinary(); assert(device_disconnected_sem); - //Install USB Host driver. Should only be called once in entire application + // 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, @@ -111,7 +122,7 @@ extern "C" void app_main(void) ESP_LOGI(TAG, "Installing CDC-ACM driver"); ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); - // Register VCP drivers to VCP service. + // Register VCP drivers to VCP service VCP::register_driver(); VCP::register_driver(); VCP::register_driver(); @@ -120,13 +131,14 @@ extern "C" void app_main(void) while (true) { 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 = 64, + .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 pick correct (already registered) driver for the device + // 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)); diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml index 94b225b6243..d8f3149282a 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml @@ -1,7 +1,7 @@ ## IDF Component Manager Manifest File dependencies: - usb_host_ch34x_vcp: "^1" - usb_host_cp210x_vcp: "^1" - usb_host_ftdi_vcp: "^1" + usb_host_ch34x_vcp: "^2" + usb_host_cp210x_vcp: "^2" + usb_host_ftdi_vcp: "^2" usb_host_vcp: "^1" idf: ">=5.1.0"