diff --git a/helpers/cfgsetup.py b/helpers/cfgsetup.py index 8b40f4c..34806b0 100644 --- a/helpers/cfgsetup.py +++ b/helpers/cfgsetup.py @@ -1,18 +1,35 @@ # -*- coding: utf-8 -*- """Automatically generate a unique SSID for the MyNaturewatch WiFi hotspot, -based on the Pi's serial number.""" +based on the Pi's serial number, and bring the hotspot up. + +Works with both the onboard WiFi and USB WiFi dongles: it waits for a wireless +interface to appear (USB adapters enumerate a few seconds later than onboard +WiFi) and auto-detects its name instead of assuming it is called 'wlan0'. +""" +import glob import os import subprocess import time -# Get SSID and passphrase from hostapd.conf -#HOST_CONFIG_FILE_PATH = "/etc/hostapd/hostapd.conf" -#with open(HOST_CONFIG_FILE_PATH, "r", encoding="utf-8") as config_file: -# host_config_file = config_file.readlines() +def find_wifi_interface(timeout=90): + """Return the name of the first wireless interface, waiting up to `timeout` + seconds for one to appear (a USB dongle can take a few seconds to come up).""" + for _ in range(timeout): + for marker in ("/sys/class/net/*/wireless", "/sys/class/net/*/phy80211"): + found = glob.glob(marker) + if found: + return found[0].split("/")[-2] + time.sleep(1) + return None + -#current_ssid = host_config_file[2][5:].strip() -#print(f"hostapd configuration - SSID: {current_ssid}") +# Wait for and detect the wireless interface (onboard WiFi or a USB dongle) +iface = find_wifi_interface() +if iface is None: + print("No wireless interface found; cannot start the hotspot.") + raise SystemExit(1) +print("Using wireless interface: " + iface) # Generate a unique SSID based on the Pi's serial number unique_id = subprocess.check_output( @@ -20,17 +37,20 @@ ) unique_ssid = f"MyNaturewatch-{unique_id.strip().decode('utf-8')[-8:]}" -#if unique_ssid == current_ssid: -# print("Unique SSID already set, no further action is needed.") -#else: print("Updating hotspot") os.system("sudo iw reg set GB") -os.system("sudo nmcli r wifi on") +os.system("sudo nmcli r wifi on") os.system("sudo nmcli con delete id Hotspot") os.system("sudo nmcli con add type wifi con-name hotspot") -os.system("sudo nmcli device wifi hotspot ssid " + unique_ssid + " password badgersandfoxes ifname wlan0") +os.system("sudo nmcli device wifi hotspot ssid " + unique_ssid + " password badgersandfoxes ifname " + iface) os.system("sudo nmcli connection modify Hotspot connection.autoconnect yes") +# Force plain WPA2-PSK (RSN/CCMP) and disable management-frame protection. +# NetworkManager 1.42 otherwise runs the hotspot in WPA2/WPA3 "transition mode" +# (key_mgmt "WPA-PSK WPA-PSK-SHA256 SAE"), which many USB WiFi drivers can't do +# properly in AP mode -- the network then won't show on some phones and fails the +# handshake on others. Plain WPA2 works on every modern client. +os.system("sudo nmcli connection modify Hotspot wifi-sec.key-mgmt wpa-psk wifi-sec.proto rsn wifi-sec.pairwise ccmp wifi-sec.group ccmp wifi-sec.pmf 1") os.system("sudo systemctl restart NetworkManager") -time.sleep(5) +time.sleep(5) os.system("sudo nmcli con up Hotspot") diff --git a/install.sh b/install.sh index a5dbbc6..a15a9cc 100644 --- a/install.sh +++ b/install.sh @@ -41,21 +41,50 @@ echo -e "dtparam=act_led_activelow=on" >> $CONFIG_FILE echo -e "# Set CPU speed to 600MHz - fixes unstable WiFi issue" >> $CONFIG_FILE echo -e "arm_freq=600" >> $CONFIG_FILE +# The bundled CustomPiOS 'raspicam' module enables the LEGACY camera stack +# (start_x=1, camera_auto_detect=0) meant for raspistill/MMAL. This image uses +# picamera2/libcamera, which needs the modern path -- with start_x=1 set, the +# Pi Camera Module is never detected. Switch to libcamera auto-detection. +sed -i 's/^start_x=1/start_x=0/' /boot/firmware/config.txt +sed -i 's/^camera_auto_detect=.*/camera_auto_detect=1/' /boot/firmware/config.txt + # Copy to installation path mkdir -p $INSTALLATION_PATH/NaturewatchCameraServer >/dev/null 2>&1 cp -r $DIR $INSTALLATION_PATH >/dev/null 2>&1 -# Update and upgrade +# Make apt/dpkg fully non-interactive so a modified-config-file prompt +# (e.g. /etc/initramfs-tools/initramfs.conf) can't stall an automated build. +# Writing this apt.conf.d drop-in makes EVERY apt-get below inherit it. +export DEBIAN_FRONTEND=noninteractive +cat > /etc/apt/apt.conf.d/99naturewatch-noninteractive <<'APTEOF' +Dpkg::Options { + "--force-confdef"; + "--force-confold"; +} +APT::Get::Assume-Yes "true"; +APTEOF + +# Update package lists only. +# NOTE: full 'apt-get upgrade' / 'dist-upgrade' is intentionally NOT run here. +# On the (now older) pinned base image it pulls a large kernel upgrade and then +# hangs forever regenerating the arm64 (v8) initramfs under 32-bit armhf QEMU +# emulation. The base image's own kernel runs the camera fine; every package we +# actually need is installed explicitly below. apt-get clean apt-get update -apt-get upgrade -y -apt-get dist-upgrade -y # Install extra packages (with specific versions) to avoid breaking the system: # apt list python3-picamera2 git python3-pip python3-libcamera libcap-dev ffmpeg python3-flask python3-numpy python3-opencv python3-kms++ --installed apt-get install -y python3-picamera2 --no-install-recommends apt-get install -y git python3-pip python3-libcamera libcap-dev ffmpeg python3-flask python3-numpy python3-opencv python3-kms++ +# Bring the libcamera stack up to the build that matches the installed libpisp. +# The base image can ship an older libcamera0.3 built against a different libpisp, +# so rpicam / picamera2 crash with an undefined-symbol error +# (libpisp ... compute_optimal_stride). This upgrades ONLY the camera libraries +# (no kernel), so it won't trigger a full-image upgrade / initramfs rebuild. +apt-get install -y --only-upgrade libcamera0.3 libcamera-ipa python3-libcamera python3-picamera2 + # Setup a venv python -m venv --system-site-packages ${INSTALLATION_PATH}/NaturewatchCameraServer/.venv diff --git a/os/config b/os/config index ca56a4a..f0af3b5 100644 --- a/os/config +++ b/os/config @@ -5,6 +5,7 @@ export BASE_IMAGE_ENLARGEROOT=2000 export AUTO_HOTSPOT_NAME=MyNaturewatch export AUTO_HOTSPOT_PASSWORD=badgersandfoxes export options loop max_loop=128 -export ADMIN_TOOLKIT_UPDATE_PACKAGES=yes +export ADMIN_TOOLKIT_UPDATE_PACKAGES=no export ADMIN_TOOLKIT_NAME=pi export ADMIN_TOOLKIT_PASSWORD=badgersandfoxes +