diff --git a/0-setup/PRESETUP.md b/0-setup/PRESETUP.md new file mode 100644 index 0000000..d1d2ac4 --- /dev/null +++ b/0-setup/PRESETUP.md @@ -0,0 +1,359 @@ + + +# Arduino UNO Q -- Workshop Master Setup Guide + +This document consolidates all validated working steps used during +provisioning and workshop preparation. + +------------------------------------------------------------------------ + +# 🔁 Reflash Debian Image + +Official guide: + +https://docs.arduino.cc/tutorials/uno-q/update-image/ + +Reflash the board before starting the setup to ensure a clean state. + +```sh +cd ~/Downloads/arduino-flasher-cli-0.5.0-linux-amd64/ +./arduino-flasher-cli flash arduino-unoq-debian-image-20251229-457.tar.zst +``` + +------------------------------------------------------------------------ + +# 🧰 Initial Setup via ADB + +## 1️⃣ First Boot -- Mandatory Password Setup + +After flashing, the board requires setting a password. + +Set it to `arduino` (workshop default): + +``` bash +adb shell "printf '%s\n%s\n' 'arduino' 'arduino' | passwd" +``` + +------------------------------------------------------------------------ + +## 2️⃣ Connect to Wi-Fi + +``` bash +adb shell "nmcli dev wifi connect 'SSID' password 'PASSWORD'" +``` + +Verify: + +``` bash +adb shell "ip addr show wlan0" +``` + +------------------------------------------------------------------------ + +# 🔐 Enable SSH (CRITICAL STEP) + +After fresh flash, SSH service fails because host keys are missing. + +Generate host keys: + +``` bash +adb shell "echo 'arduino' | sudo -S ssh-keygen -A" +``` + +Enable and start SSH: + +``` bash +adb shell "echo 'arduino' | sudo -S systemctl enable ssh" +adb shell "echo 'arduino' | sudo -S systemctl start ssh" +``` + +Verify: + +``` bash +adb shell "echo 'arduino' | sudo -S systemctl status ssh --no-pager" +adb shell "ss -ltnp | grep ':22' || true" +``` + +Expected: + + Server listening on 0.0.0.0 port 22. + +------------------------------------------------------------------------ + +## 3️⃣ Test SSH + +From your computer: + +``` bash +ssh arduino@ +``` + +Password: + + arduino + +------------------------------------------------------------------------ + +# 🖥 Prevent Screen Sleep + +Adb Command: + +```sh +adb shell "echo 'arduino' | sudo -S sh -c 'mkdir -p /etc/X11/xorg.conf.d && cat > /etc/X11/xorg.conf.d/10-monitor.conf <<\"EOF\" +Section \"Monitor\" + Identifier \"Monitor0\" + Option \"DPMS\" \"false\" +EndSection + +Section \"ServerFlags\" + Option \"StandbyTime\" \"0\" + Option \"SuspendTime\" \"0\" + Option \"OffTime\" \"0\" + Option \"BlankTime\" \"0\" +EndSection +EOF'" +``` + +------------------------------------------------------------------------ + +# Fioup + + +```sh +sudo apt update +sudo apt install -y apt-transport-https ca-certificates curl gnupg + + +curl -L https://fioup.foundries.io/pkg/deb/dists/stable/Release.gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/fioup-stable.gpg + +echo 'deb [signed-by=/etc/apt/trusted.gpg.d/fioup-stable.gpg] https://fioup.foundries.io/pkg/deb stable main' | sudo tee /etc/apt/sources.list.d/fioup.list + + +sudo apt update +sudo apt install -y fioup + +sudo mkdir -p /var/sota +sudo chown -R $USER /var/sota + +sudo usermod -aG docker $USER + +sudo fioup register --api-token --factory --name --apps pingpong-webui + +sudo -S sed -i '/^\[uptane\]/a polling_seconds = \"10\"' /var/sota/sota.toml + +sudo grep -A3 '^\[uptane\]' /var/sota/sota.toml + +sudo -S systemctl enable fioup +sudo -S systemctl start fioup + +``` + + +## Desktop icon (Chromium fullscreen) + +Creates a desktop shortcut that launches Chromium in kiosk mode. + +```sh +adb shell "mkdir -p /home/arduino/Desktop" + +adb shell "cat << 'EOF' > /home/arduino/Desktop/App.desktop +[Desktop Entry] +Version=1.0 +Type=Application +Name=App +Comment= +Exec=chromium --kiosk http://localhost:8000 +Icon=audio-input-microphone +Path= +Terminal=false +StartupNotify=false +EOF" + +adb shell "chmod 644 /home/arduino/Desktop/App.desktop" +adb shell "chown arduino:arduino /home/arduino/Desktop/App.desktop" +``` + +## Auto Start + +Starts Chromium automatically in fullscreen kiosk mode after login. + +```sh +adb shell "echo 'arduino' | sudo -S sh -c 'cat > /etc/xdg/autostart/kiosk.desktop <<\"EOF\" +[Desktop Entry] +Type=Application +Name=Kiosk +Exec=chromium --kiosk http://localhost:8000 +X-GNOME-Autostart-enabled=true +EOF'" +``` + +## DISABLE APPLABS + +```sh +adb shell "echo 'arduino' | sudo -S rm /etc/xdg/autostart/ArduinoAppLab.desktop" +``` + +## Auto Login + +Enables automatic login for user `arduino` at boot. + +```sh +adb shell "echo 'arduino' | sudo -S mkdir -p /etc/lightdm/lightdm.conf.d" + +adb shell "echo 'arduino' | sudo -S sh -c 'cat > /etc/lightdm/lightdm.conf.d/50-autologin.conf <<\"EOF\" +[Seat:*] +autologin-user=arduino +autologin-user-timeout=0 +EOF'" + +adb shell "echo 'arduino' | sudo -S reboot" +``` + +------------------------------------------------------------------------ + +# 🐳 Docker Registry Mirror -- Workshop Setup (Optional) + +## 1️⃣ Server Setup (Laptop) + +``` bash +mkdir registry-mirror +cd registry-mirror +``` + +### config.yml + +``` yaml +version: 0.1 + +log: + level: info + +http: + addr: 0.0.0.0:5000 + +storage: + filesystem: + rootdirectory: /var/lib/registry + +proxy: + remoteurl: https://registry-1.docker.io +``` + +### docker-compose.yml + +``` yaml +services: + registry-mirror: + image: registry:2 + container_name: registry-mirror + restart: always + ports: + - "5000:5000" + volumes: + - ./config.yml:/etc/docker/registry/config.yml:ro + - registry-data:/var/lib/registry + +volumes: + registry-data: +``` + +Start: + +``` bash +docker compose up -d +``` + +Verify: + +``` bash +curl http://localhost:5000/v2/ +``` + +Expected: + + {} + +------------------------------------------------------------------------ + +## 2️⃣ Configure Clients + +Use your own mirror hostname and server IP in place of `pc-server` and `192.168.20.10`. + +Add to `/etc/hosts`: +``` sh +echo "192.168.20.10 pc-server" | sudo tee -a /etc/hosts >/dev/null +``` + +Edit `/etc/docker/daemon.json`: + +``` sh +sudo tee /etc/docker/daemon.json >/dev/null <<'EOF' +{ + "log-driver": "json-file", + "log-opts": { + "max-size": "10m", + "max-file": "2" + }, + "registry-mirrors": ["http://pc-server:5000"], + "insecure-registries": ["pc-server:5000"] +} +EOF +``` + +Restart Docker: + +``` bash +sudo systemctl restart docker +``` + +------------------------------------------------------------------------ + +## 3️⃣ Validate Mirror + +On one board: + +``` bash +docker pull debian:trixie-slim +``` + +On server: + +``` bash +docker logs -f registry-mirror +``` + +------------------------------------------------------------------------ + +## 4️⃣ Pre-Warm Cache (Before Workshop) + +``` bash +docker pull debian:trixie-slim +docker pull registry:2 +docker pull python:3 +``` +------------------------------------------------------------------------ + +# 🌐 Static IP Configuration (Optional) + +Set static IP example: + +``` bash +adb shell "echo 'arduino' | sudo -S nmcli connection modify 'FoundriesWorkshop' ipv4.method manual ipv4.addresses 192.168.20.20/24 ipv4.gateway 192.168.20.1 ipv4.dns 8.8.8.8" +``` + +Restart connection: + +``` bash +adb shell "echo 'arduino' | sudo -S nmcli connection down 'FoundriesWorkshop'" +adb shell "echo 'arduino' | sudo -S nmcli connection up 'FoundriesWorkshop'" +``` + +------------------------------------------------------------------------ + +## Revert Back to DHCP + +``` bash +adb shell "echo 'arduino' | sudo -S nmcli connection modify 'FoundriesWorkshop' ipv4.method auto" +adb shell "echo 'arduino' | sudo -S nmcli connection down 'FoundriesWorkshop'" +adb shell "echo 'arduino' | sudo -S nmcli connection up 'FoundriesWorkshop'" +``` \ No newline at end of file diff --git a/0-setup/uno_q_provision.sh b/0-setup/uno_q_provision.sh deleted file mode 100644 index f2eef8b..0000000 --- a/0-setup/uno_q_provision.sh +++ /dev/null @@ -1,214 +0,0 @@ -\ -#!/usr/bin/env bash -set -euo pipefail - -# UNO Q Workshop Provisioning via ADB -# - Sets user password (first boot) -# - Connects to Wi-Fi -# - Enables SSH (generates host keys) -# - Optional: set static IP or DHCP -# - Optional: configure /etc/hosts + Docker registry mirror + restart docker -# -# Usage examples: -# ./uno_q_provision.sh --ssid FoundriesWorkshop --wifi-pass '@FoundriesWorkshop123' -# ./uno_q_provision.sh --serial 1710162692 --ssid FoundriesWorkshop --wifi-pass '@FoundriesWorkshop123' --password arduino -# ./uno_q_provision.sh --ssid FoundriesWorkshop --wifi-pass '@FoundriesWorkshop123' --static-ip 192.168.20.20 --gateway 192.168.20.1 --dns 8.8.8.8 -# ./uno_q_provision.sh --ssid FoundriesWorkshop --wifi-pass '@FoundriesWorkshop123' --configure-mirror --server-ip 192.168.20.10 --mirror-host rauls-server - -############################ -# Defaults (override via args) -############################ -SERIAL="" # If empty, uses the first "device" from adb devices -PASSWORD="arduino" # Password to set for user (and used for sudo -S) -SSID="FoundriesWorkshop" -WIFI_PASS="@FoundriesWorkshop123" - -# Network (optional) -USE_DHCP=1 # default: DHCP -STATIC_IP="" # e.g., 192.168.20.20 -GATEWAY="192.168.20.1" -DNS="8.8.8.8" - -# Docker mirror (optional) -CONFIGURE_MIRROR=0 -MIRROR_HOST="rauls-server" -SERVER_IP="192.168.20.10" # server IP for /etc/hosts entry -MIRROR_PORT="5000" - -# Safety toggles -DOCKER_RESTART=1 # restart docker after daemon.json change - -######################################## -# Helpers -######################################## -die() { echo "ERROR: $*" >&2; exit 1; } - -usage() { - cat < ADB device serial (from: adb devices) - --password Password to set for 'arduino' user and used for sudo -S (default: $PASSWORD) - - --ssid Wi-Fi SSID (default: $SSID) - --wifi-pass Wi-Fi password (default: hidden) - - --static-ip Configure static IPv4 (enables static mode) - --gateway Gateway for static IP (default: $GATEWAY) - --dns DNS for static IP (default: $DNS) - --dhcp Force DHCP (default) - - --configure-mirror Configure Docker registry mirror + /etc/hosts - --server-ip Server IP for /etc/hosts (default: $SERVER_IP) - --mirror-host Hostname to map in /etc/hosts (default: $MIRROR_HOST) - --mirror-port Mirror port (default: $MIRROR_PORT) - --no-docker-restart Do not restart docker (if you want to do it manually) - -Examples: - ./uno_q_provision.sh --ssid FoundriesWorkshop --wifi-pass '@FoundriesWorkshop123' - ./uno_q_provision.sh --serial 1710162692 --ssid FoundriesWorkshop --wifi-pass '@FoundriesWorkshop123' --password arduino - ./uno_q_provision.sh --ssid FoundriesWorkshop --wifi-pass '@FoundriesWorkshop123' --static-ip 192.168.20.20 --gateway 192.168.20.1 --dns 8.8.8.8 - ./uno_q_provision.sh --ssid FoundriesWorkshop --wifi-pass '@FoundriesWorkshop123' --configure-mirror --server-ip 192.168.20.10 --mirror-host rauls-server - -EOF -} - -pick_serial_if_needed() { - if [[ -n "$SERIAL" ]]; then return 0; fi - local first - first="$(adb devices | awk 'NR>1 && $2=="device" {print $1; exit}')" - [[ -n "$first" ]] || die "No ADB devices found. Check USB and run: adb devices" - SERIAL="$first" -} - -adbsh() { - adb -s "$SERIAL" shell "$@" -} - -sudo_sh() { - local cmd="$1" - adbsh "echo '$PASSWORD' | sudo -S sh -c \"$cmd\"" -} - -######################################## -# Arg parsing -######################################## -while [[ $# -gt 0 ]]; do - case "$1" in - --serial) SERIAL="${2:-}"; shift 2 ;; - --password) PASSWORD="${2:-}"; shift 2 ;; - --ssid) SSID="${2:-}"; shift 2 ;; - --wifi-pass) WIFI_PASS="${2:-}"; shift 2 ;; - - --static-ip) STATIC_IP="${2:-}"; USE_DHCP=0; shift 2 ;; - --gateway) GATEWAY="${2:-}"; shift 2 ;; - --dns) DNS="${2:-}"; shift 2 ;; - --dhcp) USE_DHCP=1; STATIC_IP=""; shift 1 ;; - - --configure-mirror) CONFIGURE_MIRROR=1; shift 1 ;; - --server-ip) SERVER_IP="${2:-}"; shift 2 ;; - --mirror-host) MIRROR_HOST="${2:-}"; shift 2 ;; - --mirror-port) MIRROR_PORT="${2:-}"; shift 2 ;; - --no-docker-restart) DOCKER_RESTART=0; shift 1 ;; - - -h|--help) usage; exit 0 ;; - *) die "Unknown option: $1 (use --help)" ;; - esac -done - -pick_serial_if_needed - -echo "==> Using ADB serial: $SERIAL" -echo "==> SSID: $SSID" -echo "==> DHCP: $USE_DHCP" -[[ -n "$STATIC_IP" ]] && echo "==> Static IP: $STATIC_IP Gateway: $GATEWAY DNS: $DNS" -[[ "$CONFIGURE_MIRROR" -eq 1 ]] && echo "==> Docker mirror: http://${MIRROR_HOST}:${MIRROR_PORT} (hosts: ${SERVER_IP} -> ${MIRROR_HOST})" - -######################################## -# 1) Set password (first boot) -######################################## -echo "==> Setting password for 'arduino' user (value hidden)" -adbsh "printf '%s\n%s\n' '$PASSWORD' '$PASSWORD' | passwd" >/dev/null || die "passwd failed" - -######################################## -# 2) Connect Wi-Fi -######################################## -echo "==> Connecting to Wi-Fi..." -adbsh "nmcli dev wifi connect '$SSID' password '$WIFI_PASS'" || die "nmcli wifi connect failed" - -echo "==> IP info (wlan0):" -adbsh "ip -4 addr show wlan0 | grep inet || true" - -######################################## -# 3) Enable SSH (host keys + service) -######################################## -echo "==> Ensuring SSH host keys exist..." -sudo_sh "ssh-keygen -A" >/dev/null || die "ssh-keygen -A failed" - -echo "==> Enabling + starting SSH..." -sudo_sh "systemctl enable ssh >/dev/null 2>&1 || true" -sudo_sh "systemctl restart ssh" || die "systemctl restart ssh failed" - -echo "==> SSH status:" -sudo_sh "systemctl status ssh --no-pager | sed -n '1,12p'" || true -adbsh "ss -ltnp | grep ':22' || true" - -######################################## -# 4) Optional: network static/DHCP -######################################## -CONN="$SSID" - -if [[ "$USE_DHCP" -eq 1 ]]; then - echo "==> Setting IPv4 method: DHCP (auto) on connection '$CONN'" - sudo_sh "nmcli connection modify '$CONN' ipv4.method auto" || true -else - [[ -n "$STATIC_IP" ]] || die "Static mode selected but --static-ip is empty" - echo "==> Setting IPv4 method: manual on connection '$CONN'" - sudo_sh "nmcli connection modify '$CONN' ipv4.method manual ipv4.addresses '${STATIC_IP}/24' ipv4.gateway '$GATEWAY' ipv4.dns '$DNS'" || die "nmcli static IP config failed" -fi - -echo "==> Cycling Wi-Fi connection..." -sudo_sh "nmcli connection down '$CONN' || true" -sudo_sh "nmcli connection up '$CONN'" || true - -echo "==> Current IP (wlan0):" -adbsh "ip -4 addr show wlan0 | grep inet || true" - -######################################## -# 5) Optional: Docker mirror config -######################################## -if [[ "$CONFIGURE_MIRROR" -eq 1 ]]; then - echo "==> Adding /etc/hosts entry for mirror host (if missing)..." - sudo_sh "grep -q '$MIRROR_HOST' /etc/hosts || echo '$SERVER_IP $MIRROR_HOST' >> /etc/hosts" - - echo "==> Writing /etc/docker/daemon.json (logs + mirror + insecure registries)..." - sudo_sh "cat > /etc/docker/daemon.json <<'EOF' -{ - \"log-driver\": \"json-file\", - \"log-opts\": { - \"max-size\": \"10m\", - \"max-file\": \"2\" - }, - \"registry-mirrors\": [\"http://${MIRROR_HOST}:${MIRROR_PORT}\"], - \"insecure-registries\": [\"${MIRROR_HOST}:${MIRROR_PORT}\"] -} -EOF" - - echo "==> Validating docker config..." - sudo_sh "dockerd --validate --config-file /etc/docker/daemon.json" || die "dockerd config validation failed" - - if [[ "$DOCKER_RESTART" -eq 1 ]]; then - echo "==> Restarting docker..." - sudo_sh "systemctl restart docker" || die "docker restart failed" - echo "==> Docker mirror check:" - sudo_sh "docker info 2>/dev/null | grep -i mirror -A2 || true" || true - else - echo "==> Skipping docker restart (--no-docker-restart)." - fi -fi - -echo "==> DONE." -echo "Next:" -echo " - SSH: ssh arduino@ (password: $PASSWORD)" -echo " - If using mirror: try docker pull debian:trixie-slim on the board and watch mirror logs on the server." diff --git a/7-edgeimpulse/README.md b/7-edgeimpulse/README.md index 1363885..6062cad 100644 --- a/7-edgeimpulse/README.md +++ b/7-edgeimpulse/README.md @@ -19,7 +19,8 @@ moving from prototype to production. In this lab you will: -- Create an Edge Impulse project +- Create a free [Edge Impulse account](https://edgeimp.com/foundries-ew26) +- [Clone this project](https://studio.edgeimpulse.com/public/913211/live?utm_medium=live_event&utm_source=tradeshow&utm_campaign=36403052-embedded_world_2026&utm_content=foundries_workshop_public_project) - Explore an existing dataset - Generate features - Train a model @@ -30,11 +31,17 @@ In this lab you will: ## Steps +### 0) Create an Edge Impulse account + +Create a free [Edge Impulse account](https://edgeimp.com/foundries-ew26) to start training your ML model. + +--- + ### 1) Clone the reference project Open the public Edge Impulse project: -Embedded World 2026 – Foundries Keyword Dataset +[Embedded World 2026 – Foundries Keyword Dataset](https://studio.edgeimpulse.com/public/913211/live?utm_medium=live_event&utm_source=tradeshow&utm_campaign=36403052-embedded_world_2026&utm_content=foundries_workshop_public_project) Clone the project into your account. @@ -80,7 +87,7 @@ Open the MFE processing block. Click: -Generate Features +`Generate Features` ![Generate Features](assets/edgeimpulse-4.png) @@ -153,4 +160,4 @@ You should now have: ## Transition to next lab Next we will integrate the trained model into a containerized application -and use voice recognition to control hardware on the device. \ No newline at end of file +and use voice recognition to control hardware on the device.