firmware: переработать update_fw.sh — host MCU, выбор целей, проверки
- добавлен host_linux.config (снимок ~/klipper/.config) и прошивка klipper-mcu через make flash - цели host|mcu|toolhead, --build-only, --force - set -euo pipefail, сборка всех целей до прошивки, klipper всегда запускается обратно - проверка print_stats, ожидание katapult serial (glob) и возврата UUID на can0 - сверка mcu_version с klippy после старта, лог в ~/firmware Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+253
-41
@@ -1,52 +1,264 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# Сборка и прошивка Klipper на всех MCU принтера leo-voron:
|
||||
# host — linux-процесс klipper_mcu на CB2 (/usr/local/bin/klipper_mcu, сервис klipper-mcu)
|
||||
# mcu — Octopus Pro 1.1 (stm32h723, USB-CAN bridge; прошивается по USB через katapult)
|
||||
# toolhead — EBB SB2209 (rp2040; прошивается по CAN через katapult)
|
||||
#
|
||||
# Использование:
|
||||
# update_fw.sh [-h] [--build-only] [--force] [host|mcu|toolhead ...]
|
||||
# без целей — все три
|
||||
# --build-only — только собрать прошивки в ~/firmware, ничего не прошивать
|
||||
# --force — не останавливаться, если Moonraker недоступен для проверки печати
|
||||
#
|
||||
# Исходники Klipper НЕ обновляются (это делает Moonraker Update Manager).
|
||||
# Скрипт интерактивный: пароль sudo спрашивается один раз в начале.
|
||||
#
|
||||
# Полезные команды:
|
||||
# make menuconfig KCONFIG_CONFIG=/home/biqu/printer_data/config/firmware/octopus-pro.config
|
||||
# make flash FLASH_DEVICE=0483:df11 # stm32 в DFU
|
||||
# python3 ~/katapult/scripts/flashtool.py -i can0 -q # список узлов на CAN
|
||||
# python3 ~/katapult/scripts/flashtool.py -i can0 -u <uuid> -r # запрос бутлоадера
|
||||
# make flash FLASH_DEVICE=/dev/serial/by-id/usb-Klipper_rp2040_45474E621A87A43A-if00 KCONFIG_CONFIG=.../hbb.cfg
|
||||
#
|
||||
# Старые тулхеды (UUID):
|
||||
# rp_sb2209 = 350f10596216
|
||||
# sht36_v2 = 8ce295109ef0
|
||||
|
||||
#Полезные комманды
|
||||
#make menuconfig KCONFIG_CONFIG=/home/biqu/printer_data/config/firmware/config.manta723.CAN
|
||||
#make flash FLASH_DEVICE=0483:df11
|
||||
#python3 flash_can.py -i can0 -u 6c3c86ee0b29 -f ~/firmware/octopus_klipper.bin
|
||||
#python3 ~/katapult/scripts/flashtool.py -i can0 -q
|
||||
#make flash FLASH_DEVICE=/dev/serial/by-id/usb-Klipper_rp2040_45474E621A87A43A-if00 KCONFIG_CONFIG=/home/biqu/printer_data/config/firmware/hbb.cfg
|
||||
set -euo pipefail
|
||||
|
||||
#Toolheads
|
||||
#rp_sb2209=350f10596216
|
||||
#sht36_v2=8ce295109ef0
|
||||
# ---------- Константы ----------
|
||||
MOONRAKER="http://127.0.0.1:7125"
|
||||
KLIPPER="$HOME/klipper"
|
||||
KATAPULT="$HOME/katapult/scripts/flashtool.py"
|
||||
CONFIG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
OUT_DIR="$HOME/firmware"
|
||||
KLIPPY_LOG="$HOME/printer_data/logs/klippy.log"
|
||||
CAN_IF="can0"
|
||||
JOBS=4
|
||||
|
||||
toolhead_name=ebb_rp2240
|
||||
toolhead_uuid=d7e70bd38059
|
||||
mcu_uuid=afcf80281237
|
||||
mcu_path=/dev/serial/by-id/usb-katapult_stm32h723xx_320006001751313431393536-if00
|
||||
config_path=/home/biqu/printer_data/config/firmware/
|
||||
MCU_UUID="afcf80281237"
|
||||
MCU_CFG="octopus-pro.config"
|
||||
KATAPULT_SERIAL_GLOB='/dev/serial/by-id/usb-katapult_stm32h723xx_*'
|
||||
|
||||
mkdir -p ~/firmware
|
||||
TOOLHEAD_UUID="d7e70bd38059"
|
||||
TOOLHEAD_CFG="ebb_rp2240.CAN"
|
||||
|
||||
sudo service klipper stop
|
||||
HOST_CFG="host_linux.config"
|
||||
HOST_BIN="/usr/local/bin/klipper_mcu"
|
||||
|
||||
cd ~/klipper
|
||||
make clean
|
||||
make -j4 KCONFIG_CONFIG="${config_path}octopus-pro.config"
|
||||
mv ~/klipper/out/klipper.bin ~/firmware/octopus_klipper.bin
|
||||
ALL_TARGETS=(host mcu toolhead)
|
||||
|
||||
make clean
|
||||
toolhead_config="${config_path}/${toolhead_name}.CAN"
|
||||
make -j4 KCONFIG_CONFIG=$toolhead_config
|
||||
mv ~/klipper/out/klipper.bin ~/firmware/toolhead_klipper.bin
|
||||
# ---------- Разбор аргументов ----------
|
||||
BUILD_ONLY=0
|
||||
FORCE=0
|
||||
TARGETS=()
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-h|--help) sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
--build-only) BUILD_ONLY=1 ;;
|
||||
--force) FORCE=1 ;;
|
||||
host|mcu|toolhead) TARGETS+=("$arg") ;;
|
||||
*) echo "Неизвестный аргумент: $arg (см. -h)" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
[ ${#TARGETS[@]} -eq 0 ] && TARGETS=("${ALL_TARGETS[@]}")
|
||||
want() { local t; for t in "${TARGETS[@]}"; do [ "$t" = "$1" ] && return 0; done; return 1; }
|
||||
|
||||
cd ~/katapult/scripts
|
||||
# Update MCU Octopus Pro
|
||||
echo "Start update MCU Octopus Pro"
|
||||
python3 ~/katapult/scripts/flashtool.py -i can0 -u $mcu_uuid -r
|
||||
python3 flash_can.py -f ~/firmware/octopus_klipper.bin -d $mcu_path
|
||||
sleep 2
|
||||
#read -p "MCU Manta M5P firmware flashed, please check above for any errors. Press [Enter] to continue, or [Ctrl+C] to abort"
|
||||
#echo "Finish update MCU octopus"
|
||||
# ---------- Лог и трапы ----------
|
||||
mkdir -p "$OUT_DIR"
|
||||
LOG="$OUT_DIR/update_fw-$(date +%Y%m%d-%H%M).log"
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
START_TS=$(date +%s)
|
||||
STAGE="инициализация"
|
||||
KLIPPER_STOPPED=0
|
||||
|
||||
# Update Toolhead MCU
|
||||
echo "Start update Toolhead MCU"
|
||||
python3 flash_can.py -i can0 -u $toolhead_uuid -f ~/firmware/toolhead_klipper.bin
|
||||
sleep 2
|
||||
#read -p "MCU EBB36 firmware flashed, please check above for any errors. Press [Enter] to continue, or [Ctrl+C] to abort"
|
||||
#echo "Finish update MCU EBB36"
|
||||
on_err() {
|
||||
echo
|
||||
echo "!!! ОШИБКА на этапе: $STAGE (лог: $LOG)" >&2
|
||||
}
|
||||
on_exit() {
|
||||
local rc=$?
|
||||
if [ "$KLIPPER_STOPPED" = 1 ]; then
|
||||
echo; echo "=== Запуск klipper ==="
|
||||
sudo systemctl start klipper || echo "!!! Не удалось запустить klipper — запустите вручную: sudo systemctl start klipper" >&2
|
||||
fi
|
||||
[ $rc -ne 0 ] && echo "Завершено с ошибкой (код $rc)."
|
||||
exit $rc
|
||||
}
|
||||
trap on_err ERR
|
||||
trap on_exit EXIT
|
||||
|
||||
python3 ~/katapult/scripts/flashtool.py -i can0 -q
|
||||
section() { echo; echo "=== $* ==="; }
|
||||
die() { echo "!!! $*" >&2; exit 1; }
|
||||
|
||||
sudo service klipper start
|
||||
# Ждать, пока команда-условие не вернёт 0 (таймаут в секундах)
|
||||
wait_for() {
|
||||
local timeout=$1 msg=$2; shift 2
|
||||
local i
|
||||
for ((i = 0; i < timeout; i++)); 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"; }
|
||||
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" ]; }
|
||||
mcu_versions() {
|
||||
curl -s --max-time 5 "$MOONRAKER/printer/objects/query?mcu&mcu%20EBBCan&mcu%20host" \
|
||||
| jq -r '.result.status | to_entries[] | "\(.key)\t\(.value.mcu_version // "?")\t\(.value.mcu_constants.MCU // "?")"' 2>/dev/null \
|
||||
|| echo "недоступно"
|
||||
}
|
||||
|
||||
# ---------- Preflight ----------
|
||||
STAGE="предварительные проверки"
|
||||
section "Preflight"
|
||||
echo "Цели: ${TARGETS[*]} build-only=$BUILD_ONLY лог: $LOG"
|
||||
|
||||
[ -d "$KLIPPER" ] || die "Нет исходников Klipper: $KLIPPER"
|
||||
[ -f "$KATAPULT" ] || die "Нет katapult flashtool: $KATAPULT"
|
||||
want host && { [ -f "$CONFIG_DIR/$HOST_CFG" ] || die "Нет конфига $CONFIG_DIR/$HOST_CFG"; }
|
||||
want mcu && { [ -f "$CONFIG_DIR/$MCU_CFG" ] || die "Нет конфига $CONFIG_DIR/$MCU_CFG"; }
|
||||
want toolhead && { [ -f "$CONFIG_DIR/$TOOLHEAD_CFG" ] || die "Нет конфига $CONFIG_DIR/$TOOLHEAD_CFG"; }
|
||||
|
||||
KLIPPER_VER=$(git -C "$KLIPPER" describe --always --tags --long --dirty 2>/dev/null || echo unknown)
|
||||
echo "Klipper в $KLIPPER: $KLIPPER_VER"
|
||||
|
||||
BEFORE=""
|
||||
if [ "$BUILD_ONLY" = 0 ]; then
|
||||
if want mcu || want toolhead; then
|
||||
ip -br link show "$CAN_IF" 2>/dev/null | grep -q " UP " || die "$CAN_IF не поднят"
|
||||
fi
|
||||
|
||||
# Печать не должна идти
|
||||
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 [ "$FORCE" = 1 ]; then
|
||||
echo "Moonraker недоступен, проверка печати пропущена (--force)"
|
||||
else
|
||||
die "Moonraker недоступен — не могу проверить, идёт ли печать (--force чтобы пропустить)"
|
||||
fi
|
||||
else
|
||||
echo "print_stats.state = $PRINT_STATE"
|
||||
case "$PRINT_STATE" in
|
||||
printing|paused) die "Идёт печать ($PRINT_STATE) — прошивка отменена" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo "Версии MCU до обновления:"
|
||||
BEFORE=$(mcu_versions); echo "$BEFORE" | sed 's/^/ /'
|
||||
|
||||
# Пароль sudo — один раз, до остановки сервисов
|
||||
sudo -v || die "Нужны права sudo"
|
||||
fi
|
||||
|
||||
# ---------- Сборка ----------
|
||||
build() { # build <цель> <конфиг> <артефакт: bin|elf>
|
||||
local name=$1 cfg=$CONFIG_DIR/$2 ext=$3
|
||||
STAGE="сборка $name"
|
||||
section "Сборка $name ($2)"
|
||||
make -C "$KLIPPER" clean KCONFIG_CONFIG="$cfg" >/dev/null
|
||||
make -C "$KLIPPER" olddefconfig KCONFIG_CONFIG="$cfg"
|
||||
make -C "$KLIPPER" -j"$JOBS" KCONFIG_CONFIG="$cfg"
|
||||
local src="$KLIPPER/out/klipper.$ext"
|
||||
[ -s "$src" ] || die "Сборка $name не дала $src"
|
||||
local dst="$OUT_DIR/${name}-${KLIPPER_VER}.$ext"
|
||||
cp "$src" "$dst"
|
||||
ln -sfn "$(basename "$dst")" "$OUT_DIR/${name}_klipper.$ext"
|
||||
echo "-> $dst ($(stat -c %s "$dst") байт)"
|
||||
}
|
||||
|
||||
# host собирается последним: его out/ нужен для make flash
|
||||
want mcu && build mcu "$MCU_CFG" bin
|
||||
want toolhead && build toolhead "$TOOLHEAD_CFG" bin
|
||||
want host && build host "$HOST_CFG" elf
|
||||
|
||||
if [ "$BUILD_ONLY" = 1 ]; then
|
||||
section "Готово (только сборка)"
|
||||
ls -l "$OUT_DIR"/*_klipper.* 2>/dev/null
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ---------- Прошивка ----------
|
||||
STAGE="остановка klipper"
|
||||
section "Остановка klipper"
|
||||
sudo systemctl stop klipper
|
||||
KLIPPER_STOPPED=1
|
||||
|
||||
if want host; then
|
||||
STAGE="прошивка host MCU"
|
||||
section "Прошивка host (klipper-mcu)"
|
||||
# flash-linux.sh копирует out/klipper.elf в /usr/local/bin/klipper_mcu и перезапускает klipper-mcu
|
||||
make -C "$KLIPPER" flash KCONFIG_CONFIG="$CONFIG_DIR/$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 обновлён"
|
||||
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"
|
||||
python3 "$KATAPULT" -d "$DEV" -f "$OUT_DIR/mcu_klipper.bin"
|
||||
# После прошивки плата перезагружается в Klipper и снова поднимает USB-CAN мост
|
||||
wait_for 30 "Octopus не вернулся на $CAN_IF после прошивки" can_has_uuid "$MCU_UUID"
|
||||
echo "Octopus снова на $CAN_IF"
|
||||
fi
|
||||
|
||||
if want toolhead; then
|
||||
STAGE="прошивка тулхеда"
|
||||
section "Прошивка toolhead (EBB SB2209, uuid $TOOLHEAD_UUID)"
|
||||
can_has_uuid "$TOOLHEAD_UUID" || die "Тулхед ($TOOLHEAD_UUID) не виден на $CAN_IF"
|
||||
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"
|
||||
fi
|
||||
|
||||
section "Узлы на $CAN_IF"
|
||||
python3 "$KATAPULT" -i "$CAN_IF" -q || true
|
||||
|
||||
# ---------- Запуск и проверка ----------
|
||||
STAGE="запуск klipper"
|
||||
section "Запуск klipper"
|
||||
sudo systemctl start klipper
|
||||
KLIPPER_STOPPED=0
|
||||
|
||||
STAGE="проверка после прошивки"
|
||||
if wait_for 90 "klippy не перешёл в ready" klippy_ready; then
|
||||
echo "klippy: ready"
|
||||
else
|
||||
echo "klippy state: $(klippy_state)"
|
||||
curl -s --max-time 5 "$MOONRAKER/printer/info" | jq -r '.result.state_message // empty' 2>/dev/null || true
|
||||
echo "--- хвост $KLIPPY_LOG ---"
|
||||
tail -n 40 "$KLIPPY_LOG" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
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 tail -n 200 "$KLIPPY_LOG" 2>/dev/null | grep -E "Command format mismatch|Protocol error|Lost communication" | tail -5 | grep .; then
|
||||
echo "!!! В klippy.log есть ошибки протокола (см. выше)"; MISMATCH=1
|
||||
fi
|
||||
|
||||
if [ "$MISMATCH" = 0 ]; then
|
||||
echo; echo "Готово: все MCU на $KLIPPY_VER. Дальше: QUAD_GANTRY_LEVEL и проверить TAP/z_offset."
|
||||
else
|
||||
echo; echo "Есть расхождения — проверьте вывод выше; при необходимости FIRMWARE_RESTART или повторить: $0 <цель>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user