From a525606d7f293cd64bf23bdab61343f3cb52c643 Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Mon, 18 May 2026 19:26:25 -0700 Subject: [PATCH] fix: USB serial logs now flow on cold+warm boot without jiggle (#2034) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "logs only flow if you unplug and replug the USB cable at the right moment" symptom traced to two interacting problems with the ESP32-C3 USB Serial/JTAG controller (HWCDC): 1. Serial.begin was gated on gpio.isUsbConnected(). That check sampled USB state at one specific microsecond during boot. If USB enumeration on the host hadn't completed by that moment (common after a reset that auto- reconnects a moment later), Serial was never initialized and stayed dead until the next boot where the timing happened to win. 2. HWCDC writes block for up to the configured TX timeout (default 250 ms) when the host has the port open but isn't actively draining — a state the macOS USB CDC stack enters intermittently after reconnect. The firmware then appears to hang on logging until a USB unplug+replug cycles the peripheral and flushes the TX FIFO. Fix: move the Serial init to the very top of setup() with a 250 ms stall before Serial.begin (lets the USB peripheral power-on and host enumeration complete on cold boot), and call logSerial.setTxTimeoutMs(0) so writes drop bytes harmlessly when the host is slow instead of stalling the firmware. Both warm reboot and cold power-on now produce logs immediately. Did you use AI tools to help write this code? partial --- src/main.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 9c78f11c..6c601ab9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -261,6 +261,21 @@ void setupDisplayAndFonts() { void setup() { t1 = millis(); +#ifdef ENABLE_SERIAL_LOG + // Earliest possible Serial setup. The 250 ms stall before begin() lets the + // USB Serial/JTAG peripheral finish power-on and lets the host complete USB + // enumeration before we touch the CDC state — otherwise cold boot races + // and the host has to be physically replugged for logs to flow. Warm reboot + // worked without the delay because USB was already enumerated. + // + // setTxTimeoutMs(0) makes writes non-blocking — the HWCDC TX FIFO drops + // bytes harmlessly if the host isn't actively draining, instead of blocking + // for the default 250 ms per write and chaining into a firmware hang. + delay(250); + Serial.begin(115200); + logSerial.setTxTimeoutMs(0); +#endif + HalSystem::begin(); // Read-and-clear so a panic later in setup() doesn't loop into silent reboot. @@ -276,16 +291,6 @@ void setup() { halTiltSensor.begin(); halClock.begin(); -#ifdef ENABLE_SERIAL_LOG - if (gpio.isUsbConnected()) { - Serial.begin(115200); - const unsigned long start = millis(); - while (!Serial && (millis() - start) < 500) { - delay(10); - } - } -#endif - LOG_INF("MAIN", "Hardware detect: %s", gpio.deviceIsX3() ? "X3" : "X4"); // SD Card Initialization