From a78afa3ec483e7e5aaf3a9ca30362161a27be0ef Mon Sep 17 00:00:00 2001 From: Mikhail Shahovalov Date: Thu, 3 Sep 2026 05:15:22 +0300 Subject: [PATCH] =?UTF-8?q?firmware/update=5Ffw.sh:=20=D0=B8=D1=81=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - can_has_uuid: без pipefail-ловушки grep -q (flashtool -q падал по EPIPE) - убраны пред-проверки -q перед -r: Klipper с назначенным node id не отвечает на запрос UUID - tee игнорирует SIGINT, trap INT — по Ctrl-C klipper всё равно запускается - set -E: trap ERR срабатывает внутри функций; die печатает этап - preflight различает «Moonraker недоступен» и «klippy не ready» - host: проверка смены PID klipper-mcu и наличия /tmp/klipper_host_mcu - wait_for по реальному времени; klippy.log проверяется только после последнего старта Co-Authored-By: Claude Opus 5 --- firmware/update_fw.sh | 51 +++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/firmware/update_fw.sh b/firmware/update_fw.sh index d06c65a..b8177ab 100755 --- a/firmware/update_fw.sh +++ b/firmware/update_fw.sh @@ -24,7 +24,7 @@ # rp_sb2209 = 350f10596216 # sht36_v2 = 8ce295109ef0 -set -euo pipefail +set -Eeuo pipefail # ---------- Константы ---------- MOONRAKER="http://127.0.0.1:7125" @@ -54,7 +54,7 @@ FORCE=0 TARGETS=() for arg in "$@"; do case "$arg" in - -h|--help) sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + -h|--help) sed -n '2,15p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;; --build-only) BUILD_ONLY=1 ;; --force) FORCE=1 ;; host|mcu|toolhead) TARGETS+=("$arg") ;; @@ -67,7 +67,7 @@ want() { local t; for t in "${TARGETS[@]}"; do [ "$t" = "$1" ] && return 0; done # ---------- Лог и трапы ---------- mkdir -p "$OUT_DIR" LOG="$OUT_DIR/update_fw-$(date +%Y%m%d-%H%M).log" -exec > >(tee -a "$LOG") 2>&1 +exec > >(trap '' INT; exec tee -a "$LOG") 2>&1 START_TS=$(date +%s) STAGE="инициализация" KLIPPER_STOPPED=0 @@ -88,22 +88,25 @@ on_exit() { } trap on_err ERR trap on_exit EXIT +trap 'exit 130' INT section() { echo; echo "=== $* ==="; } -die() { echo "!!! $*" >&2; exit 1; } +die() { echo "!!! [$STAGE] $*" >&2; exit 1; } # Ждать, пока команда-условие не вернёт 0 (таймаут в секундах) wait_for() { local timeout=$1 msg=$2; shift 2 - local i - for ((i = 0; i < timeout; i++)); do + local deadline=$((SECONDS + timeout)) + while [ "$SECONDS" -lt "$deadline" ]; do if "$@" >/dev/null 2>&1; then return 0; fi sleep 1 done echo "!!! Таймаут ($timeout с): $msg" >&2 return 1 } -can_has_uuid() { python3 "$KATAPULT" -i "$CAN_IF" -q 2>/dev/null | grep -q "$1"; } +# Klipper отвечает на запрос UUID только пока ему не назначен node id (canserial.c), +# поэтому -q годится лишь для узлов, только что перезагруженных после прошивки. +can_has_uuid() { local out; out=$(python3 "$KATAPULT" -i "$CAN_IF" -q 2>/dev/null) || return 1; grep -q "$1" <<< "$out"; } katapult_serial() { local d; for d in $KATAPULT_SERIAL_GLOB; do [ -e "$d" ] && { echo "$d"; return 0; }; done; return 1; } klippy_state() { curl -s --max-time 5 "$MOONRAKER/printer/info" | jq -r '.result.state // empty' 2>/dev/null; } klippy_ready() { [ "$(klippy_state)" = "ready" ]; } @@ -134,14 +137,17 @@ if [ "$BUILD_ONLY" = 0 ]; then fi # Печать не должна идти + KSTATE=$(klippy_state || true) PRINT_STATE=$(curl -s --max-time 5 "$MOONRAKER/printer/objects/query?print_stats" \ | jq -r '.result.status.print_stats.state // empty' 2>/dev/null || true) - if [ -z "$PRINT_STATE" ]; then + if [ -z "$KSTATE" ]; then if [ "$FORCE" = 1 ]; then echo "Moonraker недоступен, проверка печати пропущена (--force)" else die "Moonraker недоступен — не могу проверить, идёт ли печать (--force чтобы пропустить)" fi + elif [ -z "$PRINT_STATE" ]; then + echo "klippy в состоянии '$KSTATE' (не ready) — печать не идёт, продолжаю" else echo "print_stats.state = $PRINT_STATE" case "$PRINT_STATE" in @@ -198,20 +204,22 @@ if want host; then STAGE="прошивка host MCU" section "Прошивка host (klipper-mcu)" # flash-linux.sh копирует out/klipper.elf в /usr/local/bin/klipper_mcu и перезапускает klipper-mcu + HOST_PID_OLD=$(systemctl show -p ExecMainPID --value klipper-mcu) make -C "$KLIPPER" flash KCONFIG_CONFIG="$BUILD_TMP/$HOST_CFG" [ "$(stat -c %Y "$HOST_BIN")" -ge "$START_TS" ] || die "$HOST_BIN не обновился" - wait_for 10 "klipper-mcu не запустился" systemctl is-active --quiet klipper-mcu - echo "klipper-mcu: $(systemctl is-active klipper-mcu), $HOST_BIN обновлён" + host_restarted() { [ "$(systemctl show -p ExecMainPID --value klipper-mcu)" != "$HOST_PID_OLD" ] && [ -e /tmp/klipper_host_mcu ]; } + wait_for 10 "klipper-mcu не перезапустился (PID $HOST_PID_OLD)" host_restarted + echo "klipper-mcu перезапущен (PID $HOST_PID_OLD -> $(systemctl show -p ExecMainPID --value klipper-mcu)), $HOST_BIN обновлён" fi if want mcu; then STAGE="прошивка Octopus Pro" section "Прошивка mcu (Octopus Pro, uuid $MCU_UUID)" - can_has_uuid "$MCU_UUID" || die "Octopus ($MCU_UUID) не виден на $CAN_IF" python3 "$KATAPULT" -i "$CAN_IF" -u "$MCU_UUID" -r wait_for 20 "не появился katapult serial ($KATAPULT_SERIAL_GLOB). Проверьте USB или перезагрузите плату" katapult_serial DEV=$(katapult_serial) echo "katapult serial: $DEV" + sleep 1 # порт может быть ещё не готов сразу после появления симлинка python3 "$KATAPULT" -d "$DEV" -f "$OUT_DIR/mcu_klipper.bin" # После прошивки плата перезагружается в Klipper и снова поднимает USB-CAN мост wait_for 30 "Octopus не вернулся на $CAN_IF после прошивки" can_has_uuid "$MCU_UUID" @@ -221,7 +229,7 @@ fi if want toolhead; then STAGE="прошивка тулхеда" section "Прошивка toolhead (EBB SB2209, uuid $TOOLHEAD_UUID)" - can_has_uuid "$TOOLHEAD_UUID" || die "Тулхед ($TOOLHEAD_UUID) не виден на $CAN_IF" + # flashtool сам отправляет запрос бутлоадера по UUID и заливает прошивку python3 "$KATAPULT" -i "$CAN_IF" -u "$TOOLHEAD_UUID" -f "$OUT_DIR/toolhead_klipper.bin" wait_for 30 "тулхед не вернулся на $CAN_IF после прошивки" can_has_uuid "$TOOLHEAD_UUID" echo "Тулхед снова на $CAN_IF" @@ -250,15 +258,20 @@ section "Версии" KLIPPY_VER=$(curl -s --max-time 5 "$MOONRAKER/printer/info" | jq -r '.result.software_version // "?"' 2>/dev/null || echo "?") echo "klippy (хост): $KLIPPY_VER" AFTER=$(mcu_versions) -printf '%-12s %-32s %s\n' "MCU" "было" "стало" MISMATCH=0 -while IFS=$'\t' read -r name ver _; do - old=$(printf '%s\n' "$BEFORE" | awk -F'\t' -v n="$name" '$1==n{print $2}') - printf '%-12s %-32s %s\n' "$name" "${old:-?}" "$ver" - [ "$ver" = "$KLIPPY_VER" ] || { echo "!!! $name: версия $ver != klippy $KLIPPY_VER"; MISMATCH=1; } -done <<< "$AFTER" +if [ -z "$AFTER" ] || [ "$AFTER" = "недоступно" ]; then + echo "!!! Версии MCU недоступны (klippy не ready)"; MISMATCH=1 +else + printf '%-12s %-32s %s\n' "MCU" "было" "стало" + while IFS=$'\t' read -r name ver _; do + old=$(printf '%s\n' "$BEFORE" | awk -F'\t' -v n="$name" '$1==n{print $2}') + printf '%-12s %-32s %s\n' "$name" "${old:-?}" "$ver" + [ "$ver" = "$KLIPPY_VER" ] || { echo "!!! $name: версия $ver != klippy $KLIPPY_VER"; MISMATCH=1; } + done <<< "$AFTER" +fi -if tail -n 200 "$KLIPPY_LOG" 2>/dev/null | grep -E "Command format mismatch|Protocol error|Lost communication" | tail -5 | grep .; then +# Только строки после последнего старта klippy, чтобы не ловить старые ошибки +if tac "$KLIPPY_LOG" 2>/dev/null | sed '/^Start printer at/q' | grep -E "Command format mismatch|Protocol error|Lost communication" | tail -5 | grep .; then echo "!!! В klippy.log есть ошибки протокола (см. выше)"; MISMATCH=1 fi