ESP32-S3 as USB Host for Quectel EG800Q Modem (CDC Interface Issue)

Hi everyone,

I’m trying to use the ESP32-S3 as a USB Host to communicate with a Quectel EG800Q 4G modem via its USB interface.
I’m using the USB CDC Host libraries in the Arduino IDE, but I’m not getting any response from the modem when sending AT commands. It seems that the ESP32-S3 isn’t able to properly initialize or communicate with the EG800Q over USB.

Has anyone successfully interfaced the ESP32-S3 as a USB Host with a Quectel modem (like EC800Q/EG800Q)?

  • Do I need to modify the CDC driver or use a specific library?
  • Is there a working example for USB-to-modem communication using the ESP32-S3 native USB Host?
  • Any known limitations or setup steps I might be missing?
  • Is it possible in ardunio IDE or should i use ESP iDF

Any guidance, sample code, or working references would be really appreciated.

Thanks in advance!

hey
any thought on this

does the modem have a TTL or RS232 serial interface?
if so why not use that? e.g. ESP32-S3-DevKitC-1 connected to a Quectel Mini PCIe EVB Kit (with Quectel EC21-E modem) via RS232

yes it have,
But My mcus all other pins are doing other function, so native USB is the only ways.
Both modem and Esp32s3 hav native USB support , how to implement it in ardunio ide . give a guide.

hey guys

any update on this

I have deleted your other cross-post @novalriod123.

Cross-posting is against the Arduino Forum rules. The reason is that duplicate posts can waste the time of the people trying to help. Someone might spend a lot of time investigating and writing a detailed answer on one topic, without knowing that someone else already did the same in the other topic.

Repeated cross-posting can result in a suspension from the forum.

In the future, please only create one topic for each distinct subject matter. This is basic forum etiquette, as explained in the "How to get the best out of this forum" guide. It contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

hey guys
any update on this

looks like you are attempting ESP32S3 OTG Host serial (Virtual COM Port)
the ESP32S3 File>Examples>USB all appear to be OTG device examples (composite, serial, etc)

there is a ESP-IDF ESP32S3 OTG VCP example - see USB CDC-ACM Virtual COM Port example

I adapted it to connect to a Quectel Mini PCIe EVB Kit (with Quectel EC21-E modem) via RS232

// ESP32-S3-DevKitC-1 USB CDC-ACM Virtual COM Port example

// adapted from https://github.com/espressif/esp-idf/blob/master/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md

/*
 * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#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"
#include "esp_task_wdt.h"

using namespace esp_usb;

// Change these values to match your needs
#define EXAMPLE_BAUDRATE     (9600)//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("\e[0;33m%.*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
        }
    }
}
}

/**
 * @brief Main application
 *
 * This function shows how you can use Virtual COM Port drivers
 */
extern "C" void app_main(void)
{
    printf("\n\nESP32-S3-DevKitC-1 OTG Host VCP serial test\n");

    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");
    usb_host_config_t host_config = {};
    host_config.skip_phy_setup = false;
    host_config.intr_flags = ESP_INTR_FLAG_LOWMED;
    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>();

    // Do everything else in a loop, so we can demonstrate USB device reconnections
    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 = 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));

        printf("\n\nenter text to transmit over VCP serial\n");
        // loop reading characters from keyboard and serial and displaying
        while(1) {
          vTaskDelay(10);                   // cleat WDT
          char ch;
          ch=fgetc(stdin);                  // read character from keyboard?
          if(ch!=0xFF) {
             if(ch>=' ' || ch=='\n'){       // character read if OK
                if(ch=='\n')ch='\r';        // \r is line terminator
                printf("\e[0;32m%c",ch);    // print character and transmit over serial
                ESP_ERROR_CHECK(vcp->tx_blocking((uint8_t*) &ch, sizeof(ch)));
                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);
    }
}

the ESP-IDF Powershell monitor displays (transmitted characters blue received characters yellow)

test setup

note that GPIO19 and 20 are used for USB D- and USB D+ respectively therefore you cannot use them as general GPIO

note to enable USB host 5V you have to solder a link on the rear of the PCB

hi,

your sample program not work with core 3.3.4 and ESP32-P4 or ESP32-S3.

ide 2.3.6
Core 3.3.4
ESP32-P4
Windows 10

 fatal error: usb/cdc_acm_host.h: No such file or directory
   19 | #include "usb/cdc_acm_host.h"
      |          ^~~~~~~~~~~~~~~~~~~~

Do I need to install a specific library?

Thanks.

the code in post 8 was run using ESP-IDF V5.3
were you attempting to use the Arduino IDE?

That's what I thought, as it's posted on the arduino forum, I hadn't seen that you mentioned ESP-IDF, it works well on ESP-IDF when testing it.

I tried on ardunio ide, but fails.
On espidf i got the output and worked well

I am new to this forum, I have ESP32S3 WRoom-1 from freenova with uart usb, and CDC OTG usb ports. I am trying to talk to ec25-af modem in minipcie to usb adapter. I tired for 4 days so far getting no where this code will not compile! I think they scam us selling cdc OTG on ESP boards when Arduino doesn't support it and VS code no longer supports it. C:/Users/xxxx/Documents/esp32s3_Wroom/Wroom_esp32_VS_code_OTG_v4/Wroom_esp32_VS_code_OTG_v4/main/main.c:19:10: fatal error: usb/cdc_acm_host.h: No such file or directory
19 | #include "usb/cdc_acm_host.h"

Under config I do not see Tiny USB or USB-Host, to add these libraries downgraded to v5.3.4, still no dice. Does this not work on windows 11, wiht v5.3.4? If so what am I doing wrong.

use ESP-IDF? see example in post 8
alternatively you could use a 3.3V TTL-RS232 adapter to connect the ESP32S3 to the modem

  1. So when you connect your modem via USB to your laptop, does it behave as CDC?
  2. Can you run TeraTerm or PuTTY and connect to your modem? (Make sure your terminal program is configured to send CR, LF as line endings.)
  3. If terminal program connects to the modem, try this sequence:
ATE1
ATI

(Modems often has their echo off)

can’t be both.

CDC is the console, “native” to USB. Make sure you turned Arduino IDE→Tools→USB CDC On boot→ Enabled.

I think I am Using ESP-IDF, I was in a hurry last night to post and forgot to give a detailed explanation.. sorry about that.

I’m using VS code on windows 11, with v5.3.4 ESP-IDF. I previously tried Arduino but couldn’t get any tiny usb or cdc usb driver to load. AI and searching the internet told me that usb host hasn’t been implemented in the Esp32 Arduino library yet. Everything was pointing towards using the native ESP-IDF. Then I landed on this example which seemed to say it compiled and worked.

The post that says the example code worked post #8. Also says it only works on VS code with ESP-IDF v5.3.4. So I tried to copy that exact setup.

I might not have the config setup correctly? I need to enable USB-host or USB-tinyusb stack. But don’t see any option for USB other than. USB-CDC. What seatings did you use?

Is it a board limitation? My freenova board says it works with OTG or UART USB and even has two separate USB-C ports. Using ESP32S3.

I already have it working with uart, so I don’t need a rs232, adapter. But the test pads on the usb bridge that the ec25-af is in are tiny and hard to solder to. I can’t find a cheap breakout board minipcei, that gives a decent header to all the pins.

Plus I wanted to try having all three ports exposed, nema, at, and serial. So I can read signal quality and get gnss data without breaking my Blynk connection.

Btw than did the prompt response everyone:)

Yes excellent follow up question. Yup it enumerates perfectly in windows after installing quectel drivers. Gives 4 ports DM, AT, nema, serial.

Testing with putty works!

Yes Echo on, off stumped me before but I have echo on for now.

I’m not able to get my code to compile right now due to missing libraries. Ounce that actually works then your tips will be quite useful. Thanks so much!

I did but in Arduino for Esp32 host cdc is not possible yet. It won’t compile due to missing libraries must use ESP-IDF.

But if that statement is wrong I’d much rather use Arduino so enlightened me with any working examples:)

Thanks for your help:)

I never used VS code
I used ESP-IDF 5.3 PowerShell to build and run the VCP serial code
after changing to the project directory I ran the following commands (ESP32S3 was on COM9)

idf.py fullclean
idf.py set-target esp32s3
idf.py build
idf.py -p COM9 flash monitor

the monitor then opened , set the serial baudrate to 9600 and I could enter AT commands, e.g.