ESP32-S3 + LVGL + TinyUSB (Keyboard HID) = Fake "Connection Refused" in HTTPClient (GDMA/lwIP Buffer Conflict)

Title: ESP32-S3 + OPI PSRAM + LVGL + TinyUSB (Keyboard HID) = Fake “Connection Refused” in HTTPClient (GDMA/lwIP Buffer Conflict)

Hi everyone,

I am experiencing a critical hardware/software resource collision on an ESP32-S3 board (Waveshare 3.5" Display) while trying to run three main features concurrently: a graphical user interface (LVGL), a network stack (Wi-Fi), and hardware keyboard emulating via software (TinyUSB).

The Symptom:

If I comment out the USB.begin(); line, everything works flawlessly. The display renders fine with LVGL, and my asynchronous loops of HTTPClient.GET() / POST requests return HTTP 200 indefinitely.

However, the exact moment USB.begin() and Keyboard.begin() are executed, all subsequent outbound network requests fail immediately with a connection refused error. Strangely, the Wi-Fi connection itself does not drop; WiFi.status() remains solidly at WL_CONNECTED.

Diagnostics & FreeRTOS Task Dump:

To rule out CPU starvation from the USB polling loops, we intercepted the FreeRTOS task handles at runtime and manually downgraded the priorities of the Arduino-ESP32 core native USB tasks (which are created at the maximum priority of 24 by default):

text

================================================
FREERTOS TASKS AFTER HID
================================================
ui_task          P=1   S=2  Stack=5908  RT=1670139  (LVGL Handler)
wifi             P=23  S=2  Stack=4064  RT=131094   (Espressif Network Layer)
usbd             P=19  S=2  Stack=3016  RT=8458     (TinyUSB - Downgraded from 24)
arduino_usb_eve  P=18  S=2  Stack=1176  RT=2478     (USB Evt - Downgraded from 24)
================================================
Heap=58128 | MinHeap=57268 | Largest=7733236 (OPI PSRAM Active)

Usa el código con precaución.

As shown in the dump, the wifi task (Priority 23) has a higher priority than both USB tasks (19 and 18). Additionally, both USB tasks are pinned to Core 1, leaving Core 0 exclusively for the networking layer. The Runtime (RT) counter proves that the Wi-Fi task is getting CPU cycles regularly. This is definitely not CPU starvation.

Memory profiling also shows enough free internal RAM (~58KB SRAM) and the Octal PSRAM is mapped correctly. Adjusting network parameters like http.setTimeout(5000) and http.setReuse(true) does not fix the issue either.

The Working Hypothesis (GDMA / Silicon Level Conflict):

The immediate connection refused error triggered by HTTPClient (which traces down to the connect() socket call inside the lwIP stack) indicates that the software initialization of TinyUSB disrupts the physical GDMA (Direct Memory Access) channels or corrupts the internal memory regions (MALLOC_CAP_DMA) required by lwIP to allocate the TX/RX network ring buffers.

Since PSRAM is configured in Octal (OPI) mode (which is strictly required for the high-bandwidth display framebuffers of this Waveshare board), the ESP32-S3 internal bus arbiter is already heavily loaded. It seems that when TinyUSB reconfigures the global USB-OTG peripheral registers, it silently kicks off or breaks the DMA allocation that the Wi-Fi MAC layer relies on to move hardware network packets into internal RAM.

Questions:

  1. Has anyone successfully managed to achieve concurrent stability with the software TinyUSB stack (HID/CDC) on an ESP32-S3 that has high DMA demands due to OPI PSRAM and RGB/SPI displays?
  2. Is there a way via sdkconfig or specific Arduino HAL overrides to force TinyUSB into a dedicated GDMA channel that won’t overwrite the Wi-Fi MAC layer or lwIP socket descriptors?

Any low-level suggestions or insights would be highly appreciated! Thanks in advance.