diff --git a/deploy/README.md b/deploy/README.md index e164b6a..04fe207 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -21,6 +21,24 @@ being offline (events buffer in the local sqlite queue and flush on reconnect). | `systemd/move-detect-edge.service` | the Pi | the daemon unit (auto-restart, device access, hardening) | | `systemd/move-detect-edge.env.example` | the Pi | env template → `/etc/move-detect/edge.env` | +## Quick start — Pi with internet (one command) + +If the Pi itself has internet, skip the offline bundle entirely. Copy this repo onto +the Pi (git clone or scp), then: + +```bash +sudo deploy/setup_online.sh +``` + +It installs the system packages (`python3-picamera2`, and `hailo-all` if present), +builds the venv, pip-installs the runtime deps, fetches the MoveNet model, writes +`/etc/move-detect/edge.env` (prompting for `DEVICE_ID` / `DEVICE_SECRET` / `CAFE24_URL` / +`CAMERAS`, or take them from env vars), installs the systemd unit, and starts the service. +It begins on CPU MoveNet; drop a `yolov8s_pose.hef` in and pass `HAILO_HEF=...` (or edit +`edge.env`) to switch to Hailo. The rest of this doc is the **air-gapped** path. + +--- + ## 1. Build the bundle (on a machine WITH internet) **Most reliable: build on an aarch64 host** (a Pi 5, or an arm64 CI runner) so the diff --git a/deploy/setup_online.sh b/deploy/setup_online.sh new file mode 100644 index 0000000..be95fa4 --- /dev/null +++ b/deploy/setup_online.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +# ONE-COMMAND online installer for an INTERNET-CONNECTED Raspberry Pi 5. +# Installs everything and starts the service. For AIR-GAPPED Pis use deploy/bundle/*. +# +# Run from a clone/copy of this repo, as root: +# sudo deploy/setup_online.sh +# +# Non-interactive: pass values as env vars, e.g. +# sudo DEVICE_ID=pi-01 DEVICE_SECRET=... CAFE24_URL=https://cloud CAMERAS=csi:0:cam-01,uvc:0:cam-02 \ +# HAILO_HEF=/home/pi/yolov8s_pose.hef deploy/setup_online.sh +# Interactive (a real terminal): it prompts for anything not passed. +set -euo pipefail + +if [ "$(id -u)" -ne 0 ]; then + echo "run as root: sudo $0" >&2 + exit 1 +fi + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PREFIX=/opt/move-detect +STATE=/var/lib/move-detect +ETC=/etc/move-detect +SVC_USER=movedetect + +echo ">> [1/8] system packages (CSI camera, venv; Hailo runtime best-effort)" +export DEBIAN_FRONTEND=noninteractive +apt-get update +apt-get install -y python3-picamera2 python3-venv python3-pip +# hailo-all is the Hailo driver+runtime; only present in Pi repos with a Hailo HAT. +# Best-effort so a Pi without it still installs and runs on CPU MoveNet. +apt-get install -y hailo-all || echo " WARN: hailo-all not installed — Hailo accel unavailable, CPU MoveNet will be used." + +echo ">> [2/8] service account + device groups" +id -u "$SVC_USER" >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin "$SVC_USER" +for g in video render plugdev hailo; do + getent group "$g" >/dev/null 2>&1 && usermod -aG "$g" "$SVC_USER" || true +done + +echo ">> [3/8] source + dirs under $PREFIX" +install -d -m 0755 "$PREFIX" "$PREFIX/src" "$PREFIX/models" "$PREFIX/docs" +rm -rf "$PREFIX/src/edge" # full replace on re-run (no nested edge/edge) +cp -r "$REPO_ROOT/edge" "$PREFIX/src/edge" +cp "$REPO_ROOT/pyproject.toml" "$PREFIX/src/" 2>/dev/null || true +find "$PREFIX/src" -name __pycache__ -type d -prune -exec rm -rf {} + +cp "$REPO_ROOT/docs/pilot-runbook.md" "$PREFIX/docs/" 2>/dev/null || true + +echo ">> [4/8] venv (+ system site-packages) + pip runtime deps" +python3 -m venv --system-site-packages "$PREFIX/venv" +"$PREFIX/venv/bin/pip" install --upgrade pip >/dev/null +# numpy is intentionally NOT pinned here: it comes in transitively via onnxruntime, and +# leaving the system numpy in place (the venv sees it) avoids shadowing the numpy that +# the apt python3-picamera2 stack was compiled against. +"$PREFIX/venv/bin/pip" install requests onnxruntime opencv-python-headless + +echo ">> [5/8] MoveNet CPU model" +( cd "$REPO_ROOT" && "$PREFIX/venv/bin/python" -m edge.tools.fetch_movenet ) +cp "$REPO_ROOT/models/movenet_lightning.onnx" "$PREFIX/models/" 2>/dev/null || true + +echo ">> [6/8] pose backend" +POSE_BACKEND=movenet +HEF_DEST="$PREFIX/models/yolov8s_pose.hef" +if [ -n "${HAILO_HEF:-}" ] && [ -f "${HAILO_HEF:-}" ]; then + cp "$HAILO_HEF" "$HEF_DEST" + POSE_BACKEND=hailo + echo " -> Hailo HEF installed; POSE_BACKEND=hailo" +else + echo " -> no HAILO_HEF given; starting on CPU MoveNet." + echo " For Hailo: copy yolov8s_pose.hef to $HEF_DEST, set POSE_BACKEND=hailo in" + echo " $ETC/edge.env, then: sudo systemctl restart move-detect-edge" +fi + +echo ">> [7/8] config -> $ETC/edge.env" +install -d -m 0750 "$STATE"; chown "$SVC_USER":"$SVC_USER" "$STATE" +install -d -m 0750 "$ETC" +if [ -f "$ETC/edge.env" ]; then + echo " -> $ETC/edge.env already exists; left unchanged" +else + : "${DEVICE_ID:=}"; : "${DEVICE_SECRET:=}"; : "${CAFE24_URL:=}"; : "${CAMERAS:=}" + if [ -t 0 ]; then + [ -n "$DEVICE_ID" ] || read -rp "DEVICE_ID [pi-01]: " DEVICE_ID + [ -n "$DEVICE_SECRET" ] || { read -rsp "DEVICE_SECRET (from cloud): " DEVICE_SECRET; echo; } + [ -n "$CAFE24_URL" ] || read -rp "CAFE24_URL (cloud base URL): " CAFE24_URL + [ -n "$CAMERAS" ] || read -rp "CAMERAS [csi:0:cam-01,uvc:0:cam-02]: " CAMERAS + fi + DEVICE_ID="${DEVICE_ID:-pi-01}" + CAMERAS="${CAMERAS:-csi:0:cam-01,uvc:0:cam-02}" + # Every line is a bare KEY=value (systemd EnvironmentFile= keeps inline text literally). + umask 077 + cat > "$ETC/edge.env" < wrote $ETC/edge.env" +fi + +echo ">> [8/8] systemd service" +install -m 0644 "$REPO_ROOT/deploy/systemd/move-detect-edge.service" /etc/systemd/system/ +systemctl daemon-reload +systemctl enable --now move-detect-edge + +echo "" +echo "Done. The edge daemon is installed and started." +echo " logs: journalctl -u move-detect-edge -f" +echo " config: sudo nano $ETC/edge.env (then: sudo systemctl restart move-detect-edge)"