diff --git a/.github/img/dockge_01.jpg b/.github/img/dockge_01.jpg deleted file mode 100644 index 4a461be..0000000 Binary files a/.github/img/dockge_01.jpg and /dev/null differ diff --git a/README.md b/README.md index e4a1f90..fd6f49d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![OpenMower header](.github/img/open_mower_header.jpg)](https://github.com/ClemensElflein/OpenMower) -This repository contains the official OpenMowerOS (OMOSv2) image for running the [OpenMower](https://github.com/ClemensElflein/OpenMower) project on your OM's Pi4/CM4. +This repository contains the official OpenMowerOS (OMOSv2) image for running the [OpenMower](https://github.com/ClemensElflein/OpenMower) project on your OM's Pi/CM. ➡️ What’s new in the latest release? See [WHATSNEW.md](./WHATSNEW.md). @@ -25,14 +25,14 @@ This repository contains the official OpenMowerOS (OMOSv2) image for running the Tip: Click a section title to expand/collapse.
-Install OpenMowerOS on your Pi4/CM4 +Install OpenMowerOS on your Pi/CM -1. Flash the latest image (link TODO) to an SD card, preferably using [**Raspberry Pi** Imager](https://www.raspberrypi.com/software/). +1. Flash the latest OpenMowerOS v2.x [OpenMowerOS_YYYYMMDD.zip](https://github.com/ClemensElflein/OpenMowerOS/releases) to an SD card or your CM, preferably using [**Raspberry Pi** Imager](https://www.raspberrypi.com/software/). 2. ***Optional: Raspberry Pi Imager configuration***
When prompted by Raspberry Pi Imager, you can change some custom settings: - 1. As shown here, but never use a username other than `openmower`!
+ 1. As shown here, but the username must be `openmower`. ![General Settings](.github/img/rpimager_general.png) 2. You may also add your SSH public key for quicker SSH login; password login remains active (even if it's an either/or selection).
@@ -43,10 +43,10 @@ Tip: Click a section title to expand/collapse.
First boot and network setup -3. After writing the image, eject the card, insert it into your mowers Pi4 or xCore, and turn it on. +3. After writing the image, eject the card, insert it into your mower’s Pi or xCore, and turn it on. 4. Your Pi will boot multiple times.
- ***Sometimes, after the first boot, it may fail to reboot*** (red LED near the HDMI plug remains on, whereas the green one doesn't flicker anymore). If that happens, a power cycle will get it back on track. + ***Sometimes, after the first boot, it may fail to reboot*** (the Pi/xCore’s green LED doesn’t flicker anymore and remains off for >10 seconds). If that happens, a power cycle will get it back on track. 5. ***Optional: Comitup hotspot (if you skipped step 2 "Raspberry Pi Imager configuration")***
If you didn't enter your Wifi settings when asked for the custom settings during Pi Imager (see step 2), or if you accidentally entered the wrong Wifi settings: @@ -57,61 +57,68 @@ If you didn't enter your Wifi settings when asked for the custom settings during

- 3. Click on your home WiFi and fill in your password. + 3. Click on your home WiFi, fill in your password and click "SUBMIT". 4. The hotspot will disappear and the mower should connect to your WiFi. -6. Try pinging your mower via `ping openmower` (or the hostname you entered during Pi Imager). If the host can't be found, check your router for the mower's IP address. +6. Two minutes after the second reboot, try pinging your mower via `ping openmower` (or the hostname you entered during Pi Imager). If the host can't be found, check your router for the mower's IP address. -7. SSH into your mower via `ssh openmower@openmower` or `ssh openmower@` (password 'openmower' or the one you entered during Raspberry Pi Imager). - -8. ***Optional:***
- 1. If you didn't configure a custom password during step 2 (Raspberry Pi Imager configuration), change your password now via `passwd`. +7. ***Optional:***
+ 1. If you didn't configure a custom password during step 2 (Raspberry Pi Imager configuration), login via SSH and change your password now via `passwd`. 2. Use `raspi-config` to change keyboard, timezone, WLAN country and the like (if not configured in Raspberry Pi Imager's custom settings).
+ +
Manage OpenMower stack (GUI + CLI) -Dockge (a container manager GUI), as well as a ttyd (a WebTerminal) are automatically pulled and started after 2-5 minutes (after final boot). +[Dockge](https://dockge.kuma.pet/) (a container manager GUI) and [ttyd](https://tsl0922.github.io/ttyd/) (a web terminal) +are bundled with the OpenMowerOS image and are unpacked and installed during the final second‑boot step. +This may take about 2 minutes. + +Please wait while the Pi/xCore’s green LED is flickering or steadily on. The WebTerminal is available as a lightweight alternative to SSH for running the same commands. It can be reached via `http://openmower:7681` (adjust if you changed the hostname). -Default login credentials: openmower/openmower. For each relevant GUI action, a CLI alternative is available via a powerful `openmower` command; both are listed below. 1. Connect to the container manager: - - GUI: Open (or your individual hostname if changed), and login by entering the default Dockge admin user `openmower/openmower`: - ![Create Admin Account](.github/img/dockge_01.jpg) - CLI: - SSH into your Pi: `ssh openmower@openmower` (or your configured hostname). - WebTerminal via URL `http://openmower:7681` (or your configured hostname). + - GUI: Open (or your individual hostname if changed) 2. Configure the stack (.env) + - CLI: `openmower configure env` - GUI: ![Select and Edit Stack](.github/img/dockge_02_select_and_edit.jpg) ![Edit .env](.github/img/dockge_03_edit.jpg) ![Save .env](.github/img/dockge_04_save.jpg) - - CLI: `openmower configure env` -3. Start the stack (inclusive initial pull) - - GUI: - ![Start Stack](.github/img/dockge_05_start.jpg) - - CLI: +3. Start the stack (including the initial pull) + - CLI: If you configured your .env file via `openmower configure env` then the stack is pulled and started automatically.
+ If not: ```bash openmower pull openmower start ``` + - GUI: + ![Start Stack](.github/img/dockge_05_start.jpg) -4. Check status and open the OpenMower webApp +4. Check status and open the OpenMower web app + - CLI: `openmower status` should list three service names (open_mower_ros, Mosquitto and OpenMowerApp), all with status 'up'. If so, open a browser and visit `http://openmower:8080` (or your configured hostname). - GUI: ![Stack Active](.github/img/dockge_06_active.jpg) - - CLI: `openmower status` should list three service names (open_mower_ros, Mosquitto, OpenMowerApp), all with status 'up'. If so, open a browser and visit `http://openmower:8080` (or your configured hostname). -5. Configure the ROS parameters (CLI only for now): `openmower configure ros` +5. Configure the ROS parameters (CLI only for now): `openmower configure ros` and: + + 1. Adapt section `gps` to your needs, but set at least `datum_lat` and `datum_lon` to the corresponding lat/lon values near your docking station. Use right‑click in [Google Maps](https://maps.google.com) to get them. + 2. `ntrip_client` settings with the ones from your local RTK base or from your public NTRIP service. + 3. Once (and not before 🩸) you have validated your emergency sensors, set `enable_mower` to true.
diff --git a/WHATSNEW.md b/WHATSNEW.md index 4a38176..eeb4f5b 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -6,7 +6,7 @@ This document highlights the most relevant changes compared to previous OpenMowe ## 🆕 New - 🖥️ [Dockge](https://dockge.kuma.pet/) GUI for container management. -- 🖥️ WebTerminal [ttyd](https://github.com/tsl0922/ttyd) provides a browser-based shell as a lightweight SSH alternative. +- 🖥️ WebTerminal [ttyd](https://tsl0922.github.io/ttyd/) provides a browser-based shell as a lightweight SSH alternative. - 🧰 Unified `openmower` CLI for configure, pull, start, stop, status, shell, logs, … - 🗂️ Consolidated storage layout: configs, maps, logs now in `/home/openmower`. - 🧾 Version metadata at `/usr/share/openmoweros/version.{json,yaml,sh,txt}` (git hash, branch, describe, build timestamp). @@ -16,7 +16,7 @@ This document highlights the most relevant changes compared to previous OpenMowe - ⚙️ Key settings (e.g. WLAN SSID & password) configurable directly in Raspberry Pi Imager. - ✍️ No need to pre-edit files like `/boot/openmower/openmower_version.txt` before first boot. -- 🚀 Eliminated initial large image pull (only a short 2–5 minute Dockge pull remains). +- 🚀 Eliminated initial large image pull. - 👤 Containers run with `openmower` user permissions (no sudo needed for most operations). @@ -24,7 +24,7 @@ This document highlights the most relevant changes compared to previous OpenMowe - 🐧 Debian Trixie (arm64) images built with [pi‑gen](https://github.com/RPi-Distro/pi-gen). - 📁 `openmower` CLI command is in `/usr/local/bin`. -- 🐳 OpenMower stack: Mosquitto and OpenMowerApp (together with a small Nginx) now run as separate containers and are no longer built into the openmower image. +- 🐳 OpenMower stack: Mosquitto and OpenMowerApp (together with a small Nginx) now run as separate containers and are no longer built into the open_mower_ros image. - 📶 WLAN is managed by NetworkManager. - 🔌 LAN is managed by ifupdown. - 📡 DHCP for the internal (xCore) LAN is handled by dnsmasq. diff --git a/stage-openmower/30-docker/00-run-chroot.sh b/stage-openmower/30-docker/00-run-chroot.sh index cc8773a..f6d9c3e 100755 --- a/stage-openmower/30-docker/00-run-chroot.sh +++ b/stage-openmower/30-docker/00-run-chroot.sh @@ -36,3 +36,10 @@ cat > /etc/docker/daemon.json <<'EOF' } EOF fi + +# Bundled docker images +mkdir -p /opt/docker-images +chmod -R u=rwX,g=rX,o=rX /opt/docker-images + +# Ensure the preloader runs on boot (after docker) +systemctl enable docker-preload-images.service diff --git a/stage-openmower/30-docker/00-run.sh b/stage-openmower/30-docker/00-run.sh new file mode 100755 index 0000000..0d38363 --- /dev/null +++ b/stage-openmower/30-docker/00-run.sh @@ -0,0 +1,28 @@ +#!/bin/bash -e + +STAGE_DIR="$(dirname "$0")" + +install -m 0644 -D "$STAGE_DIR/files/etc/systemd/system/docker-preload-images.service" "$ROOTFS_DIR/etc/systemd/system/docker-preload-images.service" + +# Install bundled docker images +install -d -m 0755 "$ROOTFS_DIR/opt/docker-images" +cp -a "$STAGE_DIR/files/opt/docker-images/." "$ROOTFS_DIR/opt/docker-images/" + +# Reassemble split files into their base +DEST="$ROOTFS_DIR/opt/docker-images" +if compgen -G "$DEST"/*.tar.gz.part* > /dev/null; then + # Build unique list of basenames without the .partNN suffix + for base in $(for f in "$DEST"/*.tar.gz.part*; do echo "${f%.part*}"; done | sort -u); do + rm -f "${base}" + for p in $(ls -1 "${base}".part* 2>/dev/null | sort -V); do + cat "$p" >> "${base}" + done + rm -f "${base}".part* + done +fi + +# Uncompress all .tar.gz files to .tar. OS image gets ZIPped anyway but preload service only needs docker load them +for gz in "$DEST"/*.tar.gz; do + [ -e "$gz" ] || break + gunzip -f "$gz" +done diff --git a/stage-openmower/30-docker/files/etc/systemd/system/docker-preload-images.service b/stage-openmower/30-docker/files/etc/systemd/system/docker-preload-images.service new file mode 100644 index 0000000..f070ab9 --- /dev/null +++ b/stage-openmower/30-docker/files/etc/systemd/system/docker-preload-images.service @@ -0,0 +1,21 @@ +[Unit] +Description=Preload bundled Docker images (*.tar) into local Docker +After=docker.service +Requires=docker.service +Before=dockge.service webterminal.service +ConditionDirectoryNotEmpty=/opt/docker-images + +[Service] +Type=oneshot +TimeoutStartSec=4min +ExecStart=/bin/sh -e -c '\ +dir=/opt/docker-images; \ +# Load all .tar and delete on success \ +for tar in "$dir"/*.tar; do \ + [ -e "$tar" ] || break; \ + if docker load -i "$tar"; then rm -f "$tar"; fi; \ +done \ +' + +[Install] +WantedBy=multi-user.target diff --git a/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part00 b/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part00 new file mode 100644 index 0000000..7bf3472 Binary files /dev/null and b/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part00 differ diff --git a/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part01 b/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part01 new file mode 100644 index 0000000..ec614a1 Binary files /dev/null and b/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part01 differ diff --git a/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part02 b/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part02 new file mode 100644 index 0000000..cb35468 Binary files /dev/null and b/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part02 differ diff --git a/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part03 b/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part03 new file mode 100644 index 0000000..4f35da5 Binary files /dev/null and b/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part03 differ diff --git a/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part04 b/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part04 new file mode 100644 index 0000000..759e5e8 Binary files /dev/null and b/stage-openmower/30-docker/files/opt/docker-images/dockge.tar.gz.part04 differ diff --git a/stage-openmower/30-docker/files/opt/docker-images/ttyd.tar.gz b/stage-openmower/30-docker/files/opt/docker-images/ttyd.tar.gz new file mode 100644 index 0000000..bed2a91 Binary files /dev/null and b/stage-openmower/30-docker/files/opt/docker-images/ttyd.tar.gz differ diff --git a/stage-openmower/32-dockge/files/etc/systemd/system/dockge.service b/stage-openmower/32-dockge/files/etc/systemd/system/dockge.service index bcd3d1f..053e32f 100644 --- a/stage-openmower/32-dockge/files/etc/systemd/system/dockge.service +++ b/stage-openmower/32-dockge/files/etc/systemd/system/dockge.service @@ -1,7 +1,9 @@ [Unit] Description=Dockge -Wants=network-online.target docker.service -After=network-online.target docker.service +Wants=network-online.target docker.service docker-preload-images.service time-sync.target +After=network-online.target docker.service docker-preload-images.service time-sync.target +StartLimitIntervalSec=10min +StartLimitBurst=10 [Service] Type=oneshot @@ -9,9 +11,10 @@ WorkingDirectory=/opt/dockge RemainAfterExit=yes TimeoutStartSec=10min ExecStartPre=/bin/sh -c 'H="$(hostname -f 2>/dev/null || hostname || cat /etc/hostname)"; EF="/opt/stacks/openmower/.env"; if [ -f "$EF" ]; then if grep -qE "^[[:space:]]*HOSTNAME[[:space:]]*=" "$EF"; then sed -i -E "s|^[[:space:]]*HOSTNAME[[:space:]]*=.*|HOSTNAME=$H|g" "$EF"; else echo "HOSTNAME=$H" >> "$EF"; fi; fi' -ExecStartPre=/usr/bin/docker compose pull --quiet ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down +Restart=on-failure +RestartSec=30s [Install] WantedBy=multi-user.target diff --git a/stage-openmower/35-webterminal/files/etc/systemd/system/webterminal.service b/stage-openmower/35-webterminal/files/etc/systemd/system/webterminal.service index 91b6f91..f1ac187 100644 --- a/stage-openmower/35-webterminal/files/etc/systemd/system/webterminal.service +++ b/stage-openmower/35-webterminal/files/etc/systemd/system/webterminal.service @@ -1,15 +1,14 @@ [Unit] Description=WebTerminal (ttyd) for OpenMowerOS Documentation=https://github.com/ClemensElflein/OpenMowerOS -Wants=network-online.target docker.service -After=network-online.target docker.service +Wants=network-online.target docker.service docker-preload-images.service +After=network-online.target docker.service docker-preload-images.service [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=/opt/stacks/webterminal ExecStartPre=/bin/sh -c 'H="$(hostname -f 2>/dev/null || hostname || cat /etc/hostname)"; EF="/opt/stacks/webterminal/.env"; if [ -f "$EF" ]; then if grep -qE "^[[:space:]]*HOSTNAME[[:space:]]*=" "$EF"; then sed -i -E "s|^[[:space:]]*HOSTNAME[[:space:]]*=.*|HOSTNAME=$H|g" "$EF"; else echo "HOSTNAME=$H" >> "$EF"; fi; fi' -ExecStartPre=/usr/bin/docker compose pull --quiet ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down TimeoutStartSec=5min diff --git a/stage-openmower/40-openmower/00-run-chroot.sh b/stage-openmower/40-openmower/00-run-chroot.sh index 91b8b17..920bcc9 100755 --- a/stage-openmower/40-openmower/00-run-chroot.sh +++ b/stage-openmower/40-openmower/00-run-chroot.sh @@ -67,14 +67,3 @@ echo "✓ Installed. Version check (if supported):" echo "→ Installing tab completion for /usr/local/bin/openmower" sudo -u openmower /usr/local/bin/openmower --install-completion echo "✓ Installed" - -echo "→ Updating bash prompts to show 🐳 when STACK_NAME is set" -for f in /home/openmower/.bashrc /root/.bashrc /etc/skel/.bashrc; do - cat >> "$f" <<'OMEOF' - -# Prefix PS1 with docker whale if in stack context -if [ -n "$STACK_NAME" ]; then - PS1="🐳 $PS1" -fi -OMEOF -done diff --git a/stage-openmower/40-openmower/files/opt/stacks/openmower/.env b/stage-openmower/40-openmower/files/opt/stacks/openmower/.env index 45534c7..94a95ee 100644 --- a/stage-openmower/40-openmower/files/opt/stacks/openmower/.env +++ b/stage-openmower/40-openmower/files/opt/stacks/openmower/.env @@ -74,7 +74,7 @@ MOWER="CHANGE_ME" # 0_9_X_MPU9250: MPU9250 with 0.9.x mainboard FIRMWARE="CHANGE_ME" -# Select your ESC type +# ESC type (only relevant for V1 Hardware): # Supported values as of today: # xesc_mini: for the STM32 version (VESC) # xesc_mini_w_r4ma: for the STM32 version (VESC), @@ -102,7 +102,3 @@ DEBUG=True # Get changed by dockge.service # on each start HOSTNAME="openmower.local" - -# -# Currently only used for docker shell prompt -STACK_NAME="openmower" diff --git a/stage-openmower/40-openmower/files/opt/stacks/openmower/compose.yaml b/stage-openmower/40-openmower/files/opt/stacks/openmower/compose.yaml index 6aff5f1..02a78e1 100644 --- a/stage-openmower/40-openmower/files/opt/stacks/openmower/compose.yaml +++ b/stage-openmower/40-openmower/files/opt/stacks/openmower/compose.yaml @@ -33,6 +33,7 @@ services: image: eclipse-mosquitto:2 container_name: Mosquitto restart: unless-stopped + user: mosquitto:mosquitto ports: - "1883:1883" - "9001:9001"