Files
esp32-debug-dongle/README.md
T
2026-06-17 22:54:25 +10:00

255 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ESP32 Debug Dongle
A remote serial-debug bridge for ESP32 targets. It bridges a target device's UART to a
**web terminal**, **telnet**, and (on the original ESP32) **Bluetooth SPP** — and adds GPIO
reset/button control, NTP-dated SD logging, an OLED status page, and a **MeshCore LoRa** comms
panel for sniffing/sending channel traffic while you debug.
![ESP32 Debug Dongle web interface](debug_dongle.png)
*Web UI: serial terminal (left), SD log files (top), and the MeshCore comms panel (right).*
## Features
- **Web terminal** — browser serial terminal (xterm.js) over WebSocket
- **Telnet** (port 23) — primary remote path for `nc`/minicom or an agent
- **Bluetooth SPP** — Classic Bluetooth serial, original ESP32 only
- **Multi-port** — switch between internal-debug loopback, USB serial, and the external target UART
- **Target control** — pulse reset, pulse/latch a button (wake / force-on) from web, telnet, and REST
- **SD logging** (T3-S3) — NTP-dated logs of UART *and* MeshCore traffic, with a self-describing header
- **MeshCore panel** (T3-S3 + LoRa) — program a channel PSK at runtime, watch received messages, send messages
- **OLED status** (T3-S3) — IP, WiFi, UART, byte counts, log/SD/NTP state
## Build variants
The board and optional features are selected by PlatformIO environment (`platformio.ini`):
| Env | Board | SD | OLED | MeshCore LoRa | Bluetooth |
|-----|-------|----|------|---------------|-----------|
| `esp32dev` | generic ESP32 | | | | ✅ Classic SPP |
| `t3s3` | LilyGo T3-S3 (ESP32-S3) | ✅ | ✅ | | |
| `t3s3_mesh` | LilyGo T3-S3 (ESP32-S3 + SX1276) | ✅ | ✅ | ✅ | |
ESP32-S3 has no Classic Bluetooth — telnet + the web terminal replace SerialBT there.
`t3s3_mesh` extends `t3s3` and just adds the radio (`-DUSE_MESHCORE=1`); everything mesh-related
compiles to no-ops in the other builds, so the firmware and web UI are identical across all three.
## Hardware
### Pin connections — LilyGo T3-S3 (`t3s3` / `t3s3_mesh`)
Wire the target to the **right-hand header** — reset, TX, RX, and button are the top pins, with
GND a few pins down:
| T3-S3 GPIO | Function | Connect to target |
|------------|----------|-------------------|
| GPIO38 | Reset out (active-low pulse) | target RST |
| GPIO43 | TX | target RX |
| GPIO44 | RX | target TX |
| GPIO39 | Button out (active-low pulse/latch) | target button / wake |
| GND | Ground | target GND |
Onboard peripherals (already wired on the board, listed for reference): SD card on HSPI
(SCK 14, MISO 2, MOSI 11, CS 13); OLED on I2C (SDA 18, SCL 17); and in `t3s3_mesh` the SX1276
on FSPI (NSS 7, RST 8, DIO0 9, DIO1 33, SCLK 5, MISO 3, MOSI 6, RXEN 21, TXEN 10). The reset/
button polarity is active-low (`GPIO_CTRL_ACTIVE_LOW=1`); change it in `platformio.ini` if your
target is active-high.
### Pin connections — generic ESP32 (`esp32dev`)
External serial defaults to RX=GPIO16, TX=GPIO17, plus a common GND. No SD/OLED/LoRa.
## Quick Start
### 1. Install PlatformIO
```bash
pip install platformio # or use the VS Code PlatformIO IDE extension
```
### 2. Build & upload
Pick your environment with `-e`:
```bash
cd esp32-debug-dongle
# Firmware
pio run -e t3s3_mesh -t upload # or: -e t3s3 / -e esp32dev
# Web files (LittleFS) -- needed on first flash and after any data/ change
pio run -e t3s3_mesh -t uploadfs
# Serial monitor
pio device monitor
```
> **MeshCore note:** `t3s3_mesh` pulls the MeshCore library from a local checkout
> (`symlink:///home/scottp/github/MeshCore`) plus RadioLib / Crypto / RTClib / base64 / ed25519.
> Adjust that path in `platformio.ini` to wherever your MeshCore checkout lives.
### 3. Connect
The dongle joins your WiFi in station mode (SSID/password are build flags in `platformio.ini`).
If it can't join, it falls back to an access point:
- **AP SSID:** `ESP32-DebugDongle` **Password:** `debug1234` → open `http://192.168.4.1`
On a successful station join, the device prints its IP on the USB serial monitor and on the OLED:
```
[Ready] http://10.0.1.241 telnet 10.0.1.241 23
```
#### Bluetooth (`esp32dev` only)
Pair with `ESP32-Debug`, then use any BT serial terminal (Android "Serial Bluetooth Terminal",
Windows PuTTY on the COM port, Linux `rfcomm`, macOS `/dev/tty.ESP32-Debug`).
## Usage
### Web interface
- **Port** — Internal (debug loopback) / USB Serial / External (target UART)
- **Baud** — 9600…921600
- **Reset / Button** — momentary pulse of the target reset / button lines
- **Hold** — latch the button line held active (force-on) until released
- **Clear / Reconnect** — terminal + WebSocket
- **Log / Files** — toggle SD logging and browse/download/delete log files (T3-S3)
- **MeshCore panel** — program a channel + PSK, view RX/TX messages, send a message
### Serial ports
| Port | Description | Use case |
|------|-------------|----------|
| Internal | Virtual loopback buffer | the dongle's own debug output |
| USB Serial | UART0 (shared with USB) | console |
| External | Serial1 (target UART pins) | the device under test |
### Telnet (port 23)
Any line is forwarded verbatim to the target UART **unless** it starts with `~`, in which case
it's a dongle command:
```
~help list commands
~status port/baud/counters/log/ntp/heap
~reset [ms] pulse the reset line (default 200 ms)
~button [ms|on|off] pulse the button line, or latch it on/off (force-on)
~baud <n> set target baud
~port <int|usb|ext> select the active port
~log on|off SD logging (T3-S3)
~gpio <pin> <0|1> drive an arbitrary GPIO
~mesh [on|off] mesh status / toggle echo of mesh msgs to telnet
~psk <base64key> reprogram the user channel's PSK (16- or 32-byte key)
~chan <name> <base64key> set channel name + PSK together
~msg <text> send a message on the user channel
```
### REST API
Mirrors the telnet commands:
```
/api/status /api/reset?ms= /api/button?ms= | ?latch=on|off
/api/baud?baud= /api/port?port=int|usb|ext
/api/log?on=0|1 /api/gpio?pin=&val= /api/send?data=
/api/logs /api/logfile?name= /api/logdelete?name= (T3-S3)
```
## MeshCore comms panel (`t3s3_mesh`)
The right-hand panel talks to a MeshCore `BaseChatMesh` node on the SX1276 radio. It listens on
the well-known `Public` channel plus one **user channel** (default `SensorsTest`). You can:
- **Program** a new channel name + PSK at runtime (base64-encoded 16- or 32-byte key). The PSK is
persisted to NVS, so it survives reboots, and the change is recorded in the SD log.
- **Watch** received channel messages (sender, text, RSSI/SNR).
- **Send** a message on the user channel.
The same actions are available over telnet (`~psk`, `~chan`, `~msg`, `~mesh`). LoRa PHY defaults
are Australia-narrow (916.575 MHz, BW 62.5, SF7, CR8, 20 dBm) — change them in `platformio.ini`.
## SD logging (T3-S3)
Logs are written to NTP-dated files under `/logs/` on the SD card. Each file opens with a
self-describing header and captures both the target UART stream and MeshCore RX/TX, timestamped
per line:
```
# debug-dongle log opened /logs/20260616-142348.log
# uart: port=external baud=115200 rx=44 tx=43
# mesh: up node=dongle channel=SensorsPH psk=<base64> freq=916.575 bw=62.5 sf=7 cr=8 tx=20
[14:24:15] [mesh rx batcave] device=ScottTrailer batt=13.11 ...
[14:26:36] sensor boot #1 ...
```
This is a low-level debugger, so the channel PSK is logged in plaintext on purpose — the card
carries the key. Browse/download/delete logs from the **Files** panel or the `/api/logs*`
endpoints. Toggle with the **Log** button or `~log on|off`.
## WebSocket protocol
Endpoint: `ws://<device-ip>/ws`
- **Binary frames** — raw target serial data (both directions).
- **Text frames** — control/event JSON, prefixed with a `0x00` byte. Events carry a `type`:
`status`, `mesh` (a received/sent message), or `meshcfg` (channel/PSK state).
Browser → device commands (sent as `0x00` + JSON):
```javascript
{ "cmd": "setPort", "port": 2 } // 0=Internal, 1=USB, 2=External
{ "cmd": "setBaud", "baud": 115200 }
{ "cmd": "getStatus" }
{ "cmd": "meshSend", "text": "hello" }
{ "cmd": "meshPsk", "name": "SensorsTest", "psk": "PNtgMxiq9R7eQ3IleHoL3g==" }
{ "cmd": "meshGet" }
```
## Configuration
Most settings are **build flags** in `platformio.ini` (per environment): WiFi `STA_SSID` /
`STA_PASSWORD`, target UART pins (`TARGET_RX_PIN` / `TARGET_TX_PIN`), control pins
(`GPIO_RESET_PIN` / `GPIO_WAKE_PIN` / `GPIO_CTRL_ACTIVE_LOW`), SD/OLED/LoRa pins, the LoRa PHY,
and the default mesh channel (`SENSORS_CHANNEL_NAME` / `SENSORS_CHANNEL_PSK_B64`). The AP SSID,
Bluetooth name, and default bauds live near the top of `src/main.cpp`.
## Project structure
```
esp32-debug-dongle/
├── platformio.ini # build envs: esp32dev / t3s3 / t3s3_mesh
├── src/
│ ├── main.cpp # bridge: UART <-> web/telnet/BT, control, SD log, OLED
│ ├── meshcore_link.{h,cpp} # MeshCore node facade (no-op unless USE_MESHCORE)
│ └── LoopbackStream.{h,cpp} # internal virtual serial
├── data/
│ └── index.html # web UI (uploaded to LittleFS)
├── scripts/
│ └── download_xterm.py # optional: host xterm.js locally instead of CDN
└── README.md
```
## Troubleshooting
- **Web page won't load** — upload the filesystem (`pio run -e <env> -t uploadfs`); use `http://`, not `https`.
- **No serial data** — check baud, swap TX/RX, ensure a common ground.
- **SD "not found" / CRC errors** — confirm the SD pins, and on `t3s3_mesh` that the radio is on
FSPI and SD on HSPI (they must be different SPI peripherals).
- **MeshCore: nothing received** — confirm the channel PSK matches the sender's, and the LoRa PHY
(freq/BW/SF/CR) matches the fleet.
- **Terminal lines "staircase"** — already handled (`convertEol`); the raw SD log keeps bare `\n`.
- **Bluetooth won't pair** — original ESP32 only (`esp32dev`); not on S3.
## License
MIT License — feel free to use and modify.
## Credits
- [xterm.js](https://xtermjs.org/) — terminal emulator
- [ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) — async web server
- [ArduinoJson](https://arduinojson.org/) — JSON library by Benoît Blanchon
- [MeshCore](https://github.com/meshcore-dev/MeshCore) — LoRa mesh + RadioLib radio drivers