Fix ESP32-S3 USB CDC: post-disconnect task-WDT reboot from blocking console log writes (#10956)

* Fix ESP32-S3 USB CDC: post-disconnect task-WDT reboot from blocking console log writes

When a serial API client disconnects (USB cable still attached), the host
stops draining the USB-Serial/JTAG CDC buffer. The next raw-text debug log
write can then block the main loop task indefinitely (measured: a single
write blocked 52.4 s). The loop task stops feeding the app task watchdog
(APP_WATCHDOG_SECS = 90 s, trigger_panic), so the device reboots with
esp_reset_reason = ESP_RST_TASK_WDT ~97 s after every serial disconnect.

On 2.7.x (arduino-esp32 2.x / IDF 4.4) the reboot also re-enumerated USB;
since the Arduino 3.x migration the reboot is silent (USB stays enumerated),
making it look like a random reboot ~90 s after using the CLI.

Fix: on USB CDC targets, keep console TX in non-blocking mode (txTimeout 0,
drop-oldest) whenever no API client is provably alive, and restore the
normal bounded timeout while a client is connected so protobuf API frames
are never truncated. Toggle points: boot, handleToRadio (host sent bytes),
and onConnectionChanged (set non-blocking before disconnect handling emits
more log lines to a dead port).

Repro/validation on HELTEC_WIRELESS_TRACKER_V2 (macOS + Linux hosts):
open+close any meshtastic-python session, wait 150 s: reboot_count +1 every
time on unpatched builds; flat with this fix. Max observed log write stall
drops from 52405 ms to 30 ms.

* Condense comments per review feedback

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
p0ns
2026-07-09 11:42:15 -05:00
committed by GitHub
co-authored by Ben Meadors
parent 6768bd08df
commit 144b07986b
2 changed files with 37 additions and 0 deletions
+9
View File
@@ -40,8 +40,17 @@ class SerialConsole : public StreamAPI, public RedirectablePrint, private concur
virtual void onNowHasData(uint32_t fromRadioNum) override;
/// Track serial API connect/disconnect so we can make console writes
/// non-blocking while no host is listening (see setHostDraining()).
virtual void onConnectionChanged(bool connected) override;
/// Possibly switch to protobufs if we see a valid protobuf message
virtual void log_to_serial(const char *logLevel, const char *format, va_list arg);
private:
/// On USB CDC targets, keep console TX non-blocking unless a host is draining the
/// port, so a dead host can't stall the main loop and trip the task watchdog.
void setHostDraining(bool draining);
};
// A simple wrapper to allow non class aware code write to the console