From fae9030a203bd912daf04e2db0682a4524b08ead Mon Sep 17 00:00:00 2001 From: Gaston Gonzalez Date: Fri, 28 Feb 2025 05:55:44 -0700 Subject: [PATCH 1/5] BT Terminal - Added socat command to support exposing the rfcomm device over TCP. --- scripts/install-base.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/install-base.sh b/scripts/install-base.sh index 86c36ec..44e6f31 100755 --- a/scripts/install-base.sh +++ b/scripts/install-base.sh @@ -2,7 +2,7 @@ # # Author : Gaston Gonzalez # Date : 16 March 2024 -# Updated : 11 February 2024 +# Updated : 28 February 2025 # Purpose : Install base tools and configuration set -e @@ -25,6 +25,7 @@ apt install \ openjdk-20-jdk \ openssh-server \ screen \ + socat \ steghide \ stow \ xsel \ From 04d11e13069dcb7535d3f95c67ac0177cade97b0 Mon Sep 17 00:00:00 2001 From: Gaston Gonzalez Date: Sat, 15 Mar 2025 07:43:27 -0700 Subject: [PATCH 2/5] UV Pro - Re-implemented BTECH UV Pro command to support three modes following a connection: axcall, QtTermTCP and Winlink --- overlay/etc/skel/.config/QtTermTCP.ini | 6 +- overlay/etc/sudoers.d/et-rfcomm | 2 + overlay/opt/emcomm-tools/bin/et-kill-bt | 40 ++++ overlay/opt/emcomm-tools/bin/et-mode | 16 +- overlay/opt/emcomm-tools/bin/et-mode-bt | 128 ++++++++++++ overlay/opt/emcomm-tools/bin/et-qttermtcp | 9 +- overlay/opt/emcomm-tools/bin/et-uv-pro | 233 ++++++++++++++-------- 7 files changed, 330 insertions(+), 104 deletions(-) create mode 100755 overlay/opt/emcomm-tools/bin/et-kill-bt create mode 100755 overlay/opt/emcomm-tools/bin/et-mode-bt diff --git a/overlay/etc/skel/.config/QtTermTCP.ini b/overlay/etc/skel/.config/QtTermTCP.ini index 6732810..01b0ad8 100644 --- a/overlay/etc/skel/.config/QtTermTCP.ini +++ b/overlay/etc/skel/.config/QtTermTCP.ini @@ -5,7 +5,11 @@ AGWMonEnable=0 AGWPaclen=80 AGWPort=8000 AGWTermCall=N0CALL -AGWToCalls=TTPBBS, +AGWToCalls=TTPBBS +KISSEnable=1 +KISSMode=0 +KISSSerialPort=rfcomm0 +MYCALL=N0CALL PTT=HAMLIB PTTBAUD=0 PTTMode=17 diff --git a/overlay/etc/sudoers.d/et-rfcomm b/overlay/etc/sudoers.d/et-rfcomm index 1d88dbf..9836520 100644 --- a/overlay/etc/sudoers.d/et-rfcomm +++ b/overlay/etc/sudoers.d/et-rfcomm @@ -4,3 +4,5 @@ %bluetooth ALL=(ALL) NOPASSWD: /usr/bin/killall kissattach %bluetooth ALL=(ALL) NOPASSWD: /usr/bin/killall rfcomm + +%bluetooth ALL=(ALL) NOPASSWD: /opt/emcomm-tools/bin/et-kill-bt diff --git a/overlay/opt/emcomm-tools/bin/et-kill-bt b/overlay/opt/emcomm-tools/bin/et-kill-bt new file mode 100755 index 0000000..7233633 --- /dev/null +++ b/overlay/opt/emcomm-tools/bin/et-kill-bt @@ -0,0 +1,40 @@ +#!/bin/bash +# Author : Gaston Gonzalez +# Date : 14 March 2025 +# UPdated : 15 March 2025 +# Purpose : Stops all services tied to Bluetooth TNC operations +# +# Post Conditions: +# 1. All services started for supporting Bluetooth over serial are stopped. +# +# Note: If you try to re-establish a Bluetooth connection, you will needed to: +# 1. Turn off and turn back on your radio. +# 2. Wait 30 seconds before reconnecting (i.e. ./et-uv-pro connect) + +source /opt/emcomm-tools/bin/et-common + +stop_all_bt_services() { + + PS_OUT=$(ps -ef | grep "[k]issattach"); + if [[ $? -eq 0 ]]; then + echo "Killing kissattach processes" + killall kissattach + fi + + PS_OUT=$(ps -ef | grep "[r]fcomm0"); + if [[ $? -eq 0 ]]; then + ps -ef | grep "[r]fcomm0" | awk '{print $2}' | while read pid; do + echo "Killing rfcomm process: $pid" + kill $pid + done + + echo -e "\n${YELLOW}Wait 30 seconds before reconnecting over Bluetooth${NC}\n" + fi +} + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root!" + exit 1 +fi + +stop_all_bt_services diff --git a/overlay/opt/emcomm-tools/bin/et-mode b/overlay/opt/emcomm-tools/bin/et-mode index 1f997a9..b7f84d0 100755 --- a/overlay/opt/emcomm-tools/bin/et-mode +++ b/overlay/opt/emcomm-tools/bin/et-mode @@ -2,7 +2,7 @@ # # Author : Gaston Gonzalez # Date : 6 November 2024 -# Updated : 17 February 2025 +# Updated : 15 March 2025 # Purpose : Configures the system for a specific mode of operation # Use a simple text file to save the state of the last mode selected @@ -127,18 +127,6 @@ select_term_mode () { fi } -select_winlink_mode () { - winlink_mode=() - - BLUETOOTH_TNC_CHECK=$(ps -ef | grep "[r]fcomm connect") - if [[ $? -eq 0 ]]; then - et-log "AX.25 over Bluetooth connected. Configuring Winlink for native AX.25" - start_and_wait_for_service et-service-winlink-native-packet - min http://localhost:8080 - exit 0 - fi -} - # Build list of options options=() options+=("${MODE_ID_NONE}" "No mode selected") @@ -248,7 +236,7 @@ if [ $exit_status -eq 0 ]; then min http://localhost:8080 ;; ${MODE_ID_WINLINK_PACKET}) - select_winlink_mode + winlink_mode start_and_wait_for_service et-service-direwolf-simple start_and_wait_for_service et-service-winlink-packet min http://localhost:8080 diff --git a/overlay/opt/emcomm-tools/bin/et-mode-bt b/overlay/opt/emcomm-tools/bin/et-mode-bt new file mode 100755 index 0000000..6cf74d0 --- /dev/null +++ b/overlay/opt/emcomm-tools/bin/et-mode-bt @@ -0,0 +1,128 @@ +#!/bin/bash +# +# Author : Gaston Gonzalez +# Date : 10 March 2024 +# Purpose : Launches a mode using a connected Bluetooth TNC +source /opt/emcomm-tools/bin/et-common + +# Use a simple text file to save the state of the last mode selected +MODE_STATUS_FILE="${HOME}/.config/emcomm-tools/et-mode" + +QTTERMTCP_CONF_FILE="${HOME}/.config/QtTermTCP.ini" + +# List of supported modes of operation +MODE_ID_NONE="none" +MODE_ID_BBS_CLIENT2="bbs-client2" +MODE_ID_WINLINK_PACKET="winlink-packet" + +BLUETOOTH_TNC_CHECK=$(ps -ef | grep "[r]fcomm connect") +if [[ $? -ne 0 ]]; then + et-log "No Bluetooth TNC configured." + exit 1 +fi + +# Attempt to stop all EmComm Tools services regardless if they are running. +stop_all_services() { + et-kill-all +} + +exit_on_service_failure() { + local systemd_unit_name="$1" + systemctl is-active --user --quiet "${systemd_unit_name}" + if [ $? -ne 0 ]; then + et-log "Can't start mode. ${systemd_unit_name} failed to start." + exit 1 + fi +} + +TIMEOUT=15 # Total time to wait in seconds +INTERVAL=2 # Interval between checks in seconds + +start_and_wait_for_service() { + local systemd_unit_name="$1" + local elapsed=0 + + et-log "Starting ${systemd_unit_name}..." + systemctl --user start ${systemd_unit_name} + + et-log "Waiting for ${systemd_unit_name} to become active..." + + while ! systemctl is-active --user --quiet "${systemd_unit_name}"; do + sleep "$INTERVAL" + elapsed=$((elapsed + INTERVAL)) + if [ "$elapsed" -ge "$TIMEOUT" ]; then + et-log "Timeout reached. ${systemd_unit_name} failed to start." + exit 1 + fi + et-log "Waiting for ${systemd_unit_name} to become active..." + done + + # Some applications even when started still need a bit of extra time to be available. + # Add a short think time of 2 seconds. The ARDOP modem requies extra time. + sleep 2 + + # Do one more check to ensure that we are running, otherwise exit. + exit_on_service_failure ${systemd_unit_name} +} + +# Build list of options +options=() +options+=("${MODE_ID_NONE}" "No mode selected") +options+=("${MODE_ID_BBS_CLIENT2}" "BBS Client (QtTermTCP)") +options+=("${MODE_ID_WINLINK_PACKET}" "Winlink over VHF/UHF (AX.25 Packet)") + +# Prompt user for option and save exit status +SELECTED_MODE=$(dialog --clear --menu "Select a mode:" 0 0 0 "${options[@]}" 3>&1 1>&2 2>&3) +exit_status=$? + +tput sgr 0 && clear + +if [ $exit_status -eq 0 ]; then + + stop_all_services + + echo ${SELECTED_MODE} > ${MODE_STATUS_FILE} + + case ${SELECTED_MODE} in + ${MODE_ID_NONE}) + et-log "No mode selected" + ;; + ${MODE_ID_BBS_CLIENT2}) + if [[ -e /dev/rfcomm0 ]]; then + et-log "Starting KISS over TCP" + + socat -d TCP-LISTEN:8100,reuseaddr,fork FILE:/dev/rfcomm0,raw & + fi + + if [[ $? -ne 0 ]]; then + et-log "Can't start QtTermTCP. KISS over TCP failed to start." + exit 1 + fi + + et-log "Starting QtTermTCP." + et-qttermtcp start + ;; + ${MODE_ID_WINLINK_PACKET}) + KISS_OUT=$(sudo kissattach ${BT_DEVICE} ${AX25_PORT}) + + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to setup KISS for Bluetooth TNC.${NC}" + exit 1 + fi + + KISS_OUT=$(sudo kissparms -c 1 -p ${AX25_PORT}) + + start_and_wait_for_service et-service-winlink-native-packet + min http://localhost:8080 + ;; + *) + et-log "Mode ${SELECTED_MODE} not yet supported" + exit 1 + ;; + esac + +else + et-log "No mode selected" + echo ${MODE_ID_NONE} > ${MODE_STATUS_FILE} + exit 1 +fi diff --git a/overlay/opt/emcomm-tools/bin/et-qttermtcp b/overlay/opt/emcomm-tools/bin/et-qttermtcp index 218c34f..1165358 100755 --- a/overlay/opt/emcomm-tools/bin/et-qttermtcp +++ b/overlay/opt/emcomm-tools/bin/et-qttermtcp @@ -2,7 +2,7 @@ # # Author : Gaston Gonzalez # Date : 17 December 2024 -# Updated : 8 February 2025 +# Updated : 15 March 2025 # Purpose : Wrapper script for starting JS8Call with PnP support # # Preconditions @@ -59,11 +59,18 @@ update-config () { fi sed -i "s|^AGWTermCall.*|AGWTermCall=${CALLSIGN}|g" ${CONF_FILE} + sed -i "s|^MYCALL.*|MYCALL=${CALLSIGN}|g" ${CONF_FILE} sed -i "s|^VARATermCall.*|VARATermCall=${CALLSIGN}|g" ${CONF_FILE} sed -i "s|^YAPPPath.*|YAPPPath=${HOME}/Downloads|" ${CONF_FILE} et-log "Updated QtTermTCP configuration with user callsign '${CALLSIGN}'" + PS_OUT=$(ps -ef | grep "[r]fcomm0"); + if [[ $? -eq 0 ]]; then + et-log "Configuring for use with Bluetooth TNC" + return + fi + # 1. Check if the symlink was created by the udev rules if [ -e /dev/et-audio ]; then diff --git a/overlay/opt/emcomm-tools/bin/et-uv-pro b/overlay/opt/emcomm-tools/bin/et-uv-pro index a883a41..dd6748c 100755 --- a/overlay/opt/emcomm-tools/bin/et-uv-pro +++ b/overlay/opt/emcomm-tools/bin/et-uv-pro @@ -2,9 +2,22 @@ # # Author : Gaston Gonzalez # Date : 1 January 2025 -# Updated : 3 February 2025 -# Purpose : Utility for accessing the Bluetooth TNC on the BTECH -# UV PRO. +# Updated : 15 March 2025 +# Purpose : Utility for accessing the Bluetooth TNC on the BTECH UV PRO. +source /opt/emcomm-tools/bin/et-common + +CLEAN_UP=1 + +cleanup() { + # Note: We need to reconnect to the Bluetooth device on every mode change. + # As such, we need kill all the rfcomm and kiss process on exit. + if [[ ${CLEAN_UP} -eq 1 ]]; then + sudo /opt/emcomm-tools/bin/et-kill-bt + fi +} + +# Kill all Bluetooth TNC services on exit +trap cleanup EXIT ACTIVE_RADIO="${ET_HOME}/conf/radios.d/btech-uvpro.bt.json" DEVICE_NAME="UV-PRO" @@ -12,47 +25,35 @@ BT_DEVICE="/dev/rfcomm0" AX25_PORT="wl2k" AX25_CONF_FILE="/etc/ax25/axports" -ESC="\x1B" -RED="${ESC}[1;31m" -BLUE="${ESC}[1;34m" -GREEN="${ESC}[1;32m" -WHITE="${ESC}[97m" -YELLOW="${ESC}[1;33m" -NC="${ESC}[0m" - function usage() { echo "Usage: $(basename $0) " - echo " connect - Connect to radio via Bluetooth" - echo " pair - Pair radio (one-time operation)" - echo " unpair - Unpair radio" + echo " c, connect - Connect to radio via Bluetooth" + echo " p, pair - Pair radio (one-time operation)" + echo " u, unpair - Unpair radio" + + CLEAN_UP=0 } -if [ $# -ne 1 ]; then +if [[ $# -ne 1 ]]; then usage exit 1 fi -error_message() { - echo -e "${RED}" - et-log $* - echo -e "${NC}" -} +if [[ -e ${BT_DEVICE} ]]; then + echo -e "${YELLOW}Your ${BLUE}${DEVICE_NAME}${YELLOW} may have a connection that needs to be cleaned up." + echo -e "Press ${WHITE}[ENTER]${YELLOW} to clean-up.${NC}" + read -p "" + + cleanup +fi -info_message() { - echo -e "${BLUE}" - et-log $* - echo -e "${NC}" -} - -success_message() { - echo -e "${GREEN}" - et-log $* - echo -e "${NC}" +stop_all_services() { + et-kill-all } exit_if_no_active_radio () { if [ ! -e ${ACTIVE_RADIO} ]; then - error_message "${ACTIVE_RADIO} file not found" + echo "${ACTIVE_RADIO} file not found" exit 1 fi } @@ -60,7 +61,7 @@ exit_if_no_active_radio () { get_mac_or_exit () { MAC=$(cat ${ACTIVE_RADIO} | jq -e -r .bluetooth.mac) if [ $? -ne 0 ]; then - error_message "No MAC address found in ${ACTIVE_RADIO}." + echo "No MAC address found in ${ACTIVE_RADIO}." exit 1 fi } @@ -72,7 +73,7 @@ update_ax25_conf() { CALLSIGN=$(cat ${ET_USER_CONFIG} | jq -r .callsign) if [ "${CALLSIGN}" = "N0CALL" ]; then - error_message "No callsign set. Run et-user first." + et-log "No callsign set. Run et-user first." exit 1 fi @@ -85,51 +86,87 @@ update_ax25_conf() { fi - info_message "Adding AX25 port for ${CALLSIGN}" echo "${AX25_PORT} ${CALLSIGN} 1200 255 2 1200 Packet" >> ${AX25_CONF_FILE} } -stop_all_services() { - - PS_OUT=$(ps -ef | grep [k]issattach) - if [ $? -eq 0 ]; then - info_message "Killing kissattach process(es)..."$ - sudo killall kissattach +do_kiss_attach() { + echo -e "${BLUE}Connecting KISS interface using ${BT_DEVICE} and AX.25 port: ${AX25_PORT}${NC}" + KISS_OUT=$(sudo kissattach ${BT_DEVICE} ${AX25_PORT}) + + if [[ $? -ne 0 ]]; then + echo -e "${RED}Failed to attach KISS port.${NC}" + exit 1 fi - - sleep 3 - PS2_OUT=$(ps -ef | grep -v krfcommd | grep [r]fcomm) - if [ $? -eq 0 ]; then - info_message "Killing rfcomm process(es)..." - sudo killall rfcomm + KISS_OUT=$(sudo kissparms -c 1 -p ${AX25_PORT}) +} + +exit_on_service_failure() { + local systemd_unit_name="$1" + systemctl is-active --user --quiet "${systemd_unit_name}" + if [ $? -ne 0 ]; then + et-log "Can't start mode. ${systemd_unit_name} failed to start." + exit 1 fi } +TIMEOUT=15 # Total time to wait in seconds +INTERVAL=2 # Interval between checks in seconds + +start_and_wait_for_service() { + local systemd_unit_name="$1" + local elapsed=0 + + et-log "Starting ${systemd_unit_name}..." + systemctl --user start ${systemd_unit_name} + + et-log "Waiting for ${systemd_unit_name} to become active..." + + while ! systemctl is-active --user --quiet "${systemd_unit_name}"; do + sleep "$INTERVAL" + elapsed=$((elapsed + INTERVAL)) + if [ "$elapsed" -ge "$TIMEOUT" ]; then + et-log "Timeout reached. ${systemd_unit_name} failed to start." + exit 1 + fi + et-log "Waiting for ${systemd_unit_name} to become active..." + done + + # Some applications even when started still need a bit of extra time to be available. + # Add a short think time of 2 seconds. The ARDOP modem requies extra time. + sleep 2 + + # Do one more check to ensure that we are running, otherwise exit. + exit_on_service_failure ${systemd_unit_name} +} + + exit_if_no_active_radio case $1 in - pair) + p|pair) MAC=$(cat ${ACTIVE_RADIO} | jq -e -r .bluetooth.mac) if [ $? -eq 0 ]; then PAIR_STATUS=$(bluetoothctl info "${MAC}" | grep "Paired:" | awk '{print $2}') if [ "${PAIR_STATUS}" == "yes" ]; then - error_message "Device with MAC ${MAC} is already paired." + echo "Device with MAC ${MAC} is already paired." exit 1 fi fi echo -e "${YELLOW}" - echo -e "1. Turn on the BTECH UV PRO" - echo -e "2. Goto Menu > Pairing and press OK" - read -p "3. Press [ENTER] when done." + echo -e "1. Turn off your ${DEVICE_NAME}" + echo -e "2. Turn on your ${DEVICE_NAME}" + echo -e "3. Goto Menu > Pairing and press OK" + echo -e "4. Press ${WHITE}[ENTER]${YELLOW} when done.${NC}" + read -p "" - info_message "Searching for ${DEVICE_NAME}..." + echo "Searching for ${DEVICE_NAME}..." HCI_OUT=$(hcitool scan | grep "${DEVICE_NAME}") if [ $? -ne 0 ]; then - error_message "Can't find ${DEVICE_NAME}. Make sure that the radio is on and in pairing mode." + echo "Can't find ${DEVICE_NAME}. Make sure that the radio is on and in pairing mode." exit 1 fi @@ -164,81 +201,101 @@ EOF sleep 1 && chmod 755 ${PAIR_SCRIPT} && expect ${PAIR_SCRIPT} ;; - unpair) + u|unpair) MAC=$(cat ${ACTIVE_RADIO} | jq -e -r .bluetooth.mac) if [ $? -ne 0 ]; then - error_message "No MAC found in radio configuration file." + echo "No MAC found in radio configuration file." exit 1 fi PAIR_STATUS=$(bluetoothctl info "${MAC}") if [ $? -ne 0 ]; then - error_message "No device with ${MAC} found. Can't unpair." + echo "No device with ${MAC} found. Can't unpair." exit 1 fi - info_message "Unpairing ${DEVICE_NAME}..." + echo "Unpairing ${DEVICE_NAME}..." bluetoothctl untrust ${MAC} bluetoothctl remove ${MAC} echo -e "${YELLOW}" - echo -e "You may also want to remove this device from your UV PRO" + echo -e "You may also want to remove this device from your ${DEVICE_NAME}" echo -e "under Menu > General Settings > Connection > Paired Devices." echo -e "${NC}" ;; - connect) + c|connect) get_mac_or_exit echo -e "${YELLOW}" - echo -e "1. Turn on the BTECH UV PRO" - echo -e "2. Menu > General Settings > KISS TNC = checked" - read -p "3. Press [ENTER] when done." + echo -e "1. Turn off your ${DEVICE_NAME}" + echo -e "2. Turn on your ${DEVICE_NAME}" + echo -e "3. Goto Menu > General Settings > KISS TNC > Enable KISS TNC" + echo -e "4. Press ${WHITE}[ENTER]${YELLOW} when done.${NC}" + read -p "" is_paired=$(bluetoothctl info ${MAC} | grep "Paired" | awk '{print $2}') if [ "${is_paired}" != "yes" ]; then echo -e "${RED}" - et-log "UV Pro is not paired. Run: '$(basename $0) pair'" + et-log "${DEVICE_NAME} is not paired. Run: '$(basename $0) pair'" echo -e "${NC}" exit 1 fi - echo -e "${GREEN}Found UV Pro with MAC address: ${MAC}" - stop_all_services + echo -e "Found ${DEVICE_NAME} with MAC address: ${MAC}" update_ax25_conf - echo -e "${BLUE}Connecting to radio..." - sudo rfcomm connect /dev/rfcomm0 ${MAC} 1>/dev/null 2>&1 & + sudo rfcomm connect ${BT_DEVICE} ${MAC} 1>/dev/null 2>&1 & sleep 10 && reset - if [ -e /dev/rfcomm0 ]; then - echo -e "${GREEN}UV Pro connected via Bluetooth serial${NC}" - else - echo -e "${RED}UV Pro failed to connect via Bluetooth serial${NC}" + if [[ ! -e "${BT_DEVICE}" ]]; then + echo -e "${RED}Failed to connect via Bluetooth${NC}\n" + echo -e "${YELLOW}Try turning off and back on your radio${NC}\n" exit 1 fi - echo -e "${BLUE}Connecting KISS interface using ${BT_DEVICE} and AX.25 port: ${AX25_PORT}${NC}" - KISS_OUT=$(sudo kissattach ${BT_DEVICE} ${AX25_PORT}) - - if [ $? -eq 0 ]; then - echo -e "${GREEN}Packet port ready" - else - echo -e "${RED}Failed to setup radio for packet.${NC}" - exit 1 - fi + stop_all_services - KISS_OUT=$(sudo kissparms -c 1 -p ${AX25_PORT}) + echo -e "\nSelect a packet application to start:" + options=("axcall" "QtTermTCP" "Winlink" "Exit") - echo -e "${YELLOW}" - echo -e "SAMPLE COMMANDS DESCRIPTION " - echo -e "------------------------------- --------------------------------" - echo -e "axcall ${AX25_PORT} Connect directly to a node " - echo -e "axcall ${AX25_PORT} Connect to node via a digipeater" - echo -e "sudo axlisten -a -tttt -c Monitor inbound and outbound traffic" - echo -e "${NC}" + select opt in "${options[@]}" + do + case $opt in + "axcall") + do_kiss_attach + echo -e "${YELLOW}" + echo -e "SAMPLE COMMANDS DESCRIPTION " + echo -e "----------------------------- --------------------------------" + echo -e "axcall wl2k Connect directly to a node " + echo -e "axcall wl2k Connect to node via a digipeater" + echo -e "sudo axlisten -a -tttt -c Monitor inbound and outbound traffic" + echo -e "${NC}" + + # We can't have the rfcomm and kiss processes killed. + CLEAN_UP=0 + break + ;; + "QtTermTCP") + et-qttermtcp start 1>/dev/null 2>&1 + break + ;; + "Winlink") + do_kiss_attach + start_and_wait_for_service et-service-winlink-native-packet + min http://localhost:8080 1>/dev/null 2>&1 + break + ;; + "Exit") + break + ;; + *) + echo "Invalid option, please select a number from the list." + ;; + esac + done ;; *) From 4a3b0f07fded316a8b7aa3cb564b55bbd95002c1 Mon Sep 17 00:00:00 2001 From: Gaston Gonzalez Date: Sat, 15 Mar 2025 08:20:40 -0700 Subject: [PATCH 3/5] UV Pro - Fixed device path bug and added end-user troubleshooting instructions on failure to connect --- overlay/opt/emcomm-tools/bin/et-uv-pro | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/overlay/opt/emcomm-tools/bin/et-uv-pro b/overlay/opt/emcomm-tools/bin/et-uv-pro index dd6748c..b09bb7b 100755 --- a/overlay/opt/emcomm-tools/bin/et-uv-pro +++ b/overlay/opt/emcomm-tools/bin/et-uv-pro @@ -246,13 +246,18 @@ EOF update_ax25_conf - sudo rfcomm connect ${BT_DEVICE} ${MAC} 1>/dev/null 2>&1 & + sudo rfcomm connect /dev/rfcomm0 ${MAC} 1>/dev/null 2>&1 & sleep 10 && reset if [[ ! -e "${BT_DEVICE}" ]]; then - echo -e "${RED}Failed to connect via Bluetooth${NC}\n" - echo -e "${YELLOW}Try turning off and back on your radio${NC}\n" + echo -e "${RED}Failed to connect via Bluetooth${NC}" + echo -e "${YELLOW}" + echo -e "1. Try turning off and back on your radio." + echo -e "2. Check your radio and ensure that ${BLUE}$(hostname)${YELLOW} is listed under:" + echo -e " Menu > General Settings> Connection > Paired Devices: $(hostname)." + echo -e "" + echo -e " If it is not listed, re-run: ${WHITE}$(basename $0) pair${NC}\n" exit 1 fi From 5c1fccf51fafb4199118abcf368e8588cb9bb040 Mon Sep 17 00:00:00 2001 From: Gaston Gonzalez Date: Sat, 15 Mar 2025 08:33:22 -0700 Subject: [PATCH 4/5] VGC VR-N76 - Cloned et-uv-pro script for simplicity with minor adjustments to use VGC VR-N76 --- overlay/opt/emcomm-tools/bin/et-vr-n76 | 244 ++++++++++++++++--------- 1 file changed, 153 insertions(+), 91 deletions(-) diff --git a/overlay/opt/emcomm-tools/bin/et-vr-n76 b/overlay/opt/emcomm-tools/bin/et-vr-n76 index 945d87b..bef5488 100755 --- a/overlay/opt/emcomm-tools/bin/et-vr-n76 +++ b/overlay/opt/emcomm-tools/bin/et-vr-n76 @@ -1,11 +1,24 @@ #!/bin/bash # -# Authors : Gaston Gonzalez -# : Clifton Jones -# Date : 1 January 2025 -# Updated : 19 February 2025 -# Purpose : Utility for accessing the Bluetooth TNC on the VGC VR-N76 -# +# Authors : Gaston Gonzalez +# : Clifton Jones +# Date : 1 January 2025 +# Updated : 15 March 2025 +# Purpose : Utility for accessing the Bluetooth TNC on the VGC VR-N76. +source /opt/emcomm-tools/bin/et-common + +CLEAN_UP=1 + +cleanup() { + # Note: We need to reconnect to the Bluetooth device on every mode change. + # As such, we need kill all the rfcomm and kiss process on exit. + if [[ ${CLEAN_UP} -eq 1 ]]; then + sudo /opt/emcomm-tools/bin/et-kill-bt + fi +} + +# Kill all Bluetooth TNC services on exit +trap cleanup EXIT ACTIVE_RADIO="${ET_HOME}/conf/radios.d/vgc-vrn76.bt.json" DEVICE_NAME="VR-N76" @@ -13,47 +26,35 @@ BT_DEVICE="/dev/rfcomm0" AX25_PORT="wl2k" AX25_CONF_FILE="/etc/ax25/axports" -ESC="\x1B" -RED="${ESC}[1;31m" -BLUE="${ESC}[1;34m" -GREEN="${ESC}[1;32m" -WHITE="${ESC}[97m" -YELLOW="${ESC}[1;33m" -NC="${ESC}[0m" - function usage() { echo "Usage: $(basename $0) " - echo " connect - Connect to radio via Bluetooth" - echo " pair - Pair radio (one-time operation)" - echo " unpair - Unpair radio" + echo " c, connect - Connect to radio via Bluetooth" + echo " p, pair - Pair radio (one-time operation)" + echo " u, unpair - Unpair radio" + + CLEAN_UP=0 } -if [ $# -ne 1 ]; then +if [[ $# -ne 1 ]]; then usage exit 1 fi -error_message() { - echo -e "${RED}" - et-log $* - echo -e "${NC}" -} +if [[ -e ${BT_DEVICE} ]]; then + echo -e "${YELLOW}Your ${BLUE}${DEVICE_NAME}${YELLOW} may have a connection that needs to be cleaned up." + echo -e "Press ${WHITE}[ENTER]${YELLOW} to clean-up.${NC}" + read -p "" + + cleanup +fi -info_message() { - echo -e "${BLUE}" - et-log $* - echo -e "${NC}" -} - -success_message() { - echo -e "${GREEN}" - et-log $* - echo -e "${NC}" +stop_all_services() { + et-kill-all } exit_if_no_active_radio () { if [ ! -e ${ACTIVE_RADIO} ]; then - error_message "${ACTIVE_RADIO} file not found" + echo "${ACTIVE_RADIO} file not found" exit 1 fi } @@ -61,7 +62,7 @@ exit_if_no_active_radio () { get_mac_or_exit () { MAC=$(cat ${ACTIVE_RADIO} | jq -e -r .bluetooth.mac) if [ $? -ne 0 ]; then - error_message "No MAC address found in ${ACTIVE_RADIO}." + echo "No MAC address found in ${ACTIVE_RADIO}." exit 1 fi } @@ -73,7 +74,7 @@ update_ax25_conf() { CALLSIGN=$(cat ${ET_USER_CONFIG} | jq -r .callsign) if [ "${CALLSIGN}" = "N0CALL" ]; then - error_message "No callsign set. Run et-user first." + et-log "No callsign set. Run et-user first." exit 1 fi @@ -86,51 +87,87 @@ update_ax25_conf() { fi - info_message "Adding AX25 port for ${CALLSIGN}" echo "${AX25_PORT} ${CALLSIGN} 1200 255 2 1200 Packet" >> ${AX25_CONF_FILE} } -stop_all_services() { - - PS_OUT=$(ps -ef | grep [k]issattach) - if [ $? -eq 0 ]; then - info_message "Killing kissattach process(es)..."$ - sudo killall kissattach +do_kiss_attach() { + echo -e "${BLUE}Connecting KISS interface using ${BT_DEVICE} and AX.25 port: ${AX25_PORT}${NC}" + KISS_OUT=$(sudo kissattach ${BT_DEVICE} ${AX25_PORT}) + + if [[ $? -ne 0 ]]; then + echo -e "${RED}Failed to attach KISS port.${NC}" + exit 1 fi - - sleep 3 - PS2_OUT=$(ps -ef | grep -v krfcommd | grep [r]fcomm) - if [ $? -eq 0 ]; then - info_message "Killing rfcomm process(es)..." - sudo killall rfcomm + KISS_OUT=$(sudo kissparms -c 1 -p ${AX25_PORT}) +} + +exit_on_service_failure() { + local systemd_unit_name="$1" + systemctl is-active --user --quiet "${systemd_unit_name}" + if [ $? -ne 0 ]; then + et-log "Can't start mode. ${systemd_unit_name} failed to start." + exit 1 fi } +TIMEOUT=15 # Total time to wait in seconds +INTERVAL=2 # Interval between checks in seconds + +start_and_wait_for_service() { + local systemd_unit_name="$1" + local elapsed=0 + + et-log "Starting ${systemd_unit_name}..." + systemctl --user start ${systemd_unit_name} + + et-log "Waiting for ${systemd_unit_name} to become active..." + + while ! systemctl is-active --user --quiet "${systemd_unit_name}"; do + sleep "$INTERVAL" + elapsed=$((elapsed + INTERVAL)) + if [ "$elapsed" -ge "$TIMEOUT" ]; then + et-log "Timeout reached. ${systemd_unit_name} failed to start." + exit 1 + fi + et-log "Waiting for ${systemd_unit_name} to become active..." + done + + # Some applications even when started still need a bit of extra time to be available. + # Add a short think time of 2 seconds. The ARDOP modem requies extra time. + sleep 2 + + # Do one more check to ensure that we are running, otherwise exit. + exit_on_service_failure ${systemd_unit_name} +} + + exit_if_no_active_radio case $1 in - pair) + p|pair) MAC=$(cat ${ACTIVE_RADIO} | jq -e -r .bluetooth.mac) if [ $? -eq 0 ]; then PAIR_STATUS=$(bluetoothctl info "${MAC}" | grep "Paired:" | awk '{print $2}') if [ "${PAIR_STATUS}" == "yes" ]; then - error_message "Device with MAC ${MAC} is already paired." + echo "Device with MAC ${MAC} is already paired." exit 1 fi fi echo -e "${YELLOW}" - echo -e "1. Turn on the VGC VR-N76" - echo -e "2. Goto Menu > Pairing and press OK" - read -p "3. Press [ENTER] when done." + echo -e "1. Turn off your ${DEVICE_NAME}" + echo -e "2. Turn on your ${DEVICE_NAME}" + echo -e "3. Goto Menu > Pairing and press OK" + echo -e "4. Press ${WHITE}[ENTER]${YELLOW} when done.${NC}" + read -p "" - info_message "Searching for ${DEVICE_NAME}..." + echo "Searching for ${DEVICE_NAME}..." HCI_OUT=$(hcitool scan | grep "${DEVICE_NAME}") if [ $? -ne 0 ]; then - error_message "Can't find ${DEVICE_NAME}. Make sure that the radio is on and in pairing mode." + echo "Can't find ${DEVICE_NAME}. Make sure that the radio is on and in pairing mode." exit 1 fi @@ -165,81 +202,106 @@ EOF sleep 1 && chmod 755 ${PAIR_SCRIPT} && expect ${PAIR_SCRIPT} ;; - unpair) + u|unpair) MAC=$(cat ${ACTIVE_RADIO} | jq -e -r .bluetooth.mac) if [ $? -ne 0 ]; then - error_message "No MAC found in radio configuration file." + echo "No MAC found in radio configuration file." exit 1 fi PAIR_STATUS=$(bluetoothctl info "${MAC}") if [ $? -ne 0 ]; then - error_message "No device with ${MAC} found. Can't unpair." + echo "No device with ${MAC} found. Can't unpair." exit 1 fi - info_message "Unpairing ${DEVICE_NAME}..." + echo "Unpairing ${DEVICE_NAME}..." bluetoothctl untrust ${MAC} bluetoothctl remove ${MAC} echo -e "${YELLOW}" - echo -e "You may also want to remove this device from your VR-N76" + echo -e "You may also want to remove this device from your ${DEVICE_NAME}" echo -e "under Menu > General Settings > Connection > Paired Devices." echo -e "${NC}" ;; - connect) + c|connect) get_mac_or_exit echo -e "${YELLOW}" - echo -e "1. Turn on the VGC VR-N76" - echo -e "2. Menu > General Settings > KISS TNC = checked" - read -p "3. Press [ENTER] when done." + echo -e "1. Turn off your ${DEVICE_NAME}" + echo -e "2. Turn on your ${DEVICE_NAME}" + echo -e "3. Goto Menu > General Settings > KISS TNC > Enable KISS TNC" + echo -e "4. Press ${WHITE}[ENTER]${YELLOW} when done.${NC}" + read -p "" is_paired=$(bluetoothctl info ${MAC} | grep "Paired" | awk '{print $2}') if [ "${is_paired}" != "yes" ]; then echo -e "${RED}" - et-log "VR-N76 is not paired. Run: '$(basename $0) pair'" + et-log "${DEVICE_NAME} is not paired. Run: '$(basename $0) pair'" echo -e "${NC}" exit 1 fi - echo -e "${GREEN}Found VR-N76 with MAC address: ${MAC}" - stop_all_services + echo -e "Found ${DEVICE_NAME} with MAC address: ${MAC}" update_ax25_conf - echo -e "${BLUE}Connecting to radio..." sudo rfcomm connect /dev/rfcomm0 ${MAC} 1>/dev/null 2>&1 & sleep 10 && reset - if [ -e /dev/rfcomm0 ]; then - echo -e "${GREEN}VR-N76 connected via Bluetooth serial${NC}" - else - echo -e "${RED}VR-N76 failed to connect via Bluetooth serial${NC}" - exit 1 - fi - - echo -e "${BLUE}Connecting KISS interface using ${BT_DEVICE} and AX.25 port: ${AX25_PORT}${NC}" - KISS_OUT=$(sudo kissattach ${BT_DEVICE} ${AX25_PORT}) - - if [ $? -eq 0 ]; then - echo -e "${GREEN}Packet port ready" - else - echo -e "${RED}Failed to setup radio for packet.${NC}" + if [[ ! -e "${BT_DEVICE}" ]]; then + echo -e "${RED}Failed to connect via Bluetooth${NC}" + echo -e "${YELLOW}" + echo -e "1. Try turning off and back on your radio." + echo -e "2. Check your radio and ensure that ${BLUE}$(hostname)${YELLOW} is listed under:" + echo -e " Menu > General Settings> Connection > Paired Devices: $(hostname)." + echo -e "" + echo -e " If it is not listed, re-run: ${WHITE}$(basename $0) pair${NC}\n" exit 1 fi - KISS_OUT=$(sudo kissparms -c 1 -p ${AX25_PORT}) + stop_all_services - echo -e "${YELLOW}" - echo -e "SAMPLE COMMANDS DESCRIPTION " - echo -e "------------------------------- --------------------------------" - echo -e "axcall ${AX25_PORT} Connect directly to a node " - echo -e "axcall ${AX25_PORT} Connect to node via a digipeater" - echo -e "sudo axlisten -a -tttt -c Monitor inbound and outbound traffic" - echo -e "${NC}" + echo -e "\nSelect a packet application to start:" + options=("axcall" "QtTermTCP" "Winlink" "Exit") + + select opt in "${options[@]}" + do + case $opt in + "axcall") + do_kiss_attach + echo -e "${YELLOW}" + echo -e "SAMPLE COMMANDS DESCRIPTION " + echo -e "----------------------------- --------------------------------" + echo -e "axcall wl2k Connect directly to a node " + echo -e "axcall wl2k Connect to node via a digipeater" + echo -e "sudo axlisten -a -tttt -c Monitor inbound and outbound traffic" + echo -e "${NC}" + + # We can't have the rfcomm and kiss processes killed. + CLEAN_UP=0 + break + ;; + "QtTermTCP") + et-qttermtcp start 1>/dev/null 2>&1 + break + ;; + "Winlink") + do_kiss_attach + start_and_wait_for_service et-service-winlink-native-packet + min http://localhost:8080 1>/dev/null 2>&1 + break + ;; + "Exit") + break + ;; + *) + echo "Invalid option, please select a number from the list." + ;; + esac + done ;; *) From 1816118221caf48ab849911ab13729bb7228098f Mon Sep 17 00:00:00 2001 From: Gaston Gonzalez Date: Sun, 16 Mar 2025 09:06:16 -0700 Subject: [PATCH 5/5] Bluetooth - Removed failed prototype around a dedicated Bluetooth mode switcher --- overlay/opt/emcomm-tools/bin/et-mode-bt | 128 ------------------------ 1 file changed, 128 deletions(-) delete mode 100755 overlay/opt/emcomm-tools/bin/et-mode-bt diff --git a/overlay/opt/emcomm-tools/bin/et-mode-bt b/overlay/opt/emcomm-tools/bin/et-mode-bt deleted file mode 100755 index 6cf74d0..0000000 --- a/overlay/opt/emcomm-tools/bin/et-mode-bt +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/bash -# -# Author : Gaston Gonzalez -# Date : 10 March 2024 -# Purpose : Launches a mode using a connected Bluetooth TNC -source /opt/emcomm-tools/bin/et-common - -# Use a simple text file to save the state of the last mode selected -MODE_STATUS_FILE="${HOME}/.config/emcomm-tools/et-mode" - -QTTERMTCP_CONF_FILE="${HOME}/.config/QtTermTCP.ini" - -# List of supported modes of operation -MODE_ID_NONE="none" -MODE_ID_BBS_CLIENT2="bbs-client2" -MODE_ID_WINLINK_PACKET="winlink-packet" - -BLUETOOTH_TNC_CHECK=$(ps -ef | grep "[r]fcomm connect") -if [[ $? -ne 0 ]]; then - et-log "No Bluetooth TNC configured." - exit 1 -fi - -# Attempt to stop all EmComm Tools services regardless if they are running. -stop_all_services() { - et-kill-all -} - -exit_on_service_failure() { - local systemd_unit_name="$1" - systemctl is-active --user --quiet "${systemd_unit_name}" - if [ $? -ne 0 ]; then - et-log "Can't start mode. ${systemd_unit_name} failed to start." - exit 1 - fi -} - -TIMEOUT=15 # Total time to wait in seconds -INTERVAL=2 # Interval between checks in seconds - -start_and_wait_for_service() { - local systemd_unit_name="$1" - local elapsed=0 - - et-log "Starting ${systemd_unit_name}..." - systemctl --user start ${systemd_unit_name} - - et-log "Waiting for ${systemd_unit_name} to become active..." - - while ! systemctl is-active --user --quiet "${systemd_unit_name}"; do - sleep "$INTERVAL" - elapsed=$((elapsed + INTERVAL)) - if [ "$elapsed" -ge "$TIMEOUT" ]; then - et-log "Timeout reached. ${systemd_unit_name} failed to start." - exit 1 - fi - et-log "Waiting for ${systemd_unit_name} to become active..." - done - - # Some applications even when started still need a bit of extra time to be available. - # Add a short think time of 2 seconds. The ARDOP modem requies extra time. - sleep 2 - - # Do one more check to ensure that we are running, otherwise exit. - exit_on_service_failure ${systemd_unit_name} -} - -# Build list of options -options=() -options+=("${MODE_ID_NONE}" "No mode selected") -options+=("${MODE_ID_BBS_CLIENT2}" "BBS Client (QtTermTCP)") -options+=("${MODE_ID_WINLINK_PACKET}" "Winlink over VHF/UHF (AX.25 Packet)") - -# Prompt user for option and save exit status -SELECTED_MODE=$(dialog --clear --menu "Select a mode:" 0 0 0 "${options[@]}" 3>&1 1>&2 2>&3) -exit_status=$? - -tput sgr 0 && clear - -if [ $exit_status -eq 0 ]; then - - stop_all_services - - echo ${SELECTED_MODE} > ${MODE_STATUS_FILE} - - case ${SELECTED_MODE} in - ${MODE_ID_NONE}) - et-log "No mode selected" - ;; - ${MODE_ID_BBS_CLIENT2}) - if [[ -e /dev/rfcomm0 ]]; then - et-log "Starting KISS over TCP" - - socat -d TCP-LISTEN:8100,reuseaddr,fork FILE:/dev/rfcomm0,raw & - fi - - if [[ $? -ne 0 ]]; then - et-log "Can't start QtTermTCP. KISS over TCP failed to start." - exit 1 - fi - - et-log "Starting QtTermTCP." - et-qttermtcp start - ;; - ${MODE_ID_WINLINK_PACKET}) - KISS_OUT=$(sudo kissattach ${BT_DEVICE} ${AX25_PORT}) - - if [ $? -ne 0 ]; then - echo -e "${RED}Failed to setup KISS for Bluetooth TNC.${NC}" - exit 1 - fi - - KISS_OUT=$(sudo kissparms -c 1 -p ${AX25_PORT}) - - start_and_wait_for_service et-service-winlink-native-packet - min http://localhost:8080 - ;; - *) - et-log "Mode ${SELECTED_MODE} not yet supported" - exit 1 - ;; - esac - -else - et-log "No mode selected" - echo ${MODE_ID_NONE} > ${MODE_STATUS_FILE} - exit 1 -fi