155 lines
5.4 KiB
Bash
Executable File
155 lines
5.4 KiB
Bash
Executable File
#!/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_EOF
|
|
# Written by $(basename "$0") — see that script for why these matter.
|
|
pu port $PORT
|
|
pu baudrate $BAUD
|
|
pu bits 8
|
|
pu parity N
|
|
pu stopbits 1
|
|
pu rtscts No
|
|
pu xonxoff No
|
|
pu localecho Yes
|
|
pu addcarreturn Yes
|
|
PROFILE_EOF
|
|
)
|
|
# Rewrite whenever it differs, so a stale profile cannot quietly reintroduce
|
|
# any of the problems above.
|
|
if [ ! -f "$PROFILE" ] || [ "$(cat "$PROFILE")" != "$WANT" ]; then
|
|
printf '%s\n' "$WANT" > "$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"
|