#!/usr/bin/env bash # # Open the TranspOtter serial console. # # ./console.sh minicom, port auto-detected # ./console.sh /dev/ttyACM1 force a port # ./console.sh -m use `pio device monitor` instead # ./console.sh -r raw stty/cat fallback, no terminal program # ./console.sh -l list candidate ports and exit # # Exit minicom with Ctrl-A X. set -euo pipefail PROFILE_NAME=transpotter PROFILE="$HOME/.minirc.$PROFILE_NAME" BAUD=115200 MODE=minicom PORT="${TRANSPOTTER_PORT:-}" # Print the comment block at the top of this file, stopping at the first line # of actual code, so the help text cannot drift out of sync with it. usage() { awk 'NR>2 && /^#/ { sub(/^# ?/, ""); print; next } NR>2 { exit }' "$0"; } while [ $# -gt 0 ]; do case "$1" in -m|--monitor) MODE=monitor ;; -r|--raw) MODE=raw ;; -l|--list) MODE=list ;; -h|--help) usage; exit 0 ;; -*) echo "unknown option: $1" >&2; usage >&2; exit 2 ;; *) PORT="$1" ;; esac shift done # --- find the board ---------------------------------------------------------- # by-id first: the name is stable across replugs and unambiguous when several # USB serial devices are attached. Falls back to matching Espressif's USB vendor # id (0x303a) in sysfs, then to the usual device node. list_ports() { local found=0 for l in /dev/serial/by-id/*Espressif*; do [ -e "$l" ] && { echo "$l"; found=1; } done for d in /sys/class/tty/ttyACM* /sys/class/tty/ttyUSB*; do [ -e "$d" ] || continue [ "$(cat "$d/device/../idVendor" 2>/dev/null)" = "303a" ] || continue echo "/dev/$(basename "$d")"; found=1 done [ "$found" -eq 0 ] && [ -e /dev/ttyACM0 ] && echo /dev/ttyACM0 return 0 } if [ "$MODE" = list ]; then echo "candidate ports:"; list_ports | sort -u | sed 's/^/ /' exit 0 fi if [ -z "$PORT" ]; then PORT="$(list_ports | head -1 || true)" fi if [ -z "$PORT" ] || [ ! -e "$PORT" ]; then echo "error: no serial port found." >&2 echo " Plug the ESP32 in, or pass one explicitly: $0 /dev/ttyACM0" >&2 echo " '$0 -l' lists candidates." >&2 exit 1 fi if [ ! -w "$PORT" ]; then echo "error: $PORT is not writable. You likely need to be in the 'plugdev'" >&2 echo " or 'dialout' group: sudo usermod -aG plugdev \$USER (then re-login)." >&2 exit 1 fi # A second reader steals half the bytes and makes the console look broken — and # blocks `pio run -t upload` outright. fuser/lsof are often not installed, so # walk /proc instead: our own processes' fds are always readable. port_holders() { local target pid target="$(readlink -f "$1" 2>/dev/null)" || return 0 for l in /proc/[0-9]*/fd/*; do [ -e "$l" ] || continue [ "$(readlink -f "$l" 2>/dev/null)" = "$target" ] || continue pid="${l#/proc/}"; pid="${pid%%/*}" [ "$pid" = "$$" ] && continue echo "$pid $(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null)" done | sort -u } HOLDERS="$(port_holders "$PORT")" if [ -n "$HOLDERS" ]; then echo "warning: something else already has $PORT open:" >&2 printf '%s\n' "$HOLDERS" | sed 's/^/ /' >&2 echo " close it first — two readers split the bytes between them," >&2 echo " and an upload cannot open the port at all." >&2 fi # --- alternate modes --------------------------------------------------------- case "$MODE" in monitor) exec pio device monitor -p "$PORT" -b "$BAUD" ;; raw) echo "raw mode on $PORT — type a command and press Enter, Ctrl-C to quit." stty -F "$PORT" "$BAUD" raw -echo -crtscts cat "$PORT" & CATPID=$! trap 'kill $CATPID 2>/dev/null || true' EXIT while IFS= read -r line; do printf '%s\n' "$line" > "$PORT"; done exit 0 ;; esac # --- minicom ----------------------------------------------------------------- command -v minicom >/dev/null 2>&1 || { echo "error: minicom is not installed. Try '$0 -m' (pio device monitor)" >&2 echo " or '$0 -r' (raw), or: sudo apt install minicom" >&2 exit 1 } # Three minicom defaults make this board awkward, so the profile is written # rather than assumed: # rtscts No - the C3's USB CDC never asserts CTS, and minicom defaults # hardware flow control ON, silently blocking every keystroke. # localecho - the firmware does not echo, so without this nothing appears # as you type and the terminal seems unresponsive. # addcarreturn - turns a bare LF into CR+LF on the way in. Firmware that # prints "\n" without "\r" otherwise staircases off the right # of the screen. Harmless when the firmware already sends # CR+LF, so it is on regardless. WANT=$(cat < "$PROFILE" echo "wrote $PROFILE" fi echo "connecting to $PORT at $BAUD (Ctrl-A X to quit, Ctrl-A Z for help)" echo "note: opening the port resets the ESP32 — expect the boot banner." exec minicom "$PROFILE_NAME" -D "$PORT"