Skip to content

Add dual-channel serial passthrough to userial#1

Draft
Copilot wants to merge 5 commits intomainfrom
copilot/add-serial-pass-through-functionality
Draft

Add dual-channel serial passthrough to userial#1
Copilot wants to merge 5 commits intomainfrom
copilot/add-serial-pass-through-functionality

Conversation

Copy link
Copy Markdown

Copilot AI commented Apr 28, 2026

Summary

  • add dual-channel UART support to tools/userial with default channel A pins on GPIO4/GPIO5 and channel B pins on GPIO6/GPIO7
  • add bridged passthrough controls, per-port monitoring, targeted ASCII/hex injection, and persisted serial settings/history support
  • refresh the userial web UI, mock data, generated web assets, and dev-server behavior for the new protocol
  • ignore generated dist/ output so local preview artifacts are not committed

Validation

  • python build/build_web.py --tool-dir tools/userial
  • python build/build_web.py --tool-dir tools/wifiscan
  • pio run -e xiao_esp32c3 (blocked by PlatformIO HTTPClientError while installing espressif32 in this environment)
  • sub-agent proofreading review completed and follow-up issues were fixed
  • parallel_validation completed with 0 CodeQL alerts

Copilot AI and others added 5 commits April 28, 2026 07:51
Agent-Logs-Url: https://github.com/bring42/tbx/sessions/2816ed76-19fb-4145-812b-d366a6770f48

Co-authored-by: bring42 <63049750+bring42@users.noreply.github.com>
Agent-Logs-Url: https://github.com/bring42/tbx/sessions/2816ed76-19fb-4145-812b-d366a6770f48

Co-authored-by: bring42 <63049750+bring42@users.noreply.github.com>
Agent-Logs-Url: https://github.com/bring42/tbx/sessions/2816ed76-19fb-4145-812b-d366a6770f48

Co-authored-by: bring42 <63049750+bring42@users.noreply.github.com>
Agent-Logs-Url: https://github.com/bring42/tbx/sessions/2816ed76-19fb-4145-812b-d366a6770f48

Co-authored-by: bring42 <63049750+bring42@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds dual-channel UART monitoring/injection plus optional bidirectional passthrough bridging to the tools/userial ESP32 web-UI tool, along with updated dev-server mocks and refreshed UI assets.

Changes:

  • Extend firmware to support two UART channels (A/B), passthrough modes, per-port counters, and on-device history buffering.
  • Refresh userial web UI to show per-port traffic, filtering, injection targeting, and passthrough controls (plus updated mocks).
  • Improve dev server behavior for the new WS protocol and ignore local dist/ build artifacts.

Reviewed changes

Copilot reviewed 9 out of 11 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tools/userial/web/mock_data.json Updates mock payload schema for dual-port status/settings/history.
tools/userial/web/config.json Renames tool title for the new dual-UART functionality.
tools/userial/web/app.js Implements dual-port monitoring, filtering, export, and targeted injection logic.
tools/userial/web/app.html Updates layout to show bridge status, per-port cards, and injection port selectors.
tools/userial/web/app.css Adds styles for port badges/cards and refreshed monitor/toolbar layout.
tools/userial/src/main.cpp Implements dual-UART firmware, passthrough forwarding, history ring buffer, and persisted serial settings.
tools/userial/src/_assembled.html Regenerated assembled HTML including updated UI/JS/CSS (build output).
build/dev_server.py Enhances mock WS server to emulate dual-port protocol + rewrites WS URL for split-port dev mode.
README.md Updates tool description to reflect dual-UART bridging capabilities.
.gitignore Ignores dist/ directories to avoid committing local build previews.

Comment thread tools/userial/web/app.js
Comment on lines +25 to +28
const entry = normalizeEntry({ ...data, dir: 'RX' });
addLogEntry(entry);
updateTrafficStats(data, true);
terminalAppend('[' + entry.port + '] ' + decodeDisplayText(entry.ascii) + '\n', 'rx-line');
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RX handler normalizes the entry before calling addLogEntry(), but addLogEntry() normalizes again. This duplicates work on every message and makes it easier for the two normalization paths to drift. Consider either passing raw WS data into addLogEntry() (and separately normalizing only for terminal output) or updating addLogEntry() to accept already-normalized entries.

Copilot uses AI. Check for mistakes.
Comment thread tools/userial/web/app.js
const entry = normalizeEntry({ ...data, dir: 'RX' });
addLogEntry(entry);
updateTrafficStats(data, true);
terminalAppend('[' + entry.port + '] ' + decodeDisplayText(entry.ascii) + '\n', 'rx-line');
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For RX events, decodeDisplayText(entry.ascii) converts preview sequences like "\n" back into real newlines, and then an additional "\n" is appended. If the payload preview already contains line endings, this produces extra blank lines in the terminal output (and reintroduces carriage returns). Consider either not decoding "\n"/"\r" for the terminal view, or only appending a newline when the decoded payload doesn’t already end with one.

Suggested change
terminalAppend('[' + entry.port + '] ' + decodeDisplayText(entry.ascii) + '\n', 'rx-line');
const rxText = decodeDisplayText(entry.ascii);
const rxLineEnding = /[\r\n]$/.test(rxText) ? '' : '\n';
terminalAppend('[' + entry.port + '] ' + rxText + rxLineEnding, 'rx-line');

Copilot uses AI. Check for mistakes.
Comment thread tools/userial/web/app.css
@@ -1,19 +1,101 @@
/* RS-232 Serial Tool — specific styles */
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file header comment still says “RS-232 Serial Tool — specific styles”, but the tool has been renamed/repurposed to dual-UART. Updating this comment will help keep generated assets (_assembled.html) and future maintenance aligned with the current tool behavior.

Suggested change
/* RS-232 Serial Tool — specific styles */
/* Dual-UART Tool — specific styles */

Copilot uses AI. Check for mistakes.
Comment on lines 544 to +545
Serial.println("\n RS-232 Serial Tool ready.");
Serial.println(" Waiting for serial data...\n");
Serial.printf(" Channel A RX/TX: GPIO%u/GPIO%u\n", channels[0].rxPin, channels[0].txPin);
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Startup log still prints “RS-232 Serial Tool ready.” even though the tool/UI/config have been renamed to Dual UART. Consider updating the Serial console banner so it matches the current tool name and avoids confusion when debugging.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +70
HardwareSerial SerialChannelA(1);
HardwareSerial SerialChannelB(0);
ChannelState channels[] = {
{"A", "Channel A", SERIAL_A_RX_PIN, SERIAL_A_TX_PIN, &SerialChannelA, 0, 0},
{"B", "Channel B", SERIAL_B_RX_PIN, SERIAL_B_TX_PIN, &SerialChannelB, 0, 0},
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Channel B is instantiated as HardwareSerial(0) and later initialized in beginChannel(). On ESP32 Arduino, Serial usually maps to UART0; reconfiguring UART0 (baud/pins) after Serial.begin(115200) can break the USB/serial console and any Serial logging used by toolbox_web/toolbox_wifi. Consider moving Channel B to a different UART index (e.g., 2) or explicitly separating the debug console from UART0 so Serial output remains stable.

Copilot uses AI. Check for mistakes.
Comment on lines +378 to +382
if (len == 0) return;
channels[channelIndex].uart->write(data, len);
channels[channelIndex].txBytes += len;
tbLedFlash();

Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbLedFlash() is called immediately after every UART write. Since tbLedFlash() uses delay() (blocking), this can throttle high-throughput serial traffic, increase latency, and potentially cause UART RX buffer overflows/data loss during passthrough. Prefer a non-blocking LED activity indicator (e.g., set a timestamp/flag and blink in tbLedBlink) or make LED flashing conditional/throttled.

Copilot uses AI. Check for mistakes.
Comment on lines +408 to +412
channels[peerIndex].uart->write(buffer, count);
channels[peerIndex].txBytes += count;
}
tbLedFlash();
storeHistory(channel.id[0], 'R', forwarded ? channels[peerIndex].id[0] : 0, buffer, count, ts);
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbLedFlash() is called during RX handling (and also when forwarding to the peer UART). Since tbLedFlash() blocks via delay(), sustained traffic can slow the loop enough to starve tbWifiLoop()/tbWebLoop() and increase the risk of UART buffer overflow/data loss. Consider replacing this with a non-blocking activity indicator or throttling flashes (e.g., max once per N ms).

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +74
HistoryEntry history[HISTORY_SIZE];
size_t historyHead = 0;
size_t historyCount = 0;
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description mentions persisted serial settings/history, but the firmware only persists serial settings (Preferences) while history is kept only in RAM (history[] ring buffer) and will be lost on reboot. If persistence across reboots is intended, history needs to be stored in Preferences/flash (with wear considerations); otherwise, update the PR description/UI copy to clarify that history is session-only.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants