diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bb253d8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# Force LF line endings for all shell scripts and debian packaging files +* text=auto + +*.sh text eol=lf +*.bash text eol=lf +debian/rules text eol=lf +debian/postinst text eol=lf +debian/postrm text eol=lf +debian/conffiles text eol=lf +debian/control text eol=lf +debian/changelog text eol=lf +debian/copyright text eol=lf diff --git a/build_deb.sh b/build_deb.sh new file mode 100644 index 0000000..a89eb18 --- /dev/null +++ b/build_deb.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0 +# Copyright (C) 2026 wentywenty +# +# Build RoboParty base deb package + +set -e + +echo "==> Installing build dependencies..." +sudo apt-get update +sudo apt-get install -y dpkg-dev debhelper devscripts python3 + +echo "==> Building deb package..." +dpkg-buildpackage -us -uc -b + +echo "==> Moving deb to current directory..." +mv ../roboparty-base_*.deb . 2>/dev/null || true + +echo "==> Done!" +ls -la roboparty-base_*.deb 2>/dev/null || echo "No .deb file found, check build output above." diff --git a/debian/changelog b/debian/changelog index 8878f5a..46ca506 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +roboparty-base (1.1.1-1) stable; urgency=medium + + * Add wifi-reset and hpm-reset systemd services + * Restore debian/conffiles entries + * Move scripts to /opt/roboparty/bin + + -- wentywenty <2321901849@qq.com> Thu, 31 Jul 2026 00:00:00 +0800 + roboparty-base (1.1.0-1) stable; urgency=medium * update series udev diff --git a/debian/conffiles b/debian/conffiles index 0248521..207f295 100644 --- a/debian/conffiles +++ b/debian/conffiles @@ -8,3 +8,5 @@ /etc/udev/rules.d/99-ethercat_hpm.rules /etc/udev/rules.d/99-serial_hipnuc.rules /etc/udev/rules.d/99-serial_tws.rules +/etc/default/wifi-reset +/etc/default/hpm-reset diff --git a/debian/postinst b/debian/postinst index 5fea89a..3af3930 100755 --- a/debian/postinst +++ b/debian/postinst @@ -35,3 +35,12 @@ grep -q '@orangepi - memlock unlimited' /etc/security/limits.conf 2>/dev/null || udevadm control --reload-rules udevadm trigger --subsystem-match=tty udevadm trigger --subsystem-match=net + +# Enable and start WiFi reset monitor +systemctl daemon-reload +systemctl enable wifi-reset.service 2>/dev/null || true +systemctl start wifi-reset.service 2>/dev/null || true + +# Enable and start HPM reset monitor +systemctl enable hpm-reset.service 2>/dev/null || true +systemctl start hpm-reset.service 2>/dev/null || true diff --git a/debian/postrm b/debian/postrm index f4a9c91..9b5899a 100755 --- a/debian/postrm +++ b/debian/postrm @@ -2,6 +2,15 @@ set -e if [ "$1" = "purge" ]; then + systemctl stop wifi-reset.service 2>/dev/null || true + systemctl disable wifi-reset.service 2>/dev/null || true + systemctl stop hpm-reset.service 2>/dev/null || true + systemctl disable hpm-reset.service 2>/dev/null || true + rm -f /etc/systemd/system/wifi-reset.service + rm -f /etc/systemd/system/hpm-reset.service + rm -f /opt/roboparty/bin/wifi-reconnect.sh + rm -f /opt/roboparty/bin/reset_hpm.sh + systemctl daemon-reload rm -rf /opt/roboparty rm -f /etc/ld.so.conf.d/roboparty.conf rm -f /usr/lib/python3/dist-packages/roboparty.pth diff --git a/debian/rules b/debian/rules index 7df92dd..132b0c2 100755 --- a/debian/rules +++ b/debian/rules @@ -1,26 +1,38 @@ #!/usr/bin/make -f export DH_VERBOSE = 1 -PYTHON_VER := $(shell python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') - %: dh $@ override_dh_auto_configure: override_dh_auto_build: +override_dh_auto_test: +override_dh_auto_clean: +override_dh_auto_install: override_dh_install: dh_install - - install -d debian/roboparty-base/opt/roboparty/lib/python$(PYTHON_VER)/site-packages + + install -d debian/roboparty-base/opt/roboparty/lib/python3.10/site-packages install -d debian/roboparty-base/opt/roboparty/include install -d debian/roboparty-base/opt/roboparty/bin install -d debian/roboparty-base/opt/roboparty/share - + install -m 644 setup.bash debian/roboparty-base/opt/roboparty/ - + install -d debian/roboparty-base/etc/ld.so.conf.d install -m 644 etc/ld.so.conf.d/roboparty.conf debian/roboparty-base/etc/ld.so.conf.d/ - + install -d debian/roboparty-base/etc/udev/rules.d install -m 644 etc/udev/rules.d/*.rules debian/roboparty-base/etc/udev/rules.d/ + + install -d debian/roboparty-base/etc/systemd/system + install -m 644 etc/systemd/system/wifi-reset.service debian/roboparty-base/etc/systemd/system/ + install -m 644 etc/systemd/system/hpm-reset.service debian/roboparty-base/etc/systemd/system/ + + install -m 755 scripts/wifi-reconnect.sh debian/roboparty-base/opt/roboparty/bin/ + install -m 755 scripts/reset_hpm.sh debian/roboparty-base/opt/roboparty/bin/ + + install -d debian/roboparty-base/etc/default + install -m 644 etc/default/wifi-reset debian/roboparty-base/etc/default/ + install -m 644 etc/default/hpm-reset debian/roboparty-base/etc/default/ diff --git a/etc/default/hpm-reset b/etc/default/hpm-reset new file mode 100644 index 0000000..5b7bc9e --- /dev/null +++ b/etc/default/hpm-reset @@ -0,0 +1,24 @@ +# HPM Reset Monitor default configuration +# See: /etc/systemd/system/hpm-reset.service + +# GPIO1_B7 sysfs number on RK3588 (Bank1 * 32 + B(1) * 8 + 7) +RESET_GPIO=47 + +# Check interval in seconds +CHECK_INTERVAL=2 + +# Number of consecutive failures before triggering reset +FAIL_THRESHOLD=10 + +# Reset pulse duration in seconds +RESET_HOLD_TIME=0.5 + +# 1 = active-high, 0 = active-low +RESET_ACTIVE_HIGH=1 + +# Wait time after reset in seconds +POST_RESET_WAIT=15 + +# USB device identification (roboto_usb4can) +HPM_VID=1209 +HPM_PID=2323 diff --git a/etc/default/wifi-reset b/etc/default/wifi-reset new file mode 100644 index 0000000..ed0c3e8 --- /dev/null +++ b/etc/default/wifi-reset @@ -0,0 +1,11 @@ +# WiFi Reset Monitor default configuration +# See: /etc/systemd/system/wifi-reset.service + +# Check interval in seconds +CHECK_INTERVAL=30 + +# Number of consecutive failures before reporting error +FAIL_THRESHOLD=3 + +# WiFi interface name (set to empty for auto-detection) +WIFI_INTERFACE=wlan0 diff --git a/etc/systemd/system/hpm-reset.service b/etc/systemd/system/hpm-reset.service new file mode 100644 index 0000000..1a7a3d5 --- /dev/null +++ b/etc/systemd/system/hpm-reset.service @@ -0,0 +1,25 @@ +[Unit] +Description=RoboParty HPM Reset Monitor (GPIO1_B7 watchdog) +Documentation=https://github.com/Roboparty/roboparty_base +After=multi-user.target + +[Service] +Type=simple +EnvironmentFile=/etc/default/hpm-reset +ExecStartPre=/bin/sleep 3 +ExecStart=/opt/roboparty/bin/reset_hpm.sh +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=hpm-reset-monitor + +# Security hardening +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=/sys/class/gpio + +[Install] +WantedBy=multi-user.target diff --git a/etc/systemd/system/wifi-reset.service b/etc/systemd/system/wifi-reset.service new file mode 100644 index 0000000..b3d3641 --- /dev/null +++ b/etc/systemd/system/wifi-reset.service @@ -0,0 +1,22 @@ +[Unit] +Description=RoboParty WiFi Reset Monitor +Documentation=https://github.com/Roboparty/roboparty_base +After=network.target NetworkManager.service +Wants=NetworkManager.service + +[Service] +Type=simple +EnvironmentFile=/etc/default/wifi-reset +ExecStart=/opt/roboparty/bin/wifi-reconnect.sh +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=wifi-reset-monitor + +# Security hardening +NoNewPrivileges=yes +PrivateTmp=yes + +[Install] +WantedBy=multi-user.target diff --git a/hpm_reset.service b/hpm_reset.service deleted file mode 100644 index 09731ff..0000000 --- a/hpm_reset.service +++ /dev/null @@ -1,33 +0,0 @@ -[Unit] -Description=RoboParty HPM Reset Monitor (GPIO1_B7 watchdog) -Documentation=https://github.com/roboparty/ecatfd-master -After=ethercat.service roboparty-ecatfd-master.service -Requires=ethercat.service -Wants=roboparty-ecatfd-master.service - -[Service] -Type=simple -Environment="RESET_GPIO=47" -Environment="CHECK_INTERVAL=2" -Environment="FAIL_THRESHOLD=10" -Environment="RESET_HOLD_TIME=0.5" -Environment="RESET_ACTIVE_HIGH=1" -Environment="POST_RESET_WAIT=15" -ExecStartPre=/usr/bin/modprobe ec_master -ExecStartPre=/bin/sleep 3 -ExecStart=/opt/roboparty/ethercat/bin/hpm-reset-monitor.sh -Restart=always -RestartSec=10 -StandardOutput=journal -StandardError=journal -SyslogIdentifier=hpm-reset-monitor - -# 安全加固 -NoNewPrivileges=yes -PrivateTmp=yes -ProtectSystem=strict -ProtectHome=yes -ReadWritePaths=/sys/class/gpio - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/reset_hpm.sh b/reset_hpm.sh deleted file mode 100644 index 4520564..0000000 --- a/reset_hpm.sh +++ /dev/null @@ -1,201 +0,0 @@ -#!/bin/bash -#============================================================================== -# HPM Reset Monitor — RoboParty EtherCAT CANFD Master -# -# 功能: 后台持续监测 HPM (roboto_usb4can, 1209:2323) 的 USB 枚举状态。 -# 如果 USB 设备掉线 / 未枚举,通过 GPIO1_B7 脉冲复位 HPM。 -# -# 设备: roboto_usb4can (wentytwenty) -# VID=1209, PID=2323 -# 内核驱动: gs_usb -# -# GPIO1_B7 on RK3588: -# sysfs GPIO 编号 = Bank1 * 32 + B(1) * 8 + 7 = 32 + 8 + 7 = 47 -# 对应: /sys/class/gpio/gpio47 -# -# 连接: RK3588 GPIO1_B7 → HPM RST 引脚 (高有效复位) -#============================================================================== - -set -e - -# ---- 可配置参数 (通过 systemd Environment= 覆盖) ------------------------------- -RESET_GPIO="${RESET_GPIO:-47}" # GPIO1_B7 sysfs 编号 -CHECK_INTERVAL="${CHECK_INTERVAL:-5}" # 检测间隔 (秒) -FAIL_THRESHOLD="${FAIL_THRESHOLD:-3}" # 连续失败次数阈值 -RESET_HOLD_TIME="${RESET_HOLD_TIME:-0.5}" # 复位脉冲保持时间 (秒) -RESET_ACTIVE_HIGH="${RESET_ACTIVE_HIGH:-0}" # 1=高电平有效, 0=低电平有效 -POST_RESET_WAIT="${POST_RESET_WAIT:-15}" # 复位后等待时间 (秒) -LOG_TAG="hpm-reset-monitor" - -# USB 设备匹配 (roboto_usb4can) -HPM_VID="${HPM_VID:-1209}" -HPM_PID="${HPM_PID:-2323}" - -# ---- 日志函数 ---------------------------------------------------------------- -log_msg() { - local level="${1:-INFO}" - shift - logger -t "$LOG_TAG" -p "daemon.${level}" "$*" - printf '[%s] [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$level" "$*" >&2 -} - -# ---- sysfs GPIO 操作 --------------------------------------------------------- -gpio_path() { - echo "/sys/class/gpio/gpio${RESET_GPIO}" -} - -gpio_setup() { - local gpio_dir - gpio_dir="$(gpio_path)" - - if [ ! -d "$gpio_dir" ]; then - echo "$RESET_GPIO" > /sys/class/gpio/export 2>/dev/null || true - for _ in $(seq 1 10); do - [ -d "$gpio_dir" ] && break - usleep 100000 - done - fi - - if [ ! -d "$gpio_dir" ]; then - log_msg "ERR" "无法 export GPIO ${RESET_GPIO} (GPIO1_B7), 检查设备树是否冲突" - return 1 - fi - - echo "out" > "${gpio_dir}/direction" 2>/dev/null || true - - if [ "$RESET_ACTIVE_HIGH" -eq 1 ]; then - echo 0 > "${gpio_dir}/value" - else - echo 1 > "${gpio_dir}/value" - fi - - log_msg "INFO" "GPIO${RESET_GPIO} (GPIO1_B7) 初始化完成, 方向=out, 初始=无效电平" - return 0 -} - -gpio_set_active() { - local gpio_dir - gpio_dir="$(gpio_path)" - if [ ! -d "$gpio_dir" ]; then - log_msg "ERR" "GPIO${RESET_GPIO} 不存在, 无法操作" - return 1 - fi - if [ "$RESET_ACTIVE_HIGH" -eq 1 ]; then - echo 1 > "${gpio_dir}/value" - else - echo 0 > "${gpio_dir}/value" - fi -} - -gpio_set_inactive() { - local gpio_dir - gpio_dir="$(gpio_path)" - if [ ! -d "$gpio_dir" ]; then - return 1 - fi - if [ "$RESET_ACTIVE_HIGH" -eq 1 ]; then - echo 0 > "${gpio_dir}/value" - else - echo 1 > "${gpio_dir}/value" - fi -} - -# ---- HPM USB 枚举检测 -------------------------------------------------------- -# 返回 0 = HPM USB 设备正常枚举 -# 返回 1 = HPM 设备未找到 (需要复位) -check_hpm_usb() { - # 方法 1: lsusb — 最可靠 - if command -v lsusb &>/dev/null; then - if lsusb -d "${HPM_VID}:${HPM_PID}" 2>/dev/null | grep -q .; then - return 0 - fi - fi - - # 方法 2: sysfs usb 设备树 — 无需额外工具 - # 遍历 /sys/bus/usb/devices/ 查找 idVendor/idProduct - for dev in /sys/bus/usb/devices/*/; do - local vid pid - vid="$(cat "${dev}idVendor" 2>/dev/null || true)" - pid="$(cat "${dev}idProduct" 2>/dev/null || true)" - if [ "$vid" = "$HPM_VID" ] && [ "$pid" = "$HPM_PID" ]; then - return 0 - fi - done - - # 方法 3: /proc/bus/usb/devices (兼容旧内核) - if [ -f /proc/bus/usb/devices ]; then - if grep -q "Vendor=${HPM_VID}.*ProdID=${HPM_PID}" /proc/bus/usb/devices 2>/dev/null; then - return 0 - fi - fi - - log_msg "WARN" "HPM USB 设备 ${HPM_VID}:${HPM_PID} (roboto_usb4can) 未枚举" - return 1 -} - -# ---- 复位操作 ---------------------------------------------------------------- -pulse_reset() { - log_msg "WARN" "=== 触发 HPM 复位: GPIO${RESET_GPIO} (GPIO1_B7) ===" - - gpio_set_active - sleep "$RESET_HOLD_TIME" - gpio_set_inactive - - log_msg "INFO" "复位脉冲完成 (${RESET_HOLD_TIME}s), 等待 ${POST_RESET_WAIT}s 让 HPM 重新枚举..." - sleep "$POST_RESET_WAIT" -} - -# ---- 清理 -------------------------------------------------------------------- -cleanup() { - log_msg "INFO" "HPM Reset Monitor 正在退出..." - gpio_set_inactive 2>/dev/null || true - if [ -d "$(gpio_path)" ]; then - echo "$RESET_GPIO" > /sys/class/gpio/unexport 2>/dev/null || true - fi - log_msg "INFO" "HPM Reset Monitor 已停止" - exit 0 -} - -trap cleanup SIGTERM SIGINT SIGHUP - -# ---- 主循环 ------------------------------------------------------------------ -main() { - log_msg "INFO" "==============================================" - log_msg "INFO" "HPM Reset Monitor 启动" - log_msg "INFO" " HPM 设备: USB ${HPM_VID}:${HPM_PID} (roboto_usb4can)" - log_msg "INFO" " GPIO1_B7 → sysfs GPIO${RESET_GPIO}" - log_msg "INFO" " 检测间隔: ${CHECK_INTERVAL}s" - log_msg "INFO" " 失败阈值: ${FAIL_THRESHOLD} 次连续失败后复位" - log_msg "INFO" " 复位脉宽: ${RESET_HOLD_TIME}s (${RESET_ACTIVE_HIGH:-高}电平有效)" - log_msg "INFO" " 复位后等待: ${POST_RESET_WAIT}s" - log_msg "INFO" "==============================================" - - if ! gpio_setup; then - log_msg "ERR" "GPIO 初始化失败, 退出" - exit 1 - fi - - local fail_count=0 - - while true; do - if check_hpm_usb; then - if [ "$fail_count" -gt 0 ]; then - log_msg "INFO" "HPM USB 已恢复枚举 (之前连续失败 ${fail_count} 次)" - fi - fail_count=0 - else - fail_count=$((fail_count + 1)) - log_msg "WARN" "HPM USB 未枚举 (${fail_count}/${FAIL_THRESHOLD})" - - if [ "$fail_count" -ge "$FAIL_THRESHOLD" ]; then - log_msg "ERR" "连续 ${FAIL_THRESHOLD} 次未检测到 HPM USB 设备 — 执行硬件复位" - pulse_reset - fail_count=0 - fi - fi - - sleep "$CHECK_INTERVAL" - done -} - -main "$@" \ No newline at end of file diff --git a/scripts/reset_hpm.sh b/scripts/reset_hpm.sh new file mode 100644 index 0000000..b624f37 --- /dev/null +++ b/scripts/reset_hpm.sh @@ -0,0 +1,202 @@ +#!/bin/bash +#============================================================================== +# HPM Reset Monitor — RoboParty EtherCAT CANFD Master +# +# Continuously monitors the USB enumeration state of the HPM device +# (roboto_usb4can, 1209:2323). If the USB device drops offline or fails +# to enumerate, pulses GPIO1_B7 to hardware-reset the HPM. +# +# Device: roboto_usb4can (wentytwenty) +# VID=1209, PID=2323 +# Kernel driver: gs_usb +# +# GPIO1_B7 on RK3588: +# sysfs GPIO number = Bank1 * 32 + B(1) * 8 + 7 = 32 + 8 + 7 = 47 +# Maps to: /sys/class/gpio/gpio47 +# +# Connection: RK3588 GPIO1_B7 → HPM RST pin (active-high reset) +#============================================================================== + +set -e + +# ---- Configurable parameters (overridable via systemd Environment=) ---------- +RESET_GPIO="${RESET_GPIO:-47}" # GPIO1_B7 sysfs number +CHECK_INTERVAL="${CHECK_INTERVAL:-5}" # Check interval (seconds) +FAIL_THRESHOLD="${FAIL_THRESHOLD:-3}" # Consecutive failure threshold +RESET_HOLD_TIME="${RESET_HOLD_TIME:-0.5}" # Reset pulse duration (seconds) +RESET_ACTIVE_HIGH="${RESET_ACTIVE_HIGH:-0}" # 1=active-high, 0=active-low +POST_RESET_WAIT="${POST_RESET_WAIT:-15}" # Wait time after reset (seconds) +LOG_TAG="hpm-reset-monitor" + +# USB device identification (roboto_usb4can) +HPM_VID="${HPM_VID:-1209}" +HPM_PID="${HPM_PID:-2323}" + +# ---- Logging ----------------------------------------------------------------- +log_msg() { + local level="${1:-INFO}" + shift + logger -t "$LOG_TAG" -p "daemon.${level}" "$*" + printf '[%s] [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$level" "$*" >&2 +} + +# ---- sysfs GPIO operations --------------------------------------------------- +gpio_path() { + echo "/sys/class/gpio/gpio${RESET_GPIO}" +} + +gpio_setup() { + local gpio_dir + gpio_dir="$(gpio_path)" + + if [ ! -d "$gpio_dir" ]; then + echo "$RESET_GPIO" > /sys/class/gpio/export 2>/dev/null || true + for _ in $(seq 1 10); do + [ -d "$gpio_dir" ] && break + sleep 0.1 + done + fi + + if [ ! -d "$gpio_dir" ]; then + log_msg "ERR" "Cannot export GPIO ${RESET_GPIO} (GPIO1_B7), check device tree conflicts" + return 1 + fi + + echo "out" > "${gpio_dir}/direction" 2>/dev/null || true + + if [ "$RESET_ACTIVE_HIGH" -eq 1 ]; then + echo 0 > "${gpio_dir}/value" + else + echo 1 > "${gpio_dir}/value" + fi + + log_msg "INFO" "GPIO${RESET_GPIO} (GPIO1_B7) initialized, direction=out, initial=inactive" + return 0 +} + +gpio_set_active() { + local gpio_dir + gpio_dir="$(gpio_path)" + if [ ! -d "$gpio_dir" ]; then + log_msg "ERR" "GPIO${RESET_GPIO} not found, cannot operate" + return 1 + fi + if [ "$RESET_ACTIVE_HIGH" -eq 1 ]; then + echo 1 > "${gpio_dir}/value" + else + echo 0 > "${gpio_dir}/value" + fi +} + +gpio_set_inactive() { + local gpio_dir + gpio_dir="$(gpio_path)" + if [ ! -d "$gpio_dir" ]; then + return 1 + fi + if [ "$RESET_ACTIVE_HIGH" -eq 1 ]; then + echo 0 > "${gpio_dir}/value" + else + echo 1 > "${gpio_dir}/value" + fi +} + +# ---- HPM USB enumeration detection ------------------------------------------- +# Returns 0 = HPM USB device enumerated +# Returns 1 = HPM device not found (needs reset) +check_hpm_usb() { + # Method 1: lsusb — most reliable + if command -v lsusb &>/dev/null; then + if lsusb -d "${HPM_VID}:${HPM_PID}" 2>/dev/null | grep -q .; then + return 0 + fi + fi + + # Method 2: sysfs USB device tree — no extra tools needed + # Search /sys/bus/usb/devices/ for matching idVendor/idProduct + for dev in /sys/bus/usb/devices/*/; do + local vid pid + vid="$(cat "${dev}idVendor" 2>/dev/null || true)" + pid="$(cat "${dev}idProduct" 2>/dev/null || true)" + if [ "$vid" = "$HPM_VID" ] && [ "$pid" = "$HPM_PID" ]; then + return 0 + fi + done + + # Method 3: /proc/bus/usb/devices (legacy kernel compatibility) + if [ -f /proc/bus/usb/devices ]; then + if grep -q "Vendor=${HPM_VID}.*ProdID=${HPM_PID}" /proc/bus/usb/devices 2>/dev/null; then + return 0 + fi + fi + + log_msg "WARN" "HPM USB device ${HPM_VID}:${HPM_PID} (roboto_usb4can) not enumerated" + return 1 +} + +# ---- Reset operation --------------------------------------------------------- +pulse_reset() { + log_msg "WARN" "=== Triggering HPM reset: GPIO${RESET_GPIO} (GPIO1_B7) ===" + + gpio_set_active + sleep "$RESET_HOLD_TIME" + gpio_set_inactive + + log_msg "INFO" "Reset pulse complete (${RESET_HOLD_TIME}s), waiting ${POST_RESET_WAIT}s for HPM re-enumeration..." + sleep "$POST_RESET_WAIT" +} + +# ---- Cleanup ----------------------------------------------------------------- +cleanup() { + log_msg "INFO" "HPM Reset Monitor shutting down..." + gpio_set_inactive 2>/dev/null || true + if [ -d "$(gpio_path)" ]; then + echo "$RESET_GPIO" > /sys/class/gpio/unexport 2>/dev/null || true + fi + log_msg "INFO" "HPM Reset Monitor stopped" + exit 0 +} + +trap cleanup SIGTERM SIGINT SIGHUP + +# ---- Main loop --------------------------------------------------------------- +main() { + log_msg "INFO" "==============================================" + log_msg "INFO" "HPM Reset Monitor starting" + log_msg "INFO" " HPM device: USB ${HPM_VID}:${HPM_PID} (roboto_usb4can)" + log_msg "INFO" " GPIO1_B7 → sysfs GPIO${RESET_GPIO}" + log_msg "INFO" " Check interval: ${CHECK_INTERVAL}s" + log_msg "INFO" " Failure threshold: ${FAIL_THRESHOLD} consecutive failures" + log_msg "INFO" " Reset pulse: ${RESET_HOLD_TIME}s (active-${RESET_ACTIVE_HIGH:-high})" + log_msg "INFO" " Post-reset wait: ${POST_RESET_WAIT}s" + log_msg "INFO" "==============================================" + + if ! gpio_setup; then + log_msg "ERR" "GPIO initialization failed, exiting" + exit 1 + fi + + local fail_count=0 + + while true; do + if check_hpm_usb; then + if [ "$fail_count" -gt 0 ]; then + log_msg "INFO" "HPM USB re-enumerated (${fail_count} previous failure(s))" + fi + fail_count=0 + else + fail_count=$((fail_count + 1)) + log_msg "WARN" "HPM USB not enumerated (${fail_count}/${FAIL_THRESHOLD})" + + if [ "$fail_count" -ge "$FAIL_THRESHOLD" ]; then + log_msg "ERR" "${FAIL_THRESHOLD} consecutive HPM USB detection failures — triggering hardware reset" + pulse_reset + fail_count=0 + fi + fi + + sleep "$CHECK_INTERVAL" + done +} + +main "$@" diff --git a/scripts/wifi-reconnect.sh b/scripts/wifi-reconnect.sh new file mode 100644 index 0000000..4422fc2 --- /dev/null +++ b/scripts/wifi-reconnect.sh @@ -0,0 +1,222 @@ +#!/bin/bash +#============================================================================== +# WiFi Reconnect Monitor — RoboParty +# +# Continuously monitors WiFi connection state in the background. +# If WiFi disconnects, automatically reconnects using locally stored +# WiFi profiles. Reports an error after 3 consecutive reconnect failures. +# +# Dependencies: NetworkManager (nmcli) +#============================================================================== + +set -e + +# ---- Configurable parameters (overridable via systemd Environment=) ---------- +CHECK_INTERVAL="${CHECK_INTERVAL:-30}" # Check interval (seconds) +FAIL_THRESHOLD="${FAIL_THRESHOLD:-3}" # Consecutive failure threshold +WIFI_INTERFACE="${WIFI_INTERFACE:-wlan0}" # WiFi interface name +LOG_TAG="wifi-reset-monitor" + +# ---- Logging ----------------------------------------------------------------- +log_msg() { + local level="${1:-INFO}" + shift + logger -t "$LOG_TAG" -p "daemon.${level}" "$*" + printf '[%s] [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$level" "$*" >&2 +} + +# ---- WiFi connection state detection ----------------------------------------- +# Returns 0 = WiFi connected +# Returns 1 = WiFi disconnected +check_wifi() { + # Method 1: nmcli — check specific WiFi device state + if command -v nmcli &>/dev/null; then + local wifi_state + wifi_state=$(nmcli -t -f GENERAL.STATE device show "$WIFI_INTERFACE" 2>/dev/null | cut -d: -f2) + if [ "$wifi_state" = "100" ]; then + # 100 = NM_DEVICE_STATE_ACTIVATED + return 0 + fi + fi + + # Method 2: iw — check if associated with an AP + if command -v iw &>/dev/null; then + if iw dev "$WIFI_INTERFACE" link 2>/dev/null | grep -q "Connected to"; then + return 0 + fi + fi + + # Method 3: check if the interface has an IP address (last resort) + if ip addr show "$WIFI_INTERFACE" 2>/dev/null | grep -q "inet "; then + return 0 + fi + + log_msg "WARN" "WiFi interface ${WIFI_INTERFACE} is disconnected" + return 1 +} + +# ---- WiFi reconnect ---------------------------------------------------------- +# Returns 0 = reconnect succeeded +# Returns 1 = reconnect failed +reconnect_wifi() { + log_msg "INFO" "Attempting to reconnect WiFi..." + + # Method 1: use nmcli saved connections + if command -v nmcli &>/dev/null; then + # Ensure WiFi device is enabled + nmcli radio wifi on 2>/dev/null || true + nmcli device set "$WIFI_INTERFACE" managed true 2>/dev/null || true + + # Get all saved WiFi connection names + local connections + connections=$(nmcli -t -f NAME,TYPE connection show 2>/dev/null | grep ":802-11-wireless" | cut -d: -f1) + + if [ -n "$connections" ]; then + log_msg "INFO" "Found $(echo "$connections" | wc -l) saved WiFi connection(s)" + + while IFS= read -r conn; do + [ -z "$conn" ] && continue + log_msg "INFO" "Trying connection: [$conn]" + + if nmcli connection up "$conn" 2>/dev/null; then + # Wait for DHCP to assign an IP + for _ in $(seq 1 15); do + sleep 1 + if ip addr show "$WIFI_INTERFACE" 2>/dev/null | grep -q "inet "; then + log_msg "INFO" "Connected to [$conn] with IP address" + return 0 + fi + done + # nmcli reported success but no IP yet, treat as success + log_msg "INFO" "Connected to [$conn] (waiting for IP assignment)" + return 0 + fi + + log_msg "WARN" "Connection [$conn] failed, trying next..." + done <<< "$connections" + fi + fi + + # Method 2: wpa_cli — reconnect via wpa_supplicant + if command -v wpa_cli &>/dev/null; then + log_msg "INFO" "Attempting reconnect via wpa_supplicant..." + if wpa_cli -i "$WIFI_INTERFACE" reconfigure 2>/dev/null; then + sleep 5 + if check_wifi; then + return 0 + fi + fi + fi + + # Method 3: rescan and retry + if command -v nmcli &>/dev/null; then + log_msg "INFO" "Rescanning WiFi networks..." + nmcli device wifi rescan 2>/dev/null || true + sleep 3 + + # Retry all saved connections + local retry_connections + retry_connections=$(nmcli -t -f NAME,TYPE connection show 2>/dev/null | grep ":802-11-wireless" | cut -d: -f1) + while IFS= read -r conn; do + [ -z "$conn" ] && continue + if nmcli connection up "$conn" 2>/dev/null; then + log_msg "INFO" "Connected to [$conn] after rescan" + return 0 + fi + done <<< "$retry_connections" + fi + + log_msg "ERR" "All reconnect methods failed" + return 1 +} + +# ---- Error reporting --------------------------------------------------------- +report_failure() { + local msg="WiFi reconnect failed! ${FAIL_THRESHOLD} consecutive attempts could not restore the connection. Please check the wireless network." + log_msg "ERR" "$msg" + + # Multiple reporting channels + + # 1. Desktop notification (X11/Wayland) + if command -v notify-send &>/dev/null; then + for disp in /tmp/.X11-unix/X*; do + local dnum + dnum=$(basename "$disp" | sed 's/^X//') + DISPLAY=":${dnum}" notify-send -u critical "WiFi Reconnect Failed" "$msg" 2>/dev/null || true + done + fi + + # 2. wall broadcast to all terminals + if command -v wall &>/dev/null; then + echo "[WiFi Reset Monitor] ${msg}" | wall 2>/dev/null || true + fi + + # 3. Write marker file for external monitoring + mkdir -p /var/run/roboparty + echo "$(date '+%Y-%m-%d %H:%M:%S') ${msg}" > /var/run/roboparty/wifi-failure +} + +# ---- Cleanup ----------------------------------------------------------------- +cleanup() { + log_msg "INFO" "WiFi Reset Monitor shutting down..." + rm -f /var/run/roboparty/wifi-failure + log_msg "INFO" "WiFi Reset Monitor stopped" + exit 0 +} + +trap cleanup SIGTERM SIGINT SIGHUP + +# ---- Main loop --------------------------------------------------------------- +main() { + log_msg "INFO" "==============================================" + log_msg "INFO" "WiFi Reset Monitor starting" + log_msg "INFO" " Interface: ${WIFI_INTERFACE}" + log_msg "INFO" " Check interval: ${CHECK_INTERVAL}s" + log_msg "INFO" " Failure threshold: ${FAIL_THRESHOLD} consecutive failures" + log_msg "INFO" "==============================================" + + # Verify WiFi interface exists + if ! ip link show "$WIFI_INTERFACE" &>/dev/null; then + log_msg "WARN" "WiFi interface ${WIFI_INTERFACE} not found, attempting auto-detection..." + local detected + detected=$(iw dev 2>/dev/null | grep "Interface" | head -1 | awk '{print $2}') + if [ -n "$detected" ]; then + WIFI_INTERFACE="$detected" + log_msg "INFO" "Auto-detected WiFi interface: ${WIFI_INTERFACE}" + else + log_msg "ERR" "No WiFi interface found, exiting" + exit 1 + fi + fi + + local fail_count=0 + + while true; do + if check_wifi; then + if [ "$fail_count" -gt 0 ]; then + log_msg "INFO" "WiFi connection restored (${fail_count} previous failure(s))" + fi + fail_count=0 + # Clear failure marker + rm -f /var/run/roboparty/wifi-failure + else + fail_count=$((fail_count + 1)) + log_msg "WARN" "WiFi disconnected (${fail_count}/${FAIL_THRESHOLD})" + + # Attempt reconnect on every disconnect + if reconnect_wifi; then + log_msg "INFO" "WiFi reconnected successfully" + fail_count=0 + rm -f /var/run/roboparty/wifi-failure + elif [ "$fail_count" -ge "$FAIL_THRESHOLD" ]; then + report_failure + # Reset counter to keep monitoring (avoid spamming errors) + fail_count=0 + fi + fi + + sleep "$CHECK_INTERVAL" + done +} + +main "$@" diff --git a/wifi_reset.service b/wifi_reset.service deleted file mode 100644 index e69de29..0000000