Files
firmware/src/SerialConsole.h
T
Ben Meadors 905482ccce fix(serial): don't sleep forever with pending PhoneAPI output on UART consoles (#11500)
* fix(serial): don't sleep forever with pending PhoneAPI output on UART consoles

Since #11164 bounded the stream drain, a config dump can end a dispatch
with output still queued. On UART-console ESP32 boards runOnce() then
returns INT32_MAX with no RX pending, and neither rxInt() nor
onNowHasData() fires for the remaining output, so the download wedges
mid nodeinfo stream until the client happens to send a byte.

Add StreamAPI::hasPendingOutput() (transport-retained frame or queued
PhoneAPI data) and have SerialConsole::runOnce() short-poll (<=25ms)
while it holds instead of sleeping INT32_MAX. The #11164 write budget
is unchanged; idle sleep behavior with a drained queue is unchanged.
The retained-frame probe also covers the ESP32-S2 USB-CDC branch,
which takes the same INT32_MAX path.

* test(serial): restore scratch NodeDB via tearDown, trim comments to house style

A failed TEST_ASSERT longjmps out of a Unity test without running
destructors, so RAII cannot restore the swapped nodeDB pointer; install
the scratch NodeDB explicitly and restore/delete it in tearDown(),
which runs after every test outcome. Also shorten the new comments to
the two-line house limit.
2026-08-14 10:41:44 -05:00

74 lines
2.8 KiB
C++

#pragma once
#include "RedirectablePrint.h"
#include "StreamAPI.h"
#include "mesh/StreamFrameWriter.h"
/**
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/
class SerialConsole : public StreamAPI, public RedirectablePrint, private concurrency::OSThread
{
/**
* If true we are talking to a smart host and all messages (including log messages) must be framed as protobufs.
*/
bool usingProtobufs = false;
public:
/// Initialize the shared serial stream for console and protobuf traffic.
SerialConsole();
/**
* we override this to notice when we've received a protobuf over the serial stream. Then we shunt off
* debug serial output.
*/
virtual bool handleToRadio(const uint8_t *buf, size_t len) override;
/// Write a raw console byte unless the stream is in protobuf mode.
virtual size_t write(uint8_t c) override;
/// Service serial input, pending output, and connection state.
virtual int32_t runOnce() override;
/// Flush raw console output when explicitly requested.
void flush();
/// Wake the serial thread after receive activity.
void rxInt();
protected:
/// Check the current underlying physical link to see if the client is currently connected
virtual bool checkIsConnected() override;
/// Wake the serial thread when PhoneAPI queues output.
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;
/// Emit a framed API log when enabled, or raw output before protobuf mode.
virtual void log_to_serial(const char *logLevel, const char *format, va_list arg);
/// Continue retained USB CDC output before PhoneAPI advances.
virtual bool finishPendingFrame() override;
/// Report a retained USB CDC frame awaiting TX space.
virtual bool hasRetainedFrame() override;
/// Return whether the dedicated log buffer can be safely overwritten.
virtual bool canEncodeLogRecord() override;
/// Write or retain one framed USB CDC message.
virtual bool writeFrame(uint8_t *buf, size_t len, bool bestEffort) override;
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);
#if defined(ARDUINO_USB_CDC_ON_BOOT) && ARDUINO_USB_CDC_ON_BOOT
StreamFrameWriter frameWriter;
#endif
};
// A simple wrapper to allow non class aware code write to the console
void consoleInit();
extern SerialConsole *console;