USB Host Mode HT-HC33/32

Hello,

I am currently working with a *Heltec HT-HC32 and 33 (ESP32-S3) and trying to enable USB OTG / USB Host mode for connecting USB FTDI cables.

The device keeps showing the following error log:

E (...) HUB: Root port reset failed

Here are the technical details of what I have already verified:

  1. I identified GPIO20 (USB_D+) and GPIO19 (USB_D−) from the J1 header and connected them to a USB-A breakout board. ( with and without resistors)

  2. Common GND is correctly connected.

  3. I provided VBUS = 5V from a powered USB hub to ensure the host powers the device.

  4. Firmware is configured in USB Host mode only (no CDC Device / JTAG enabled).

  5. Delays were adjusted to allow VBUS stabilization before port reset.

  6. Tested with multiple cables.

Even with these conditions, USB OTG enumeration still fails at the very first root port reset stage.

Any guidance, documentation, or working reference would be greatly appreciated.

BTW, I use another ESP32-S3 board (Not HELTEC) same firmware, same cables, and it works normally.

Thank you for your time and assistance.

I don’t have a HT-HC33/32 but the following OTG Host serial (Virtual COM Port) code works on a Heltec_WiFi_LoRa32_V3 using ESP-IDF

// 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

// note that GPIO19 and 20 are used for USB D- and USB D+ respectively

/*
 * 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;36m%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);
    }
}

ESP-IDF Poweshell output when communicating with a Quectel Mini PCIe EVB Kit (with Quectel EC21-E modem) via RS232 (transmitted characters blue received characters yellow)

test setup
image

Thank you so much for your response. Finally, I realized that the problem was two resistors that were being used for another purpose on the board. I uninstalled those resistors, and the USB pins started working as expected.

Hi Inglpa, what resistors did you remove? I’m having trouble connecting my HT-HC33 I might be having the same issue?

Cheers Adam.