Debug Builds: the console over Wi-Fi (Debug Console, TCP 2323)

cardputer-adv-debug (-DRORO_DEBUG, version +debug) adds a Debug Console:
after a token line, a client gets the last 6 KB of console output, live
lines (ESP-IDF logs included) and the serial commands. The socket task
only queues lines; the main loop runs them. Release builds compile none
of it. The token lives in ~/.config/roro9stack/debug-token, created by
_docker.sh and passed into the container.

All output now goes through `console`, which never waits for USB: a host
that was attached but not reading stalled the main loop up to 2 s per
line. New commands everywhere: info (slots with their versions from NVS,
since the framework stamps its own into each image), tasks, reboot,
boot other, log level, help. scripts/rdbg.py is the client; flash.sh
--debug builds it; CI builds both variants. ADR 0004.

Verified on the device: USB-flashed, then updated over Wi-Fi to a Debug
Build that confirmed on Probation; both slots hold Debug Builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-04 02:44:14 +02:00
co-authored by Claude Opus 5.5
parent 5b199c2436
commit 0fb7f4e9d5
18 changed files with 828 additions and 97 deletions
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <Print.h>
#include <cstddef>
#include <cstdint>
namespace roro {
// Where the firmware's console output goes: the USB serial port, and in a Debug Build also a ring
// buffer the Debug Console sends over Wi-Fi (with what came before the connection, so boot messages
// aren't lost). Use `console` instead of Serial for anything a human should be able to read remotely.
class Console : public Print {
public:
size_t write(uint8_t c) override { return write(&c, 1); }
size_t write(const uint8_t* data, size_t len) override;
using Print::write;
#ifdef RORO_DEBUG
static constexpr size_t kRingBytes = 6144;
// Also copies ESP-IDF's own log lines into the ring (they still reach the serial port).
void captureEspLogs();
// Copies bytes written since `pos` into `out`, and advances `pos`. A reader that fell more than
// the ring behind gets the oldest bytes still there, and `skipped` says how many it missed.
size_t readSince(uint32_t& pos, uint8_t* out, size_t max, uint32_t& skipped);
// The position of the oldest byte still in the ring: a new reader starts there.
uint32_t oldest() const;
// The ring only, for output that already reaches the serial port another way.
void toRing(const uint8_t* data, size_t len);
private:
uint8_t ring_[kRingBytes];
uint32_t head_ = 0; // total bytes ever written; the ring holds the last kRingBytes of them
#endif
};
extern Console console;
} // namespace roro