|
| 1 | +# SerialNet Debug Studio |
| 2 | + |
| 3 | +<p align="center"> |
| 4 | + <img src="images/logo.png" alt="SerialNet Debug Studio logo" width="180"> |
| 5 | +</p> |
| 6 | + |
| 7 | +**[简体中文 README →](README.md)** |
| 8 | + |
| 9 | +A locally hosted web-based debugger: the browser provides the UI and live charts; a **FastAPI** backend handles **serial / TCP / UDP** I/O. **WebSockets** push logs and parsed numeric data—no database or login required. |
| 10 | + |
| 11 | +**SerialNet Debug Studio** is a local **serial monitor**, **TCP/UDP debugging tool**, and **serial plotter** style dashboard for **embedded development**. It provides protocol-friendly sending, live logs, named multi-channel charts, HEX input, checksum options, and a browser-based UI that can serve as an enhanced alternative to **Arduino Serial Monitor** and **Arduino Serial Plotter** for many workflows. |
| 12 | + |
| 13 | +## Use cases / Search intent |
| 14 | + |
| 15 | +- **Serial monitor** for COM / UART device logging and command sending |
| 16 | +- **TCP debugging tool** for local TCP client bring-up and device testing |
| 17 | +- **UDP debugging tool** for packet send/receive and local listening |
| 18 | +- **Serial plotter** style visualization for sensor, ADC, telemetry, and lab data |
| 19 | +- **Embedded debugging tool** for Arduino, ESP32, STM32, MCUs, and gateways |
| 20 | +- **Local web-based debugger** with Python backend, browser UI, no cloud account, no database |
| 21 | + |
| 22 | +## If you are looking for |
| 23 | + |
| 24 | +- a local tool that supports **Serial / TCP / UDP** |
| 25 | +- a **serial monitor** with **HEX send**, **line ending control**, and **checksums** |
| 26 | +- a browser-based alternative to **Arduino Serial Monitor** |
| 27 | +- a lightweight multi-channel **serial plotter** for named fields |
| 28 | +- an **embedded debugging dashboard** with both logs and live charts |
| 29 | + |
| 30 | +this project may fit your workflow. |
| 31 | + |
| 32 | +## Why this project |
| 33 | + |
| 34 | +When debugging embedded work, the **Arduino IDE** Serial Monitor is still oriented largely around **USB / Bluetooth**-style links; it is awkward for **Wi‑Fi** stacks (**TCP/UDP**). The console is crowded and hard to read, and outbound traffic is not easy to **tailor** (checksums, line endings, hex payloads, and so on). The **Serial Plotter** is handy but limited in protocol and workflow. **Serial Studio Pro** is capable but heavy for everyday use, and even straightforward network use can be paywalled. |
| 35 | + |
| 36 | +**SerialNet Debug Studio** sits in between: one **local web UI** with **serial + TCP + UDP**, clearer logs, **ECharts** multi-series plots, and a send path you can shape for real protocols—no cloud account, no database—aimed at lighter-weight embedded bring-up and field debugging. |
| 37 | + |
| 38 | +## Compared with common tools |
| 39 | + |
| 40 | +- Compared with **Arduino Serial Monitor**: it supports **TCP / UDP** in addition to serial. |
| 41 | +- Compared with **Arduino Serial Plotter**: it adds **named channels**, multi-series charts, and protocol-oriented sending controls. |
| 42 | +- Compared with heavier desktop suites: it stays **local-first, lightweight, and browser-based**. |
| 43 | +- Compared with tiny packet tools: it adds **charting, send history, checksums, and multilingual UI**. |
| 44 | + |
| 45 | +## Architecture and screenshots |
| 46 | + |
| 47 | +### Architecture diagram |
| 48 | + |
| 49 | +The diagram below summarizes the full flow: **Serial / TCP / UDP** devices on the left, the **FastAPI** backend with connection management, parsing, and **WebSocket** updates in the middle, and the browser-side panels for connection, logs, charts, and sending on the right. |
| 50 | + |
| 51 | + |
| 52 | + |
| 53 | +### UI screenshots |
| 54 | + |
| 55 | +The table below shows the most representative UI views in a compact GitHub-friendly layout. |
| 56 | + |
| 57 | +| Serial connection and live chart | TCP debugging view | |
| 58 | +|---|---| |
| 59 | +| In serial mode, the left side shows live logs while the right side renders dynamic multi-series charts from parsed channels, which is useful for sensors, ADC values, and other embedded telemetry.<br><br> | In TCP client mode, you can connect to a target host and port, inspect logs, send commands, and watch the chart in the same browser UI.<br><br> | |
| 60 | + |
| 61 | +| Chart box zoom | Send history | |
| 62 | +|---|---| |
| 63 | +| The chart panel supports box selection so you can zoom into a local time range and inspect waveform details more closely.<br><br> | The send area keeps a reusable command history so common debug commands can be filled back quickly during repetitive testing.<br><br> | |
| 64 | + |
| 65 | +## Feature summary |
| 66 | + |
| 67 | +| Area | Description | |
| 68 | +|------|-------------| |
| 69 | +| Transport | **Serial (COM)** / **TCP client** / **UDP** (send + optional local listen). Only one active connection at a time. | |
| 70 | +| Log | Timestamps normalized in the UI (UTC ISO, consistent with `SYS`). Channels `[RX]` / `[TX]` / `[SYS]` / `[ERR]`, auto-scroll; max line count is listed under tunables below. | |
| 71 | +| Chart | **ECharts** multi-series lines; point cap below. Series appear **dynamically** from parsed data (Arduino Serial Plotter–style). Time axis can show **HH:MM:SS.mmm** locally. Pause/resume, zoom reset, copy/export CSV. | |
| 72 | +| Send | **Text / HEX** modes; **line ending** (None / LF / CR / CRLF); several **checksum** algorithms (XOR-8, CRC variants, MOD-256, Adler-32, Fletcher-16). Checksum covers payload only; bytes go between payload and line ending. Optional **TX echo** checkbox (append `[TX]` on success). **Enter** sends. History **↑ / ↓** plus popover list and **Clear history** (local storage only). Send row and format row disabled when disconnected. | |
| 73 | +| i18n | **8 UI languages** (zh / en / ja / ko / de / fr / es / pt-BR); see `static/i18n.js`. | |
| 74 | +| TCP | **Host/Port** remembered locally; **last 3** successful connects as quick chips. | |
| 75 | +| Link state | **WebSocket** plus periodic **GET `/api/status`** keep UI in sync so a broken WS alone does not show “connected” incorrectly. | |
| 76 | + |
| 77 | +## Tech stack |
| 78 | + |
| 79 | +- Python **3.10+** (3.11+ recommended) |
| 80 | +- **FastAPI** + **Uvicorn** |
| 81 | +- **WebSocket** (Starlette) |
| 82 | +- **pyserial** (port list + I/O) |
| 83 | +- Front end: **plain HTML / CSS / JavaScript**, **Apache ECharts** (CDN), i18n in **`static/i18n.js`** and app logic in **`static/app.js`** |
| 84 | + |
| 85 | +## Repository layout |
| 86 | + |
| 87 | +``` |
| 88 | +SerialNet-Debug-Studio/ |
| 89 | +├── app.py # Uvicorn shim: adds `src` to path, exports `app` |
| 90 | +├── src/ |
| 91 | +│ └── serialnet_debug_studio/ |
| 92 | +│ ├── app.py # FastAPI app + static mount (serves repo-root `static/`) |
| 93 | +│ ├── connection_manager.py |
| 94 | +│ ├── parser.py |
| 95 | +│ └── transports/ |
| 96 | +│ ├── base_transport.py |
| 97 | +│ ├── serial_transport.py |
| 98 | +│ ├── tcp_transport.py |
| 99 | +│ └── udp_transport.py |
| 100 | +├── static/ |
| 101 | +│ ├── index.html |
| 102 | +│ ├── i18n.js |
| 103 | +│ ├── app.js |
| 104 | +│ └── style.css |
| 105 | +├── images/ # Logo, architecture figure, and README screenshots |
| 106 | +│ ├── logo.png |
| 107 | +│ ├── Architecture diagram.png |
| 108 | +│ └── ... |
| 109 | +├── scripts/ # Ad-hoc local test helpers (not pytest) |
| 110 | +│ ├── tcp_test_server.py |
| 111 | +│ ├── udp_sender.py |
| 112 | +│ └── serial_mock.py |
| 113 | +├── requirements.txt |
| 114 | +├── LICENSE |
| 115 | +├── NOTICE |
| 116 | +├── README.md |
| 117 | +└── README.en.md |
| 118 | +``` |
| 119 | + |
| 120 | +## Quick start |
| 121 | + |
| 122 | +```bash |
| 123 | +cd SerialNet-Debug-Studio |
| 124 | +python -m pip install -r requirements.txt |
| 125 | +uvicorn app:app --host 127.0.0.1 --port 8000 |
| 126 | +# Equivalent: uvicorn serialnet_debug_studio.app:app --app-dir src --host 127.0.0.1 --port 8000 |
| 127 | +``` |
| 128 | + |
| 129 | +Open **http://127.0.0.1:8000/** in a browser. The page connects to **`/ws`** automatically. |
| 130 | + |
| 131 | +## Defaults (roughly match the UI) |
| 132 | + |
| 133 | +| Item | Default | |
| 134 | +|------|---------| |
| 135 | +| Mode | TCP (`localStorage` can restore last mode when idle) | |
| 136 | +| TCP | `192.168.1.100:5000` | |
| 137 | +| Serial | 115200, 8N1, read timeout 0.2 s | |
| 138 | +| UDP remote | `192.168.1.100:5001`; example local listen `5001` | |
| 139 | +| Line ending | **LF** | |
| 140 | + |
| 141 | +## HTTP API |
| 142 | + |
| 143 | +| Method | Path | Notes | |
| 144 | +|--------|------|--------| |
| 145 | +| GET | `/api/ports` | `{ "ports": ["COM3", ...] }` | |
| 146 | +| POST | `/api/connect` | JSON: `mode` is `serial` / `tcp` / `udp` plus the matching config object (same field names as front-end `buildConnectBody`, e.g. `serial.port`, `tcp.host`, …). | |
| 147 | +| POST | `/api/disconnect` | Drop the active connection. | |
| 148 | +| POST | `/api/send` | One of: **①** `{ "bytes_b64": "<Base64>" }` — send **raw bytes**, **no** auto newline, **no** extra server-side `TX` log over WS (matches the SPA “advanced send”). **②** `{ "text": "..." }` (or `line`) — UTF-8; if the string does **not** end with `\n`, the server **appends** `\n` and emits a `TX` log message over WS. | |
| 149 | +| GET | `/api/status` | `{ "state", "mode", "detail" }` with `state` ∈ `disconnected` \| `connecting` \| `connected` \| `error`. | |
| 150 | + |
| 151 | +## WebSocket `/ws` |
| 152 | + |
| 153 | +Server → client JSON: |
| 154 | + |
| 155 | +| `type` | Purpose | |
| 156 | +|--------|---------| |
| 157 | +| `status` | Same fields as `/api/status` when connection state changes. | |
| 158 | +| `log` | `channel` (e.g. `RX` / `TX` / `SYS` / `ERR`), `message`, `ts` (UTC ISO; the UI formats like local `SYS` lines). | |
| 159 | +| `parsed_data` | `values`: numeric map, `raw_line`: source line (may be omitted if nothing numeric parsed). | |
| 160 | + |
| 161 | +The client may send arbitrary text keep-alives; the server reads and discards them. |
| 162 | + |
| 163 | +## RX / chart line protocol |
| 164 | + |
| 165 | +### Framing and encoding |
| 166 | + |
| 167 | +- Bytes buffer until **LF (`\n`)** completes a frame; bytes before that LF are UTF-8 decoded (invalid bytes replaced), then **`rstrip('\r\n')`** removes trailing CR/LF from the line body, so lines may end in **CRLF** or **LF** only. |
| 168 | +- The full line is passed to `src/serialnet_debug_studio/parser.py`. **Bare numbers without `=` / `:` are not treated as channels** (unlike Arduino Serial Plotter–style comma-only numeric CSV). |
| 169 | + |
| 170 | +### Per-line parse shape (what reaches the chart) |
| 171 | + |
| 172 | +- Fields are split by ASCII **`,`**. Each field must be **`key=value`** or **`key:value`** (only the **first** `=` or `:` in that field splits key and value). |
| 173 | +- **Key**: non-empty after trim; becomes the **series / legend name** (case-sensitive). |
| 174 | +- **Value**: after trim, must parse as a number per that `parser.py`: |
| 175 | + - If it contains **`.`** or **`e` / `E`**, parse as float; |
| 176 | + - else if it matches **`±0x…`**, parse as hexadecimal int; |
| 177 | + - else parse as decimal int; |
| 178 | + - on failure, try float once more. |
| 179 | +- Bad segments are skipped. If **no** numeric key/value pair survives, **`parsed_data` is not sent** (no new chart point), but the line still appears as **`[RX]`** in the log. |
| 180 | + |
| 181 | +### Chart behavior (`parsed_data` → ECharts) |
| 182 | + |
| 183 | +- **X axis**: each non-empty `values` payload adds **one** sample; **X is browser local time in ms when the message arrives**, not an embedded device timestamp unless you later add an explicit convention. |
| 184 | +- **Multi-series**: keys on the same line share that X; **series that appeared before but are missing on a later line get `null` at that X** (gaps in the line). New keys add series dynamically. |
| 185 | + |
| 186 | +**Examples (each line ends with `\n`):** |
| 187 | + |
| 188 | +```text |
| 189 | +temp=23.5,hum=55 |
| 190 | +adc0:1023, adc1:512, flags=0x01 |
| 191 | +x=1.2e-3, y=-4 |
| 192 | +``` |
| 193 | + |
| 194 | +## Front-end send pipeline (`static/app.js`) |
| 195 | + |
| 196 | +- When **connected**, the SPA posts **`bytes_b64`** to **`/api/send`** for all modes (Serial / TCP / UDP). |
| 197 | +- Frame order: **payload** (UTF-8 text or HEX bytes) → **checksum bytes** (payload only) → **line ending** (LF/CR/CRLF as UTF-8). |
| 198 | +- HEX: supports `AA 55 01` and `AA550102`; invalid input logs `[ERR]`, no modal dialogs. |
| 199 | + |
| 200 | +## Browser `localStorage` (front end only) |
| 201 | + |
| 202 | +| Key | Purpose | |
| 203 | +|-----|---------| |
| 204 | +| `webdbg_ui_lang_v1` | UI language | |
| 205 | +| `webdbg_cmd_history_v1` | Send history, up to 100 entries | |
| 206 | +| `webdbg_tcp_recent_v1` | Last 3 successful TCP connects | |
| 207 | +| `webdbg_tcp_form_v1` | Last Host / Port fields | |
| 208 | +| `webdbg_ui_mode_v1` | Last transport mode (Serial/TCP/UDP) | |
| 209 | +| `webdbg_tx_echo_v1` | Whether TX echo to log is enabled | |
| 210 | + |
| 211 | +## Tunables (`static/app.js`, etc.) |
| 212 | + |
| 213 | +| Constant | Default | Meaning | |
| 214 | +|----------|---------|---------| |
| 215 | +| `MAX_LOG_LINES` | 10000 | Log DOM rows; oldest dropped when over limit | |
| 216 | +| `MAX_POINTS` | 1000 | Chart sample cap | |
| 217 | +| `MAX_CMD_HIST` | 100 | Send history cap | |
| 218 | +| `MAX_TCP_RECENT` | 3 | TCP “recent” chip cap | |
| 219 | +| Status poll | 3000 ms | Poll `/api/status` while connecting or connected | |
| 220 | + |
| 221 | +Badges near titles show **current / max**; styling hints when near or at the limit. |
| 222 | + |
| 223 | +## Static asset caching |
| 224 | + |
| 225 | +If the browser caches scripts/CSS aggressively, bump the **`?v=`** query on `i18n.js` / `app.js` / `style.css` in **`static/index.html`**. |
| 226 | + |
| 227 | +## Local testing |
| 228 | + |
| 229 | +1. **TCP** — Terminal A: `python scripts/tcp_test_server.py --port 5000`. Terminal B: run `uvicorn`, connect to `127.0.0.1:5000` in the UI. |
| 230 | + |
| 231 | +2. **UDP** — Enable listen and set the local port in the UI; terminal: `python scripts/udp_sender.py --host 127.0.0.1 --port <port>`. |
| 232 | + |
| 233 | +3. **Serial** — On Windows, tools like **com0com** give a virtual pair; run `python scripts/serial_mock.py --port COMx` on one side and connect from the UI on the other. |
| 234 | + |
| 235 | +## FAQ |
| 236 | + |
| 237 | +### Can this tool replace Arduino Serial Monitor? |
| 238 | + |
| 239 | +Yes. It covers serial logging and sending, while also adding **TCP / UDP**, HEX send, checksum options, line ending control, and browser-based live charts. |
| 240 | + |
| 241 | +### Does it support serial plotter style visualization? |
| 242 | + |
| 243 | +Yes. Parsed numeric fields are rendered as dynamic multi-series charts keyed by channel name, which works well for sensors, ADC values, and telemetry streams. |
| 244 | + |
| 245 | +### Can I use it for TCP or UDP debugging? |
| 246 | + |
| 247 | +Yes. The same UI supports **TCP client** mode and **UDP send/listen** workflows in addition to serial communication. |
| 248 | + |
| 249 | +### Does it work offline? |
| 250 | + |
| 251 | +Yes. It is a **local-first** tool that runs on your machine and does not require a cloud account, database, or login. |
| 252 | + |
| 253 | +### What kinds of devices or projects is it suitable for? |
| 254 | + |
| 255 | +It is a good fit for **Arduino**, **ESP32**, **STM32**, serial modules, Wi-Fi modules, sensor gateways, and other embedded systems where you want logs and live numeric visualization in the same tool. |
| 256 | + |
| 257 | +## Notes |
| 258 | + |
| 259 | +- The browser **cannot** talk to hardware serial ports directly; the local Python process does all I/O. |
| 260 | +- Shutting down the app or `uvicorn` releases serial ports and sockets via lifespan cleanup. |
| 261 | +- Pin versions in **`requirements.txt`** for reproducibility; patch levels follow your environment if unpinned. |
| 262 | + |
| 263 | +## License |
| 264 | + |
| 265 | +This project (**SerialNet Debug Studio**) is licensed under the **Apache License, Version 2.0**. See [`LICENSE`](LICENSE) for the full text; a short attribution summary is in [`NOTICE`](NOTICE). |
| 266 | + |
| 267 | +- **Copyright © 2026 Allen Liao** |
| 268 | +- Third-party dependencies (e.g. FastAPI, Uvicorn, pyserial) and front-end CDN assets remain under their respective licenses. |
0 commit comments