Auto-Recovery & Connection Drop Handling
Relevant source files
The following files were used as context for generating this wiki page:
ComChan implements a robust auto-recovery system designed to handle intermittent connection drops, hardware resets, and transient errors across various communication mediums, including physical Serial ports, RTT/Defmt debug probes, and Bluetooth Low Energy (BLE) streams. The system ensures that the application remains responsive, provides visual feedback to the user during disconnections, and automatically attempts to restore data flow without requiring a full application restart.
The recovery logic is integrated into the core execution loops of the standard Monitor, the Dual Monitor TUI, and the Serial Plotter. By utilizing configurable timeouts and reset delays, the system balances aggressive reconnection attempts with hardware stability, ensuring a graceful exit or recovery when hardware is removed or reset.
Sources: README.md, src/monitor.rs:328-406, CHANGELOG.md
Architecture and Logic Flow
The auto-recovery system operates via a continuous polling mechanism within the main communication loops. When a read or write operation fails, the system transitions from a "Connected" state to a "Waiting/Reconnecting" state. During this period, the UI remains active, allowing users to switch modes or quit while the background process attempts to re-initialize the connection.
Reconnection Lifecycle
- Error Detection: A
std::io::Error(other thanTimedOut) is caught during a serial read or write operation. - State Transition: The
is_connectedflag is set tofalse, and the specific error is logged to the status bar or terminal. - Polling Loop: The system enters a
whileloop that attempts to re-open the port or re-initialize the RTT session. - Reset Delay: A configurable
reset_delay_msis applied after a successful re-open to allow the target hardware to stabilize. - Buffer Clearing: The input buffers are cleared to prevent stale data from flooding the UI upon reconnection.
The following diagram illustrates the recovery flow for a standard serial connection:
Sources: src/monitor.rs:328-412, src/plotter.rs:400-415
Component-Specific Recovery
Standard Monitor (Serial & RTT)
In run_normal_mode, the recovery logic uses a while running.load() loop that wraps the initialization of RTT readers and Serial ports. If a device is not found or a connection is lost, the system prints a "Waiting for device..." message and enters a polling cycle. This cycle is interrupted by the poll_ctrl_rx_while_waiting! macro, which checks if the user has requested to switch modes or quit during the reconnection attempt.
Sources: src/monitor.rs:328-412, src/monitor.rs:67-110
Serial Plotter Mode
The Plotter mode handles RTT loss specifically by catching errors in reader.poll_logs(). If a loss is detected, it immediately attempts to create a new RttDefmtReader. For physical serial ports, the plotter uses a "Drain Loop" that attempts to pull available data into a receive_buf, reporting Read error to the status bar without crashing the UI.
Sources: src/plotter.rs:388-410, src/plotter.rs:460-480
Dual Monitor Mode
The dual_ports.rs module spawns independent threads for each port. If one thread encounters a Read Error or Write Error, it sends a DualEvent::Error back to the main TUI thread. This allows the other port to continue functioning while the errored pane displays the status.
Sources: src/dual_ports.rs:118-160, src/dual_ports.rs:260-265
Configuration Parameters
The behavior of the recovery system is governed by several fields within the MergedConfig struct, which can be set via CLI arguments or the comchan.toml file.
| Parameter | Type | Default | Description |
|---|---|---|---|
timeout_ms | u64 | 500 | Timeout for individual serial read/write operations. |
reset_delay_ms | u64 | 1000 | Delay after opening a port to allow hardware to boot. |
simulate | bool | false | Bypasses recovery by providing mock data streams. |
verbose | bool | false | Enables detailed error logging during recovery attempts. |
Sources: src/config.rs:104-123, src/config.rs:240-255
Implementation Details
RTT Reconnection Snippet
The system attempts to re-initialize the RTT session using the provided ELF file and chip name. This is critical for debugging targets that power-cycle frequently.
// src/plotter.rs:400-410
match RttDefmtReader::new(elf, config.chip.clone()) {
Ok(new_reader) => {
*reader = new_reader;
state.last_error = Some("RTT re-connected!".to_string());
}
Err(err) => {
state.last_error = Some(format!("RTT re-connect failed: {}", err));
std::thread::sleep(Duration::from_millis(500));
}
}
Sources: src/plotter.rs:400-410
Serial Reconnection with DTR Control
During serial reconnection, the system explicitly toggles the Data Terminal Ready (DTR) signal to ensure the target reset logic is triggered correctly if supported.
// src/monitor.rs:360-365
match serialport::new(&port_name, config.baud)
.timeout(Duration::from_millis(config.timeout_ms))
.open()
{
Ok(mut p) => {
let _ = p.write_data_terminal_ready(false);
thread::sleep(Duration::from_millis(config.reset_delay_ms));
let _ = p.write_all(b"\r");
Sources: src/monitor.rs:360-370
ComChan's auto-recovery system ensures that embedded developers can maintain a continuous view of their system's telemetry and logs, even through hardware power cycles and physical disconnections. By isolating the connection logic from the UI rendering and utilizing configurable delays, the project provides a resilient tool for real-world embedded debugging.