From 0a096575741942d9c64f1b3385c42b562de1eec7 Mon Sep 17 00:00:00 2001 From: Arend-Jan van Hilten Date: Wed, 17 Jun 2026 18:03:44 +0200 Subject: [PATCH 1/8] Hold packages that might break overlayed systems --- install_mirte.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/install_mirte.sh b/install_mirte.sh index 798a0f8..8afc185 100755 --- a/install_mirte.sh +++ b/install_mirte.sh @@ -122,6 +122,13 @@ sudo systemctl disable --now unattended-upgrades.service || true # either no net # update time in /etc/fake-hwclock.data sudo fake-hwclock save +# mark some apt packages as hold to not break when upgrading +# when using overlay, we don't want to update the kernel, as it will break some things +# updates are in the overlay, while kernel is started from underlay. + +# wildcards and non-existing packages are ignored, so this is safe to run on all images. +sudo apt-mark hold "linux-dtb-*" "linux-image-*" "linux-u-boot-*" "linux-headers-*" "armbian-plymouth-theme" + # remove force ipv4 sudo rm /etc/apt/apt.conf.d/99force-ipv4 || true sudo rm /etc/resolv.conf || true # remove resolv.conf to use the one from the network. From 280011f3ee50dba28269a28e039fda6c5d84fb76 Mon Sep 17 00:00:00 2001 From: Arend-Jan van Hilten Date: Wed, 17 Jun 2026 18:23:33 +0200 Subject: [PATCH 2/8] separate battery soc check and shutdown msg&pcb shutoff service --- install_mirte_master.sh | 38 +++++------- services/mirte-battery-watcher.service | 13 +++++ services/mirte-shutdown.service | 1 - services/mirte-usb-switch.service | 0 services/mirte_battery_watcher.sh | 80 ++++++++++++++++++++++++++ services/mirte_boot.sh | 15 ----- 6 files changed, 107 insertions(+), 40 deletions(-) create mode 100644 services/mirte-battery-watcher.service mode change 100755 => 100644 services/mirte-shutdown.service mode change 100755 => 100644 services/mirte-usb-switch.service create mode 100755 services/mirte_battery_watcher.sh delete mode 100755 services/mirte_boot.sh diff --git a/install_mirte_master.sh b/install_mirte_master.sh index 6ebf0ff..326e25e 100755 --- a/install_mirte_master.sh +++ b/install_mirte_master.sh @@ -18,17 +18,20 @@ mkdir build cd build cmake .. make -j -sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-usb-switch.service /lib/systemd/system/ -sudo systemctl daemon-reload -sudo systemctl stop mirte-usb-switch.service || /bin/true -sudo systemctl start mirte-usb-switch.service -sudo systemctl enable mirte-usb-switch.service - -sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-shutdown.service /lib/systemd/system/ -sudo systemctl daemon-reload -sudo systemctl stop mirte-shutdown.service || /bin/true -sudo systemctl start mirte-shutdown.service -sudo systemctl enable mirte-shutdown.service + +function add_service() { + service_name=$1 + sudo rm -f /lib/systemd/system/$service_name + sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/$service_name /lib/systemd/system/ + sudo systemctl daemon-reload + sudo systemctl stop $service_name || /bin/true + sudo systemctl start $service_name + sudo systemctl enable $service_name +} + +add_service mirte_battery_watcher.service # check that battery is not empty and shutdown if it is +add_service mirte-shutdown.service # show a message on the screen when shutting down and trigger a shutdown of the robot +add_service mirte-usb-switch.service # turn on/off depth cam usb port. # create a gpio group and add mirte to it. This is needed to access the gpio ports, otherwise only sudo is allowed. sudo groupadd gpiod @@ -41,19 +44,6 @@ pip install gpiod==1.5.4 # python3.8 version pip install gtts playsound openai==0.28.0 sounddevice scipy SpeechRecognition soundfile transformers datasets pyyaml pydub Elevenlabs || true # some strange package versions pip install numpy==1.23.1 # python3.8 fix -# mkdir ~/uboot_fix/ -# cd ~/uboot_fix/ -# # audio fix uboot for orange pi 3b -# if [[ ${type:=""} == "mirte_orangepi3b" ]]; then -# wget https://mirte.arend-jan.com/files/fixes/uboot/linux-u-boot-orangepi3b-edge_24.2.1_arm64__2023.10-S095b-P0000-H264e-V49ed-B11a8-R448a.deb -# sudo apt install ./linux-u-boot-orangepi3b-edge_24.2.1_arm64__2023.10-S095b-P0000-H264e-V49ed-B11a8-R448a.deb -# rm linux-u-boot-orangepi3b-edge_24.2.1_arm64__2023.10-S095b-P0000-H264e-V49ed-B11a8-R448a.deb -# cd ../ -# rm -rf uboot_fix/ -# fi - -# cd ~/mirte_ws/src -# git clone --recurse-submodules https://github.com/arendjan/mirte-telemetrix-cpp.git cd ~/mirte_ws source /opt/ros/humble/setup.bash rosdep install -y --from-paths src/ --ignore-src --rosdistro humble diff --git a/services/mirte-battery-watcher.service b/services/mirte-battery-watcher.service new file mode 100644 index 0000000..feb5292 --- /dev/null +++ b/services/mirte-battery-watcher.service @@ -0,0 +1,13 @@ +[Unit] +Description=Mirte-shutdown +After=mirte-ros.service + +[Service] +User=mirte +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/src/mirte/mirte-install-scripts/services/mirte_battery_watcher.sh +TimeoutSec=30 +[Install] +WantedBy=multi-user.target +WantedBy=mirte-ros.service diff --git a/services/mirte-shutdown.service b/services/mirte-shutdown.service old mode 100755 new mode 100644 index fa425d8..7859acd --- a/services/mirte-shutdown.service +++ b/services/mirte-shutdown.service @@ -6,7 +6,6 @@ After=mirte-ros.service User=mirte Type=oneshot RemainAfterExit=true -ExecStart=/usr/local/src/mirte/mirte-install-scripts/services/mirte_boot.sh ExecStop=/usr/local/src/mirte/mirte-install-scripts/services/mirte_shutdown.sh TimeoutSec=30 [Install] diff --git a/services/mirte-usb-switch.service b/services/mirte-usb-switch.service old mode 100755 new mode 100644 diff --git a/services/mirte_battery_watcher.sh b/services/mirte_battery_watcher.sh new file mode 100755 index 0000000..87a3248 --- /dev/null +++ b/services/mirte_battery_watcher.sh @@ -0,0 +1,80 @@ +#!/bin/bash +set -x +. /home/mirte/.mirte_settings.sh +. /opt/ros/humble/setup.bash +. /home/mirte/mirte_ws/install/setup.bash +SECONDS=0 +LAST_SECONDS=0 +WARN_LVL=0 +mirte_space=$(cat /etc/hostname | tr '[:upper:]' '[:lower:]' | tr '-' '_') + +STOP=false +trap 'echo "Stopping mirte_master_check"; kill $ECHO_PID; STOP=true' SIGTERM SIGINT +# ros2 topic echo restarts when the topic appears again, so we can just echo always +# on every check, it will read the file, take the last reading and then clear the file +# if the sensor is off, then the file will be empty and will trigger shutdown after some time. +topic=/io/power/power_watcher +if [ "$MIRTE_USE_MULTIROBOT" = "true" ]; then + topic=/$mirte_space/io/power/power_watcher +fi +ros2 topic echo $topic sensor_msgs/msg/BatteryState --field percentage >/tmp/batteryState & +ECHO_PID=$! +while ! systemctl list-jobs | grep -q -E 'shutdown.target.*start' && ! $STOP; do + OK=false + + # echo "topics" + percentage=$( + tail -2 /tmp/batteryState | head -1 + ) || true + echo $percentage + true >/tmp/batteryState + # percentage=$(echo "$percentage" | tail -1) + # echo $percentage + # echo $( echo $percentage | wc -c) + if [ "$(echo $percentage | wc -c)" -gt 1 ]; then + # echo "percentage" + percentage=$(echo "$percentage" | awk '{print $NF}') + # echo $percentage + if (($(echo "$percentage > 0.1" | bc -l))); then + OK=true + fi + fi + + if $OK; then + # echo "latest percentage: " + # echo $percentage + SECONDS=0 # update time since last ok + LAST_SECONDS=0 + WARN_LVL=0 + fi + # if there is a time jump and seconds is more than last_seconds+2, reset seconds + # time jumps happen when the time is updated (on connecting to wifi) + next_second=$((LAST_SECONDS + 20)) + if [ $SECONDS -gt $next_second ]; then + echo "time jump" + SECONDS=0 + LAST_SECONDS=0 + WARN_LVL=0 + fi + LAST_SECONDS=$SECONDS + + if [ $SECONDS -gt 300 ] && [ $WARN_LVL -eq 0 ]; then + wall "No ROS for longer than 5min" + WARN_LVL=1 + fi + if [ $SECONDS -gt 600 ] && [ $WARN_LVL -eq 1 ]; then + wall "No ROS for longer than 10min, shutting down in 5min" + WARN_LVL=2 + fi + if [ $SECONDS -gt 900 ] && [ $WARN_LVL -eq 2 ]; then + wall "shutting down" + WARN_LVL=3 + date >/home/mirte/.shutdown_power + sudo shutdown now + fi + sleep 10 +done + +kill $ECHO_PID || true +jobs -p +ros2 daemon stop || true # deamon is sometimes started by ros2 topic echo, otherwise the shutdown will get stuck and wait for 30+ seconds diff --git a/services/mirte_boot.sh b/services/mirte_boot.sh deleted file mode 100755 index 44bf74c..0000000 --- a/services/mirte_boot.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -set -xe -# check if file /home/mirte/shutdown exists -if [ -f /home/mirte/.shutdown ]; then - # check if file /home/mirte/shutdown_done exists - echo "correct shutdown" - rm /home/mirte/.shutdown -else - echo "incorrect shutdown" - touch /home/mirte/.shutdown_incorrect - # append date to shutdown_incorrect file - date >>/home/mirte/.shutdown_incorrect -fi -"$(dirname "$0")/mirte_master_check.sh" & From 78462f4a9d3bee05de990c85d3a7b7a87320de49 Mon Sep 17 00:00:00 2001 From: Arend-Jan van Hilten Date: Wed, 17 Jun 2026 19:00:59 +0200 Subject: [PATCH 3/8] better battery soc check --- services/mirte_battery_watcher.sh | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/services/mirte_battery_watcher.sh b/services/mirte_battery_watcher.sh index 87a3248..be8eae6 100755 --- a/services/mirte_battery_watcher.sh +++ b/services/mirte_battery_watcher.sh @@ -7,7 +7,7 @@ SECONDS=0 LAST_SECONDS=0 WARN_LVL=0 mirte_space=$(cat /etc/hostname | tr '[:upper:]' '[:lower:]' | tr '-' '_') - +BATTERY_FILE=/tmp/batteryState STOP=false trap 'echo "Stopping mirte_master_check"; kill $ECHO_PID; STOP=true' SIGTERM SIGINT # ros2 topic echo restarts when the topic appears again, so we can just echo always @@ -17,17 +17,21 @@ topic=/io/power/power_watcher if [ "$MIRTE_USE_MULTIROBOT" = "true" ]; then topic=/$mirte_space/io/power/power_watcher fi -ros2 topic echo $topic sensor_msgs/msg/BatteryState --field percentage >/tmp/batteryState & +ros2 topic echo $topic sensor_msgs/msg/BatteryState --field percentage >$BATTERY_FILE & ECHO_PID=$! while ! systemctl list-jobs | grep -q -E 'shutdown.target.*start' && ! $STOP; do OK=false - + TOPIC_EXISTS=false + # if file is more than 2 lines, then there is a new reading, otherwise the topic is not publishing + if [ "$(wc -l <$BATTERY_FILE)" -gt 2 ]; then + TOPIC_EXISTS=true + fi # echo "topics" percentage=$( - tail -2 /tmp/batteryState | head -1 + tail -2 $BATTERY_FILE | head -1 ) || true echo $percentage - true >/tmp/batteryState + true >$BATTERY_FILE # percentage=$(echo "$percentage" | tail -1) # echo $percentage # echo $( echo $percentage | wc -c) @@ -37,6 +41,11 @@ while ! systemctl list-jobs | grep -q -E 'shutdown.target.*start' && ! $STOP; do # echo $percentage if (($(echo "$percentage > 0.1" | bc -l))); then OK=true + elif (($(echo "$percentage <= 0.1" | bc -l))); then + wall "Battery is low, shutting down in 2min" + date >>/home/mirte/.shutdown_battery + sudo shutdown +2 + STOP=true fi fi @@ -69,8 +78,9 @@ while ! systemctl list-jobs | grep -q -E 'shutdown.target.*start' && ! $STOP; do if [ $SECONDS -gt 900 ] && [ $WARN_LVL -eq 2 ]; then wall "shutting down" WARN_LVL=3 - date >/home/mirte/.shutdown_power + date >>/home/mirte/.shutdown_power sudo shutdown now + STOP=true fi sleep 10 done From 53da740fde968d3eb823d4ff7492c0b67f6d4114 Mon Sep 17 00:00:00 2001 From: Arend-Jan van Hilten Date: Thu, 18 Jun 2026 10:28:35 +0200 Subject: [PATCH 4/8] Add info battery watcher + fix style --- services/mirte_battery_watcher.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/services/mirte_battery_watcher.sh b/services/mirte_battery_watcher.sh index be8eae6..e1cbd45 100755 --- a/services/mirte_battery_watcher.sh +++ b/services/mirte_battery_watcher.sh @@ -6,6 +6,10 @@ set -x SECONDS=0 LAST_SECONDS=0 WARN_LVL=0 + +# if battery <= 10%, shutdown in 2 min. +# otherwise if longer than 15 min no message, shutdown. + mirte_space=$(cat /etc/hostname | tr '[:upper:]' '[:lower:]' | tr '-' '_') BATTERY_FILE=/tmp/batteryState STOP=false @@ -21,11 +25,6 @@ ros2 topic echo $topic sensor_msgs/msg/BatteryState --field percentage >$BATTERY ECHO_PID=$! while ! systemctl list-jobs | grep -q -E 'shutdown.target.*start' && ! $STOP; do OK=false - TOPIC_EXISTS=false - # if file is more than 2 lines, then there is a new reading, otherwise the topic is not publishing - if [ "$(wc -l <$BATTERY_FILE)" -gt 2 ]; then - TOPIC_EXISTS=true - fi # echo "topics" percentage=$( tail -2 $BATTERY_FILE | head -1 From ac51e0b3d01a3dbe5557fa6f9b5e0b9466b24490 Mon Sep 17 00:00:00 2001 From: Arend-Jan van Hilten Date: Wed, 29 Jul 2026 12:39:25 +0200 Subject: [PATCH 5/8] Add armbian-firmware to apt hold --- install_mirte.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install_mirte.sh b/install_mirte.sh index 8afc185..06c0919 100755 --- a/install_mirte.sh +++ b/install_mirte.sh @@ -127,7 +127,7 @@ sudo fake-hwclock save # updates are in the overlay, while kernel is started from underlay. # wildcards and non-existing packages are ignored, so this is safe to run on all images. -sudo apt-mark hold "linux-dtb-*" "linux-image-*" "linux-u-boot-*" "linux-headers-*" "armbian-plymouth-theme" +sudo apt-mark hold "linux-dtb-*" "linux-image-*" "linux-u-boot-*" "linux-headers-*" "armbian-plymouth-theme" "armbian-firmware" # remove force ipv4 sudo rm /etc/apt/apt.conf.d/99force-ipv4 || true From f0220b1f81b30a7436866b44cecb127260b9ebd2 Mon Sep 17 00:00:00 2001 From: Arend-Jan van Hilten Date: Sun, 2 Aug 2026 13:32:21 +0200 Subject: [PATCH 6/8] Fix battery watcher script installing --- install_mirte_master.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/install_mirte_master.sh b/install_mirte_master.sh index 326e25e..d999481 100755 --- a/install_mirte_master.sh +++ b/install_mirte_master.sh @@ -21,6 +21,11 @@ make -j function add_service() { service_name=$1 + # check if service file exists in install scripts, if not, exit with error + if [[ ! -f $MIRTE_SRC_DIR/mirte-install-scripts/services/$service_name ]]; then + echo "Service file $service_name does not exist in $MIRTE_SRC_DIR/mirte-install-scripts/services/" + exit 1 + fi sudo rm -f /lib/systemd/system/$service_name sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/$service_name /lib/systemd/system/ sudo systemctl daemon-reload @@ -29,7 +34,7 @@ function add_service() { sudo systemctl enable $service_name } -add_service mirte_battery_watcher.service # check that battery is not empty and shutdown if it is +add_service mirte-battery-watcher.service # check that battery is not empty and shutdown if it is add_service mirte-shutdown.service # show a message on the screen when shutting down and trigger a shutdown of the robot add_service mirte-usb-switch.service # turn on/off depth cam usb port. From 9e09c9faedaba97a2b850b19a44152cc27a11fea Mon Sep 17 00:00:00 2001 From: Arend-Jan van Hilten Date: Mon, 17 Aug 2026 16:43:15 +0200 Subject: [PATCH 7/8] update node versions + use new generate command instead of build --- install_web.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install_web.sh b/install_web.sh index 82a19ff..874ad48 100755 --- a/install_web.sh +++ b/install_web.sh @@ -9,8 +9,8 @@ sudo apt update || true sudo apt install -y python3-pip python3-setuptools python3-wheel sudo -H pip install nodeenv -# Install nodeenv -nodeenv --node=18.0.0 $MIRTE_SRC_DIR/mirte-web-interface/node_env +# Install node, lts 22 is tested by the front-end, will need to update after april 2027 https://github.com/nodejs/release#release-schedule +nodeenv --node=22. $MIRTE_SRC_DIR/mirte-web-interface/node_env # Install web interface . $MIRTE_SRC_DIR/mirte-web-interface/node_env/bin/activate @@ -19,7 +19,7 @@ nodeenv --node=18.0.0 $MIRTE_SRC_DIR/mirte-web-interface/node_env cd $MIRTE_SRC_DIR/mirte-web-interface/vue-frontend || exit 1 npm install . npm rebuild -npm run build +NUXT_APP_BASE_URL="/" npm run generate rm -rf node_modules || true # Install backend From f985e5a4bfd3c1f91baba6d3ae58d2f7a4d0408e Mon Sep 17 00:00:00 2001 From: Arend-Jan van Hilten Date: Tue, 18 Aug 2026 09:00:49 +0200 Subject: [PATCH 8/8] Use single node install for back & front-end --- install_web.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/install_web.sh b/install_web.sh index 13b92e1..d8aa937 100755 --- a/install_web.sh +++ b/install_web.sh @@ -9,15 +9,17 @@ sudo apt update || true sudo apt install -y python3-pip python3-setuptools python3-wheel sudo -H pip install nodeenv -# Install node, lts 22 is tested by the front-end, will need to update after april 2027 https://github.com/nodejs/release#release-schedule -nodeenv --node=22. $MIRTE_SRC_DIR/mirte-web-interface/node_env +# TODO: around april 2027, update node versions. +# Newer nodeenv (1.11?) will have option for --node=22 instead of exact version +nodeenv --node=22.12.0 $MIRTE_SRC_DIR/mirte-web-interface/node_env . $MIRTE_SRC_DIR/mirte-web-interface/node_env/bin/activate + +# Install backend packages cd $MIRTE_SRC_DIR/mirte-web-interface/nodejs-backend || exit 1 npm install . +# Build front-end cd $MIRTE_SRC_DIR/mirte-web-interface/vue-frontend || exit 1 -nodeenv --node=22.12.0 $MIRTE_SRC_DIR/mirte-web-interface/vue-frontend/node_env -. $MIRTE_SRC_DIR/mirte-web-interface/vue-frontend/node_env/bin/activate npm install NUXT_APP_BASE_URL=/ npm run generate rm -rf node_modules || true