Touchscreen kiosk for the Raspberry Pi HQ camera (IMX477) used as a microscope: live preview from boot, raw frame recording, full-resolution stills, and automatic capture-SSD mounting. - app.py: GTK3 fullscreen UI over cage — preview, REC/STOP, PHOTO, 90-degree rotate button, tap-to-open exposure/gain/settings sliders, and an "SSD NOT MOUNTED" badge - camera.py: picamera2 wrapper with separate video (ISP preview + packed 12-bit raw) and still (4056x3040) configurations - recorder.py: writer thread turning the raw stream into one Bayer frame per file plus index.jsonl/session.json, with dropped/missed accounting - photos.py: still capture to DNG + JPEG, and a background worker that renders a viewable 16-bit sRGB TIFF from the 12-bit Bayer frame - storage.py: SSD mount state and the date/time/format session layout - tools/unpack_raw.py: desktop-side unpacker/demosaicer - system/ + install.sh: systemd units, udev rule, mount helper, sudoers Note: raw frames are written byte-for-byte as the sensor produced them, so they stay decodeable regardless of how the stills pipeline evolves.
97 lines
2.6 KiB
Bash
Executable File
97 lines
2.6 KiB
Bash
Executable File
#!/bin/bash
|
|
# Manage the microscope capture SSD mount.
|
|
#
|
|
# microscope-ssd mount mount the SSD (waits briefly for it to appear)
|
|
# microscope-ssd umount flush and unmount safely
|
|
# microscope-ssd status print: mounted | stale | unmounted | absent
|
|
#
|
|
# Called by the udev-triggered microscope-ssd-mount.service when the drive is
|
|
# plugged in, by fstab at boot, and by the kiosk UI (through a NOPASSWD sudoers
|
|
# entry) when the operator taps MOUNT SSD.
|
|
#
|
|
# stdout stays machine-readable (status) while everything also lands in a log
|
|
# file, because udev and systemd capture stdout in ways that are awkward to read.
|
|
set -u
|
|
|
|
MOUNT_POINT="${MICROSCOPE_MOUNT:-/mnt/microscope}"
|
|
UUID="${MICROSCOPE_UUID:-caf4320b-0fa3-4674-bfa9-f17f6165f681}"
|
|
OWNER="${MICROSCOPE_OWNER:-tyler:tyler}"
|
|
DEV="/dev/disk/by-uuid/${UUID}"
|
|
LOG_FILE="${MICROSCOPE_LOG:-/run/microscope-ssd.log}"
|
|
|
|
log() {
|
|
local message="microscope-ssd: $*"
|
|
echo "$message" >>"$LOG_FILE" 2>/dev/null || true
|
|
echo "$message"
|
|
}
|
|
|
|
in_mounts() { grep -qs " ${MOUNT_POINT} " /proc/self/mounts; }
|
|
|
|
do_status() {
|
|
# 'stale' means a mount entry survived a yank: the device node is gone.
|
|
if in_mounts; then
|
|
if [ -e "$DEV" ]; then echo mounted; else echo stale; fi
|
|
elif [ -e "$DEV" ]; then
|
|
echo unmounted
|
|
else
|
|
echo absent
|
|
fi
|
|
}
|
|
|
|
do_mount() {
|
|
mkdir -p "$MOUNT_POINT"
|
|
|
|
if in_mounts; then
|
|
if [ -e "$DEV" ]; then
|
|
log "already mounted"
|
|
return 0
|
|
fi
|
|
log "clearing stale mount entry"
|
|
umount -l "$MOUNT_POINT" 2>/dev/null || true
|
|
fi
|
|
|
|
for _ in $(seq 1 40); do
|
|
[ -e "$DEV" ] && break
|
|
sleep 0.25
|
|
done
|
|
if [ ! -e "$DEV" ]; then
|
|
log "SSD not present ($DEV)"
|
|
return 1
|
|
fi
|
|
|
|
if mount -t ext4 -o noatime,errors=remount-ro "$DEV" "$MOUNT_POINT"; then
|
|
chown "$OWNER" "$MOUNT_POINT" 2>/dev/null || true
|
|
chmod 755 "$MOUNT_POINT" 2>/dev/null || true
|
|
log "mounted $DEV at $MOUNT_POINT"
|
|
return 0
|
|
fi
|
|
log "mount failed"
|
|
return 1
|
|
}
|
|
|
|
do_umount() {
|
|
if ! in_mounts; then
|
|
log "not mounted"
|
|
return 0
|
|
fi
|
|
sync
|
|
if umount "$MOUNT_POINT"; then
|
|
log "unmounted (safe to unplug)"
|
|
return 0
|
|
fi
|
|
log "unmount failed - something is still using $MOUNT_POINT"
|
|
return 1
|
|
}
|
|
|
|
ACTION="${1:-status}"
|
|
case "$ACTION" in
|
|
mount|umount) log "--- $(date -Is) action=$ACTION euid=$(id -u) ---" ;;
|
|
esac
|
|
|
|
case "$ACTION" in
|
|
mount) do_mount ;;
|
|
umount) do_umount ;;
|
|
status) do_status ;;
|
|
*) echo "usage: $0 {mount|umount|status}" >&2; exit 2 ;;
|
|
esac
|