diff --git a/arduino/hello.ino b/arduino/hello.ino new file mode 100644 index 0000000..47c69e6 --- /dev/null +++ b/arduino/hello.ino @@ -0,0 +1,10 @@ +void setup() { + pinMode(LED_BUILTIN, OUTPUT); +} + +void loop() { + digitalWrite(LED_BUILTIN, HIGH); + delay(10); + digitalWrite(LED_BUILTIN, LOW); + delay(1000); +} diff --git a/pi/ui/lib/main.dart b/pi/ui/lib/main.dart new file mode 100644 index 0000000..121d6cb --- /dev/null +++ b/pi/ui/lib/main.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(const SmartSerowApp()); +} + +class SmartSerowApp extends StatelessWidget { + const SmartSerowApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Smart Serow', + debugShowCheckedModeBanner: false, + theme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.teal, + brightness: Brightness.dark, + ), + useMaterial3: true, + ), + home: const HomePage(), + ); + } +} + +class HomePage extends StatelessWidget { + const HomePage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.terrain, + size: 120, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox(height: 24), + Text( + 'Smart Serow', + style: Theme.of(context).textTheme.headlineLarge?.copyWith( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + Text( + 'System Ready', + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Colors.grey, + ), + ), + ], + ), + ), + ); + } +} diff --git a/pi/ui/pubspec.yaml b/pi/ui/pubspec.yaml new file mode 100644 index 0000000..415dc39 --- /dev/null +++ b/pi/ui/pubspec.yaml @@ -0,0 +1,19 @@ +name: smartserow_ui +description: Smart Serow embedded UI for Raspberry Pi Zero 2W +publish_to: 'none' +version: 0.1.0 + +environment: + sdk: '>=3.0.0 <4.0.0' + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^3.0.0 + +flutter: + uses-material-design: true diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000..bf33186 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Build script for Smart Serow Flutter UI +# Run this in WSL2 with flutter-elinux installed + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +UI_DIR="$PROJECT_ROOT/pi/ui" + +echo "=== Smart Serow Build ===" +echo "Project: $UI_DIR" + +cd "$UI_DIR" + +# Clean previous build (optional, comment out for faster incremental builds) +# flutter-elinux clean + +echo "Fetching dependencies..." +flutter-elinux pub get + +echo "Building for ARM64 (elinux)..." +flutter-elinux build elinux --target-arch=arm64 --release + +BUILD_OUTPUT="$UI_DIR/build/elinux/arm64/release/bundle" + +if [ -d "$BUILD_OUTPUT" ]; then + echo "" + echo "=== Build Complete ===" + echo "Output: $BUILD_OUTPUT" + ls -lh "$BUILD_OUTPUT" +else + echo "ERROR: Build output not found at $BUILD_OUTPUT" + exit 1 +fi diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 0000000..b5d8d2b --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Deploy script for Smart Serow Flutter UI +# Pushes build bundle to Pi and optionally restarts service + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +CONFIG_FILE="$SCRIPT_DIR/deploy_target.json" + +# Parse config +if [ ! -f "$CONFIG_FILE" ]; then + echo "ERROR: Config file not found: $CONFIG_FILE" + exit 1 +fi + +HOST=$(jq -r '.host' "$CONFIG_FILE") +REMOTE_PATH=$(jq -r '.remote_path' "$CONFIG_FILE") +SERVICE_NAME=$(jq -r '.service_name' "$CONFIG_FILE") + +BUILD_DIR="$PROJECT_ROOT/pi/ui/build/elinux/arm64/release/bundle" + +echo "=== Smart Serow Deploy ===" +echo "Target: $HOST:$REMOTE_PATH" +echo "Source: $BUILD_DIR" + +if [ ! -d "$BUILD_DIR" ]; then + echo "ERROR: Build directory not found. Run build.sh first." + exit 1 +fi + +# Sync build to Pi +echo "" +echo "Syncing files..." +rsync -avz --delete \ + "$BUILD_DIR/" \ + "$HOST:$REMOTE_PATH/bundle/" + +# Restart service if requested +RESTART="${1:-}" +if [ "$RESTART" = "--restart" ] || [ "$RESTART" = "-r" ]; then + echo "" + echo "Restarting service: $SERVICE_NAME" + ssh "$HOST" "sudo systemctl restart $SERVICE_NAME" + sleep 2 + ssh "$HOST" "systemctl status $SERVICE_NAME --no-pager" +else + echo "" + echo "Deploy complete. To restart service, run:" + echo " ssh $HOST 'sudo systemctl restart $SERVICE_NAME'" + echo "" + echo "Or run this script with --restart flag" +fi diff --git a/scripts/deploy_target.json b/scripts/deploy_target.json new file mode 100644 index 0000000..2cbcfc5 --- /dev/null +++ b/scripts/deploy_target.json @@ -0,0 +1,5 @@ +{ + "host": "pi@smartserow.local", + "remote_path": "/opt/smartserow", + "service_name": "smartserow-ui" +} diff --git a/scripts/pi_setup.sh b/scripts/pi_setup.sh new file mode 100644 index 0000000..e6cdb79 --- /dev/null +++ b/scripts/pi_setup.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# One-time setup script for Smart Serow on Raspberry Pi +# Run this ON the Pi itself + +set -e + +echo "=== Smart Serow Pi Setup ===" + +# Check if running on Pi (arm architecture) +ARCH=$(uname -m) +if [[ "$ARCH" != "aarch64" && "$ARCH" != "armv7l" ]]; then + echo "WARNING: This doesn't look like a Pi (arch: $ARCH)" + echo "Continuing anyway..." +fi + +APP_DIR="/opt/smartserow" +SERVICE_FILE="/etc/systemd/system/smartserow-ui.service" + +# Create app directory +echo "Creating app directory: $APP_DIR" +sudo mkdir -p "$APP_DIR/bundle" +sudo chown -R pi:pi "$APP_DIR" + +# Install runtime dependencies for flutter-elinux +echo "Installing runtime dependencies..." +sudo apt-get update +sudo apt-get install -y \ + libgl1-mesa-dri \ + libgles2-mesa \ + libegl1-mesa \ + libdrm2 \ + libgbm1 \ + libinput10 \ + libudev1 \ + fonts-noto + +# For X11 debug mode (optional but useful) +sudo apt-get install -y \ + xorg \ + xinit \ + libx11-6 \ + libxkbcommon-x11-0 + +# Copy systemd service +echo "Installing systemd service..." +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if [ -f "$SCRIPT_DIR/smartserow-ui.service" ]; then + sudo cp "$SCRIPT_DIR/smartserow-ui.service" "$SERVICE_FILE" +else + echo "WARNING: Service file not found at $SCRIPT_DIR/smartserow-ui.service" + echo "Copy it manually to $SERVICE_FILE" +fi + +# Enable service +echo "Enabling service..." +sudo systemctl daemon-reload +sudo systemctl enable smartserow-ui + +# Add pi user to required groups for DRM/KMS access +echo "Setting up permissions..." +sudo usermod -aG video pi +sudo usermod -aG input pi +sudo usermod -aG render pi 2>/dev/null || true + +echo "" +echo "=== Setup Complete ===" +echo "" +echo "Next steps:" +echo "1. Deploy the app: run deploy.sh from your dev machine" +echo "2. Start service: sudo systemctl start smartserow-ui" +echo "3. Or reboot: sudo reboot" +echo "" +echo "Useful commands:" +echo " systemctl status smartserow-ui # Check status" +echo " journalctl -u smartserow-ui -f # View logs" +echo "" diff --git a/scripts/smartserow-ui.service b/scripts/smartserow-ui.service new file mode 100644 index 0000000..7f02d44 --- /dev/null +++ b/scripts/smartserow-ui.service @@ -0,0 +1,30 @@ +[Unit] +Description=Smart Serow UI +After=multi-user.target +Wants=multi-user.target + +[Service] +Type=simple +User=pi +Group=pi +WorkingDirectory=/opt/smartserow/bundle + +# DRM/KMS backend for direct framebuffer (no X11 needed) +ExecStart=/opt/smartserow/bundle/smartserow_ui -b drm + +# Restart on crash +Restart=always +RestartSec=3 + +# Environment for DRM/KMS access +Environment=XDG_RUNTIME_DIR=/run/user/1000 +Environment=HOME=/home/pi + +# Give time for GPU to initialize on boot +ExecStartPre=/bin/sleep 2 + +# Ensure clean shutdown +TimeoutStopSec=10 + +[Install] +WantedBy=multi-user.target