debian-laptop

This commit is contained in:
legop3
2026-06-30 23:34:05 -04:00
parent 37b8f71944
commit 282cb3e135
15 changed files with 504 additions and 35 deletions
BIN
View File
Binary file not shown.
Vendored
BIN
View File
Binary file not shown.
Vendored Executable
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+131
View File
@@ -0,0 +1,131 @@
#!/usr/bin/env bash
set -euo pipefail
set +H
ENV_FILE="${ROVERD_MEDIA_ENV_FILE:-/var/lib/roverd/media.env}"
if [[ ! -f "$ENV_FILE" ]]; then
echo "Environment file ${ENV_FILE} missing; cannot publish laptop video" >&2
exit 1
fi
# Load roverd's generated media.env as data instead of sourcing it as shell.
# The SRT publish URL contains normal query-string characters like '&' and '#!',
# so evaluating the file would be both fragile and unnecessary.
load_env_file() {
local content=""
if [[ -r "$ENV_FILE" ]]; then
content="$(cat "$ENV_FILE")"
elif command -v sudo >/dev/null 2>&1; then
content="$(sudo -n cat "$ENV_FILE" 2>/dev/null || true)"
fi
if [[ -z "$content" ]]; then
echo "Cannot read ${ENV_FILE} (permission denied). Run as a user that can read it, or allow sudo -n for cat." >&2
exit 1
fi
local line key val
while IFS= read -r line || [[ -n "$line" ]]; do
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
[[ "$line" =~ ^[[:space:]]*# ]] && continue
if [[ "$line" =~ ^[[:space:]]*export[[:space:]]+([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
key="${BASH_REMATCH[1]}"
val="${BASH_REMATCH[2]}"
elif [[ "$line" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
key="${BASH_REMATCH[1]}"
val="${BASH_REMATCH[2]}"
else
continue
fi
val="${val#${val%%[![:space:]]*}}"
val="${val%${val##*[![:space:]]}}"
if [[ "$val" =~ ^\".*\"$ ]]; then
val="${val:1:${#val}-2}"
elif [[ "$val" =~ ^\'.*\'$ ]]; then
val="${val:1:${#val}-2}"
fi
printf -v "$key" '%s' "$val"
export "$key"
done <<< "$content"
}
load_env_file
: "${ROVERD_VIDEO_ENABLE:?ROVERD_VIDEO_ENABLE not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_PUBLISH_URL:?ROVERD_VIDEO_PUBLISH_URL not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_DEVICE:?ROVERD_VIDEO_DEVICE not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_WIDTH:?ROVERD_VIDEO_WIDTH not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_HEIGHT:?ROVERD_VIDEO_HEIGHT not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_FPS:?ROVERD_VIDEO_FPS not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_BITRATE:?ROVERD_VIDEO_BITRATE not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_INVERT:?ROVERD_VIDEO_INVERT not set in ${ENV_FILE}}"
if [[ "${ROVERD_VIDEO_ENABLE}" -ne 1 ]]; then
echo "Laptop video publisher disabled by roverd media config; skipping" >&2
exit 0
fi
if [[ -z "${ROVERD_VIDEO_DEVICE}" ]]; then
echo "ROVERD_VIDEO_DEVICE is required for the debian-laptop V4L2 publisher" >&2
exit 1
fi
if [[ -n "${FFMPEG_BIN:-}" ]]; then
FFMPEG_BIN_PATH="$FFMPEG_BIN"
elif command -v ffmpeg >/dev/null 2>&1; then
FFMPEG_BIN_PATH="$(command -v ffmpeg)"
else
echo "ffmpeg not found; install it via apt install ffmpeg." >&2
exit 1
fi
VIDEO_FILTER_ARGS=()
if [[ "${ROVERD_VIDEO_INVERT}" -ne 0 ]]; then
# The Pi publisher uses libcamera rotation. With a generic webcam, ffmpeg's
# transpose pair gives the same 180-degree correction without assuming a
# camera-specific driver feature.
VIDEO_FILTER_ARGS=(-vf "transpose=2,transpose=2")
fi
run_pipeline() {
"${FFMPEG_BIN_PATH}" \
-hide_banner \
-loglevel warning \
-fflags nobuffer \
-flags low_delay \
-thread_queue_size 4096 \
-f v4l2 \
-framerate "${ROVERD_VIDEO_FPS}" \
-video_size "${ROVERD_VIDEO_WIDTH}x${ROVERD_VIDEO_HEIGHT}" \
-i "${ROVERD_VIDEO_DEVICE}" \
"${VIDEO_FILTER_ARGS[@]}" \
-an \
-c:v libx264 \
-preset veryfast \
-tune zerolatency \
-profile:v baseline \
-pix_fmt yuv420p \
-b:v "${ROVERD_VIDEO_BITRATE}" \
-maxrate "${ROVERD_VIDEO_BITRATE}" \
-bufsize "$((ROVERD_VIDEO_BITRATE / 2))" \
-g "${ROVERD_VIDEO_FPS}" \
-keyint_min "${ROVERD_VIDEO_FPS}" \
-sc_threshold 0 \
-flush_packets 1 \
-muxdelay 0 \
-muxpreload 0 \
-f mpegts \
"${ROVERD_VIDEO_PUBLISH_URL}"
}
while true; do
if run_pipeline; then
exit 0
fi
echo "Debian laptop video publisher exited, restarting in 2s..." >&2
sleep 2
done
+164 -26
View File
@@ -1,11 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# Installer for the roverd agent on Raspberry Pi # Installer for the roverd agent on Raspberry Pi and Debian laptop rover hosts.
set -euo pipefail set -euo pipefail
BINARY_SRC="dist/roverd" PROFILE="pi"
CONFIG_SRC="pi/roverd/roverd.sample.yaml" BINARY_SRC=""
CONFIG_SRC=""
REPO_ROOT="$(pwd -P)" REPO_ROOT="$(pwd -P)"
usage() { usage() {
@@ -13,6 +14,8 @@ usage() {
Usage: sudo ./pi/install_roverd.sh [options] Usage: sudo ./pi/install_roverd.sh [options]
Options: Options:
--profile <name> Install profile: pi or debian-laptop (default: pi)
--debian-laptop Shortcut for --profile debian-laptop
-b, --binary <path> Path to the roverd binary (default: dist/roverd) -b, --binary <path> Path to the roverd binary (default: dist/roverd)
-c, --config <path> Source config to install if /etc/roverd.yaml is missing -c, --config <path> Source config to install if /etc/roverd.yaml is missing
(default: pi/roverd/roverd.sample.yaml) (default: pi/roverd/roverd.sample.yaml)
@@ -21,8 +24,8 @@ Options:
The script must run from the repository root and as root (sudo). It will: The script must run from the repository root and as root (sudo). It will:
* create system users/groups if needed * create system users/groups if needed
* install /usr/local/bin/roverd and /etc/roverd.yaml * install /usr/local/bin/roverd and /etc/roverd.yaml
* install /usr/local/bin/video/audio helpers and systemd units * install profile-specific video helpers and shared audio helpers
* install fixed-location Google Chrome TTS assets for roverd * install Pi-only Google Chrome TTS assets when using the pi profile
* install the fixed-command self-update helper used by admin-triggered updates * install the fixed-command self-update helper used by admin-triggered updates
* enable roverd.service and media publisher/listener services * enable roverd.service and media publisher/listener services
USAGE USAGE
@@ -30,6 +33,14 @@ USAGE
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--profile)
PROFILE="${2:-}"
shift 2
;;
--debian-laptop)
PROFILE="debian-laptop"
shift
;;
-b|--binary) -b|--binary)
BINARY_SRC="${2:-}" BINARY_SRC="${2:-}"
shift 2 shift 2
@@ -50,6 +61,32 @@ while [[ $# -gt 0 ]]; do
esac esac
done done
case "$PROFILE" in
pi)
BINARY_SRC="${BINARY_SRC:-dist/roverd}"
CONFIG_SRC="${CONFIG_SRC:-pi/roverd/roverd.sample.yaml}"
VIDEO_HELPER_SRC="pi/bin/video-publisher.sh"
VIDEO_HELPER_DEST="/usr/local/bin/video-publisher"
VIDEO_SERVICE_SRC="pi/systemd/video-publisher.service"
VIDEO_SERVICE_NAME="video-publisher.service"
ROVERD_GROUPS="dialout,gpio,video,render,audio"
;;
debian-laptop)
BINARY_SRC="${BINARY_SRC:-dist/roverd-debian-laptop}"
CONFIG_SRC="${CONFIG_SRC:-pi/roverd/roverd.debian-laptop.sample.yaml}"
VIDEO_HELPER_SRC="pi/bin/debian-laptop-video-publisher.sh"
VIDEO_HELPER_DEST="/usr/local/bin/debian-laptop-video-publisher"
VIDEO_SERVICE_SRC="pi/systemd/debian-laptop-video-publisher.service"
VIDEO_SERVICE_NAME="debian-laptop-video-publisher.service"
ROVERD_GROUPS="dialout,video,render,audio"
;;
*)
echo "Unknown profile: $PROFILE" >&2
usage
exit 1
;;
esac
if [[ "${EUID}" -ne 0 ]]; then if [[ "${EUID}" -ne 0 ]]; then
echo "Please run as root (sudo)" >&2 echo "Please run as root (sudo)" >&2
exit 1 exit 1
@@ -68,14 +105,37 @@ fi
ensure_user() { ensure_user() {
local user="$1" local user="$1"
local groups="${2:-}" local groups="${2:-}"
local existing_groups=""
if ! id -u "$user" >/dev/null 2>&1; then if ! id -u "$user" >/dev/null 2>&1; then
if [[ -n "$groups" ]]; then if [[ -n "$groups" ]]; then
useradd -r -s /usr/sbin/nologin -G "$groups" "$user" local group
IFS=',' read -ra group_list <<< "$groups"
for group in "${group_list[@]}"; do
if getent group "$group" >/dev/null 2>&1; then
existing_groups="${existing_groups:+$existing_groups,}$group"
else
log "Skipping missing system group '$group' for $user"
fi
done
fi
if [[ -n "$existing_groups" ]]; then
useradd -r -s /usr/sbin/nologin -G "$existing_groups" "$user"
else else
useradd -r -s /usr/sbin/nologin "$user" useradd -r -s /usr/sbin/nologin "$user"
fi fi
elif [[ -n "$groups" ]]; then elif [[ -n "$groups" ]]; then
usermod -a -G "$groups" "$user" local group
IFS=',' read -ra group_list <<< "$groups"
for group in "${group_list[@]}"; do
if getent group "$group" >/dev/null 2>&1; then
existing_groups="${existing_groups:+$existing_groups,}$group"
else
log "Skipping missing system group '$group' for $user"
fi
done
if [[ -n "$existing_groups" ]]; then
usermod -a -G "$existing_groups" "$user"
fi
fi fi
} }
@@ -83,7 +143,7 @@ log() {
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*" echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*"
} }
if ! command -v rpicam-vid >/dev/null 2>&1 && ! command -v libcamera-vid >/dev/null 2>&1; then if [[ "$PROFILE" == "pi" ]] && ! command -v rpicam-vid >/dev/null 2>&1 && ! command -v libcamera-vid >/dev/null 2>&1; then
log "WARNING: neither rpicam-vid nor libcamera-vid found in PATH; install libcamera-apps." log "WARNING: neither rpicam-vid nor libcamera-vid found in PATH; install libcamera-apps."
fi fi
@@ -97,6 +157,23 @@ install_video_deps() {
apt-get install -y --no-install-recommends libcamera-apps ffmpeg apt-get install -y --no-install-recommends libcamera-apps ffmpeg
} }
install_debian_laptop_deps() {
# The debian-laptop profile is intentionally Debian-specific, so using apt
# and systemd directly is simpler than pretending this installer is a
# cross-distro abstraction. v4l-utils is included for local camera inspection
# with tools such as v4l2-ctl while ffmpeg owns publishing.
if command -v ffmpeg >/dev/null 2>&1 \
&& command -v arecord >/dev/null 2>&1 \
&& command -v aplay >/dev/null 2>&1 \
&& command -v v4l2-ctl >/dev/null 2>&1; then
log "Debian laptop media dependencies already installed; skipping apt install"
return
fi
log "Installing Debian laptop media dependencies (ffmpeg, ALSA tools, V4L2 tools)..."
apt-get update
apt-get install -y --no-install-recommends ffmpeg alsa-utils v4l-utils ca-certificates
}
find_boot_config() { find_boot_config() {
if [[ -f /boot/firmware/config.txt ]]; then if [[ -f /boot/firmware/config.txt ]]; then
printf "/boot/firmware/config.txt" printf "/boot/firmware/config.txt"
@@ -129,9 +206,35 @@ ensure_pwm_overlay() {
log "Enabled dtoverlay=pwm-2chan on GPIO12/13 in $boot_config (backup at $backup). Reboot required for changes to apply." log "Enabled dtoverlay=pwm-2chan on GPIO12/13 in $boot_config (backup at $backup). Reboot required for changes to apply."
} }
ensure_user roverd "dialout,gpio,video,render,audio" install_profile_dependencies() {
case "$PROFILE" in
pi)
install_video_deps
ensure_pwm_overlay
;;
debian-laptop)
install_debian_laptop_deps
;;
esac
}
install_profile_audio_support() {
case "$PROFILE" in
pi)
install_audio_support
;;
debian-laptop)
# Laptop audio uses the distro's normal ALSA "default" device by
# default. Avoid installing the Raspberry Pi AIY asound.conf because
# that would break ordinary laptop sound routing.
log "Skipping Pi AIY audio setup for debian-laptop profile"
;;
esac
}
ensure_user roverd "$ROVERD_GROUPS"
install -o roverd -g roverd -m 0755 "$BINARY_SRC" /usr/local/bin/roverd install -o roverd -g roverd -m 0755 "$BINARY_SRC" /usr/local/bin/roverd
log "Installed roverd binary" log "Installed roverd binary for profile $PROFILE"
install_self_update_support() { install_self_update_support() {
local sudoers_file="/etc/sudoers.d/roverd-self-update" local sudoers_file="/etc/sudoers.d/roverd-self-update"
@@ -185,8 +288,7 @@ fi
install -m 0644 pi/systemd/roverd.service /etc/systemd/system/roverd.service install -m 0644 pi/systemd/roverd.service /etc/systemd/system/roverd.service
log "Installed roverd systemd unit" log "Installed roverd systemd unit"
install_video_deps install_profile_dependencies
ensure_pwm_overlay
# Enable Google AIY v1 sound card, ALSA defaults, and TTS engines # Enable Google AIY v1 sound card, ALSA defaults, and TTS engines
install_audio_support() { install_audio_support() {
@@ -298,11 +400,13 @@ install_google_tts_assets() {
log "Installed Google Chrome TTS assets to $asset_dir" log "Installed Google Chrome TTS assets to $asset_dir"
} }
# Install video publisher assets # Install the profile-specific video publisher. The audio publisher/listener are
install -D -o root -g root -m 0755 pi/bin/video-publisher.sh /usr/local/bin/video-publisher # shared because both Pi and Debian laptop profiles use the same media.env audio
log "Installed video-publisher helper" # contract and ffmpeg/ALSA command shape.
install -m 0644 pi/systemd/video-publisher.service /etc/systemd/system/video-publisher.service install -D -o root -g root -m 0755 "$VIDEO_HELPER_SRC" "$VIDEO_HELPER_DEST"
log "Installed video-publisher systemd unit" log "Installed $PROFILE video publisher helper"
install -m 0644 "$VIDEO_SERVICE_SRC" "/etc/systemd/system/$VIDEO_SERVICE_NAME"
log "Installed $PROFILE video publisher systemd unit"
# Install audio-only publisher assets # Install audio-only publisher assets
install -D -o root -g root -m 0755 pi/bin/audio-only-publisher.sh /usr/local/bin/audio-only-publisher install -D -o root -g root -m 0755 pi/bin/audio-only-publisher.sh /usr/local/bin/audio-only-publisher
install -m 0644 pi/systemd/audio-only-publisher.service /etc/systemd/system/audio-only-publisher.service install -m 0644 pi/systemd/audio-only-publisher.service /etc/systemd/system/audio-only-publisher.service
@@ -312,7 +416,9 @@ install -D -o root -g root -m 0755 pi/bin/audio-forward-listener.sh /usr/local/b
install -m 0644 pi/systemd/audio-forward-listener.service /etc/systemd/system/audio-forward-listener.service install -m 0644 pi/systemd/audio-forward-listener.service /etc/systemd/system/audio-forward-listener.service
log "Installed audio-forward listener helper + systemd unit" log "Installed audio-forward listener helper + systemd unit"
install -d -o roverd -g roverd /var/lib/roverd install -d -o roverd -g roverd /var/lib/roverd
cat > /var/lib/roverd/media.env <<'ENV' case "$PROFILE" in
pi)
cat > /var/lib/roverd/media.env <<'ENV'
# Managed by roverd; placeholder values will be overwritten at runtime. # Managed by roverd; placeholder values will be overwritten at runtime.
ROVERD_VIDEO_ENABLE=1 ROVERD_VIDEO_ENABLE=1
ROVERD_VIDEO_PUBLISHER=pi-libcamera ROVERD_VIDEO_PUBLISHER=pi-libcamera
@@ -336,6 +442,34 @@ ROVERD_AUDIO_PLAYBACK_DEVICE=forward
ROVERD_AUDIO_PLAYBACK_NORMALIZE=1 ROVERD_AUDIO_PLAYBACK_NORMALIZE=1
ROVERD_AUDIO_PLAYBACK_NORMALIZE_FILTER=dynaudnorm=f=75:g=15:m=10:p=0.9,alimiter=limit=0.85:level=disabled ROVERD_AUDIO_PLAYBACK_NORMALIZE_FILTER=dynaudnorm=f=75:g=15:m=10:p=0.9,alimiter=limit=0.85:level=disabled
ENV ENV
;;
debian-laptop)
cat > /var/lib/roverd/media.env <<'ENV'
# Managed by roverd; placeholder values will be overwritten at runtime.
ROVERD_VIDEO_ENABLE=1
ROVERD_VIDEO_PUBLISHER=debian-laptop-v4l2
ROVERD_VIDEO_PUBLISH_URL=srt://192.168.0.86:9000?streamid=#!::r=CHANGE_ME,m=publish&latency=10&mode=caller&transtype=live&pkt_size=1316
ROVERD_VIDEO_DEVICE=/dev/video0
ROVERD_VIDEO_WIDTH=640
ROVERD_VIDEO_HEIGHT=480
ROVERD_VIDEO_FPS=30
ROVERD_VIDEO_BITRATE=2000000
ROVERD_VIDEO_INVERT=0
ROVERD_VIDEO_SENSOR_MODE=
ROVERD_AUDIO_CAPTURE_ENABLE=1
ROVERD_AUDIO_CAPTURE_PUBLISH_URL=srt://192.168.0.86:9000?streamid=#!::r=CHANGE_ME-audio,m=publish&latency=10&mode=caller&transtype=live&pkt_size=1316
ROVERD_AUDIO_CAPTURE_DEVICE=default
ROVERD_AUDIO_CAPTURE_SAMPLE_RATE=48000
ROVERD_AUDIO_CAPTURE_CHANNELS=2
ROVERD_AUDIO_CAPTURE_BITRATE=510000
ROVERD_AUDIO_PLAYBACK_ENABLE=1
ROVERD_AUDIO_PLAYBACK_FORWARD_URL=srt://192.168.0.86:9000?streamid=#!::r=CHANGE_ME-fwd,m=request&latency=10&mode=caller&transtype=live&pkt_size=1316
ROVERD_AUDIO_PLAYBACK_DEVICE=default
ROVERD_AUDIO_PLAYBACK_NORMALIZE=1
ROVERD_AUDIO_PLAYBACK_NORMALIZE_FILTER=dynaudnorm=f=75:g=15:m=10:p=0.9,alimiter=limit=0.85:level=disabled
ENV
;;
esac
chown roverd:roverd /var/lib/roverd/media.env chown roverd:roverd /var/lib/roverd/media.env
chmod 0640 /var/lib/roverd/media.env chmod 0640 /var/lib/roverd/media.env
# Create persistent audio FIFO for capture -> publisher # Create persistent audio FIFO for capture -> publisher
@@ -349,25 +483,29 @@ else
chown roverd:audio "$FIFO_PATH" chown roverd:audio "$FIFO_PATH"
chmod 0660 "$FIFO_PATH" chmod 0660 "$FIFO_PATH"
fi fi
# Ensure ALSA config is in place for rovermic device if [[ "$PROFILE" == "pi" ]]; then
install -m 0644 pi/asound.conf /etc/asound.conf # Ensure ALSA config is in place for the Raspberry Pi Google Voice HAT
log "Installed ALSA config (/etc/asound.conf)" # rovermic/forward devices. Debian laptop installs should keep their normal
# distro sound configuration and use device: default in roverd.yaml.
install -m 0644 pi/asound.conf /etc/asound.conf
log "Installed ALSA config (/etc/asound.conf)"
fi
install_audio_support install_profile_audio_support
systemctl daemon-reload systemctl daemon-reload
systemctl enable roverd.service systemctl enable roverd.service
systemctl enable video-publisher.service systemctl enable "$VIDEO_SERVICE_NAME"
systemctl enable audio-only-publisher.service systemctl enable audio-only-publisher.service
systemctl enable audio-forward-listener.service systemctl enable audio-forward-listener.service
if [[ $CONFIG_EXISTS -eq 1 ]]; then if [[ $CONFIG_EXISTS -eq 1 ]]; then
systemctl restart roverd.service systemctl restart roverd.service
systemctl restart video-publisher.service systemctl restart "$VIDEO_SERVICE_NAME"
systemctl restart audio-only-publisher.service systemctl restart audio-only-publisher.service
systemctl restart audio-forward-listener.service systemctl restart audio-forward-listener.service
log "Restarted roverd + media publishers/listener" log "Restarted roverd + media publishers/listener"
else else
log "Skipped auto-start because config is the sample; edit $CONFIG_DEST then run: sudo systemctl restart roverd video-publisher audio-only-publisher audio-forward-listener" log "Skipped auto-start because config is the sample; edit $CONFIG_DEST then run: sudo systemctl restart roverd ${VIDEO_SERVICE_NAME%.service} audio-only-publisher audio-forward-listener"
fi fi
log "Install complete" log "Install complete for profile $PROFILE"
+10 -6
View File
@@ -3,18 +3,22 @@ GOOS ?= linux
GOARCH ?= arm GOARCH ?= arm
GOARM ?= 6 GOARM ?= 6
.PHONY: build pi-build dummy clean .PHONY: build pi-build debian-laptop dummy verifier-tools clean
build: build: pi-build debian-laptop dummy
go build -o $(BIN_DIR)/roverd ./cmd/roverd
pi-build: verifier-tools:
GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build -trimpath -ldflags="-s -w" -o $(BIN_DIR)/roverd ./cmd/roverd
GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build -trimpath -ldflags="-s -w" -o $(BIN_DIR)/servoverifier ./cmd/servoverifier GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build -trimpath -ldflags="-s -w" -o $(BIN_DIR)/servoverifier ./cmd/servoverifier
GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build -trimpath -ldflags="-s -w" -o $(BIN_DIR)/hornverifier ./cmd/hornverifier GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build -trimpath -ldflags="-s -w" -o $(BIN_DIR)/hornverifier ./cmd/hornverifier
pi-build: verifier-tools
GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build -trimpath -ldflags="-s -w" -o $(BIN_DIR)/roverd ./cmd/roverd
debian-laptop:
GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -tags debian_laptop -o $(BIN_DIR)/roverd-debian-laptop ./cmd/roverd
dummy: dummy:
GOOS=linux GOARCH=amd64 go build -tags dummy -o $(BIN_DIR)/roverd-dummy ./cmd/roverd GOOS=linux GOARCH=amd64 go build -tags dummy -o $(BIN_DIR)/roverd-dummy ./cmd/roverd
clean: clean:
rm -f $(BIN_DIR)/roverd $(BIN_DIR)/servoverifier $(BIN_DIR)/hornverifier rm -f $(BIN_DIR)/roverd $(BIN_DIR)/roverd-debian-laptop $(BIN_DIR)/roverd-dummy $(BIN_DIR)/servoverifier $(BIN_DIR)/hornverifier
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !dummy //go:build !dummy && !debian_laptop
package roverd package roverd
+25
View File
@@ -0,0 +1,25 @@
//go:build debian_laptop
package roverd
import (
"context"
"fmt"
"log"
)
type BRCPulser struct{}
func NewBRCPulser(_ BRCConfig, _ *log.Logger) (*BRCPulser, error) {
/*
The first Debian laptop profile keeps BRC disabled instead of pretending a
USB serial modem-control line has already been selected and tested. The
config can set brc.gpioPin: -1 to skip construction entirely; if someone
enables it, fail loudly so the rover does not silently miss wake pulses.
*/
return nil, fmt.Errorf("brc is not supported in the debian-laptop build yet")
}
func (b *BRCPulser) Close() {}
func (b *BRCPulser) Start(ctx context.Context) {}
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !dummy //go:build !dummy && !debian_laptop
package roverd package roverd
+38
View File
@@ -0,0 +1,38 @@
//go:build debian_laptop
package roverd
import (
"fmt"
"log"
)
type CameraServo struct{}
func NewCameraServo(_ CameraServoConfig, _ *log.Logger) (*CameraServo, error) {
/*
The Debian laptop profile starts with the laptop's built-in webcam and no
Pi PWM servo. If a laptop rover eventually grows an external servo board,
it should get its own implementation instead of reusing Raspberry Pi GPIO
assumptions.
*/
return nil, fmt.Errorf("camera servo not supported in the debian-laptop build")
}
func (c *CameraServo) Close() {}
func (c *CameraServo) SetAngle(angle float64) error {
return fmt.Errorf("camera servo disabled")
}
func (c *CameraServo) Nudge(delta float64) error {
return fmt.Errorf("camera servo disabled")
}
func (c *CameraServo) SetPulseWidth(micros int) error {
return fmt.Errorf("camera servo disabled")
}
func (c *CameraServo) CurrentAngle() float64 {
return 0
}
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !dummy //go:build !dummy && !debian_laptop
package roverd package roverd
+32
View File
@@ -0,0 +1,32 @@
//go:build debian_laptop
package roverd
import (
"fmt"
"log"
)
type GPIOToggle struct {
name string
}
func NewGPIOToggle(name string, _ GPIOToggleConfig, _ *log.Logger) (*GPIOToggle, error) {
/*
A Debian laptop has no Raspberry Pi GPIO character-device contract for
headlights or lasers. Returning an error when enabled makes bad laptop
configs fail during startup instead of advertising controls that cannot
change any hardware.
*/
return nil, fmt.Errorf("%s not supported in the debian-laptop build", name)
}
func (g *GPIOToggle) Close() {}
func (g *GPIOToggle) HandleAction(action string) error {
return fmt.Errorf("%s not supported in the debian-laptop build", g.name)
}
func (g *GPIOToggle) On() bool {
return false
}
@@ -0,0 +1,84 @@
# Sample configuration for roverd on a Debian laptop rover.
name: laptop-rover
description: "Debian laptop rover using USB serial, webcam, mic, and speakers."
color: "#4DB6AC"
serverUrl: ws://control-server.local:8080/rover
serial:
device: /dev/ttyUSB0
baud: 115200
# BRC is disabled in the first Debian laptop profile because USB serial
# RTS/DTR wake-pulse support needs adapter-specific testing before it should be
# trusted to keep a Roomba awake.
brc:
gpioPin: -1
battery:
full: 2068
warn: 1700
urgent: 1650
maxWheelSpeed: 350
media:
publishPort: 9000
manage: true
healthUrl: ""
healthInterval: 30s
video:
enabled: true
service: debian-laptop-video-publisher.service
publisher: debian-laptop-v4l2
device: /dev/video0
width: 640
height: 480
fps: 30
bitrate: 2000000
inverted: false
audioCapture:
enabled: true
service: audio-only-publisher.service
device: default
sampleRate: 48000
channels: 2
bitrate: 510000
audioPlayback:
enabled: true
service: audio-forward-listener.service
device: default
normalize: true
cameraServo:
enabled: false
audio:
ttsEnabled: false
horn:
enabled: false
headlight:
enabled: false
laser:
enabled: false
autoSideBrush:
enabled: true
speed: 20
private:
enabled: false
safety:
speedLimitEnabled: false
speedLimitMaxWheelSpeed: 250
hardOvercurrentEnabled: false
overcurrentStopMs: 300
hardBumpEnabled: false
bumpBackoffSpeed: 250
bumpBackoffMs: 350
cliffEnabled: false
cliffBackoffSpeed: 250
cliffBackoffMs: 500
triggerCooldownMs: 800
@@ -0,0 +1,17 @@
[Unit]
Description=Rover Debian Laptop Video Publisher (V4L2 -> SRT)
After=network-online.target roverd.service
Wants=network-online.target
[Service]
Type=simple
User=roverd
Group=roverd
WorkingDirectory=/var/lib/roverd
EnvironmentFile=/var/lib/roverd/media.env
ExecStart=/usr/local/bin/debian-laptop-video-publisher
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target