diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index 5aafc0630..2e55e31bb 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -65,6 +65,8 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con Port.setRX(SERIAL2_RX); #endif Port.begin(SERIAL_BAUD); + // Boot with console TX in non-blocking mode: no host is provably listening yet. + setHostDraining(false); time_t timeout = millis(); while (!Port) { if (Throttle::isWithinTimespanMs(timeout, FIVE_SECONDS_MS)) { @@ -121,6 +123,28 @@ bool SerialConsole::checkIsConnected() return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT); } +void SerialConsole::setHostDraining(bool draining) +{ +#ifdef IS_USB_SERIAL + // Timeout 0 makes HWCDC writes drop instead of block when the host stops draining; + // bounded blocking is restored while an API client is connected so frames aren't truncated. + Port.setTxTimeoutMs(draining ? 100 : 0); +#else + (void)draining; +#endif +} + +void SerialConsole::onConnectionChanged(bool connected) +{ + // Order matters on disconnect: make console TX non-blocking *before* the + // PowerFSM/close handling below emits more log lines to a dead port. + if (!connected) + setHostDraining(false); + StreamAPI::onConnectionChanged(connected); + if (connected) + setHostDraining(true); +} + /** * we override this to notice when we've received a protobuf over the serial * stream. Then we shut off debug serial output. @@ -129,6 +153,10 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) { // only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled. if (config.has_lora && config.security.serial_enabled) { + // The host just sent us bytes, so it is alive and draining the port: + // restore normal bounded-blocking TX before any API response is written. + setHostDraining(true); + // Switch to protobufs for log messages usingProtobufs = true; canWrite = true; diff --git a/src/SerialConsole.h b/src/SerialConsole.h index 98577e4bc..e81ded22f 100644 --- a/src/SerialConsole.h +++ b/src/SerialConsole.h @@ -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