diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 23adf9ad..43df953c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -10,7 +10,7 @@ on: jobs: custom-firmware-build: - runs-on: ubuntu-24.04 + runs-on: ubuntu-24.04-arm steps: - name: Checkout uses: actions/checkout@v3 @@ -31,19 +31,30 @@ jobs: key: kernels-${{ runner.os }}-${{ runner.arch }}-${{ env.KERNEL_SHA }} - name: Install build dependencies - run: - sudo apt-get update -y && sudo apt-get install -y build-essential cmake gcc-aarch64-linux-gnu + run: | + sudo apt-get update -y && + sudo apt-get install -y build-essential cmake \ + gcc-aarch64-linux-gnu pkg-config squashfs-tools git-core \ + bc libssl-dev - name: Download firmware run: make firmware - - name: Build with Custom - run: make custom_firmware CUSTOM_FIRMWARE_FILE=firmware_custom.bin + - name: Build with Basic firmware + run: make basic_firmware BASIC_FIRMWARE_FILE=firmware_basic.bin + + - name: Build with Extended firmware + run: make extended_firmware EXTENDED_FIRMWARE_FILE=firmware_extended.bin - name: Clear Kernel Git Repo run: git -C tmp/kernel/ clean -fdx - uses: actions/upload-artifact@v4 with: - name: custom-build - path: firmware_custom.bin + name: basic-build + path: firmware_basic.bin + + - uses: actions/upload-artifact@v4 + with: + name: extended-build + path: firmware_extended.bin diff --git a/Makefile b/Makefile index a214e141..b16f19f6 100644 --- a/Makefile +++ b/Makefile @@ -5,15 +5,20 @@ all: tools # ================= Build Tools ================= DEBUG_FIRMWARE_FILE := firmware/firmware_debug.bin -CUSTOM_FIRMWARE_FILE := firmware/firmware_custom.bin +BASIC_FIRMWARE_FILE := firmware/firmware_basic.bin +EXTENDED_FIRMWARE_FILE := firmware/firmware_extended.bin $(DEBUG_FIRMWARE_FILE): firmware/$(FIRMWARE_FILE) tools ./scripts/enable_debug_misc.sh $< tmp/debug $@ -$(CUSTOM_FIRMWARE_FILE): firmware/$(FIRMWARE_FILE) tools - ./scripts/create_custom_firmware.sh $< tmp/custom $@ +$(BASIC_FIRMWARE_FILE): firmware/$(FIRMWARE_FILE) tools + ./scripts/create_firmware.sh $< tmp/basic $@ overlays/basic overlays/camera-native -custom_firmware: $(CUSTOM_FIRMWARE_FILE) +$(EXTENDED_FIRMWARE_FILE): firmware/$(FIRMWARE_FILE) tools + ./scripts/create_firmware.sh $< tmp/extended $@ overlays/basic overlays/camera-new + +basic_firmware: $(BASIC_FIRMWARE_FILE) +extended_firmware: $(EXTENDED_FIRMWARE_FILE) debug_firmware: $(DEBUG_FIRMWARE_FILE) extract_firmware: firmware/$(FIRMWARE_FILE) tools ./scripts/extract_squashfs.sh $< tmp/extracted diff --git a/README.md b/README.md index 2b025660..05a48fcc 100644 --- a/README.md +++ b/README.md @@ -8,22 +8,49 @@ This project provides utilities to work with Snapmaker U1 firmware images. It enables creating custom firmware builds, and enabling debug features, like SSH access. -## Features +## Firmware Variants + +| Variant | Description | +|---------|-------------| +| **Basic** | SSH, USB ethernet, native camera (~1Hz in Fluidd) | +| **Extended** | Basic + new camera stack with HW-accelerated streams | + +### Common Features -- Extract firmware images (SquashFS rootfs) -- Create custom firmware with modifications - Enable SSH: `root/snapmaker` and `lava/snapmaker` - Enable DHCP on USB ethernet adapters - Disable WiFi power saving -- Expose camera feed in Fluidd (~1Hz) + +### Extended Camera + +The extended firmware replaces the native camera with a hardware-accelerated +stack using the Rockchip MPP/VPU. + +Camera endpoints available at `http:///webcam/`: + +| Endpoint | Description | +|----------|-------------| +| `/webcam/snapshot.jpg` | Built-in: JPEG snapshot | +| `/webcam/stream.mjpg` | Built-in: MJPEG stream (~15fps) | +| `/webcam/stream.h264` | Built-in: H264 stream (raw) | +| `/webcam/player` | Built-in: H264 web player | +| `/webcam2/snapshot.jpg` | USB: JPEG snapshot | +| `/webcam2/stream.mjpg` | USB: MJPEG stream (~15fps) | +| `/webcam2/stream.h264` | USB: H264 stream (raw) | +| `/webcam2/player` | USB: H264 web player | + +Fluidd automatically picks up the webcam configuration. + +If USB camera is connected it will be exposed under `/webcam2/`. +Requires reboot. ## Pre-builts 1. Go to [Actions](https://github.com/paxx12/SnapmakerU1/actions/workflows/build.yaml). You need GitHub account. -1. Download latest artifact for latest build. +1. Download `basic-build` or `extended-build` artifact. 1. Unpack the `.zip` 1. Put the `.bin` file onto USB device (FAT32/exFAT format). -1. Go to `About > Firmware version > Local Update > Select firmware_custom.bin` +1. Go to `About > Firmware version > Local Update > Select the .bin file` 1. Connect using `ssh root@` with `snapmaker` password. **This will void your warranty, but you get SSH access.** diff --git a/custom/patches/01-enable-ssh.patch b/overlays/basic/patches/01-enable-ssh.patch similarity index 100% rename from custom/patches/01-enable-ssh.patch rename to overlays/basic/patches/01-enable-ssh.patch diff --git a/custom/patches/02-disable-wlan-power-save.patch b/overlays/basic/patches/02-disable-wlan-power-save.patch similarity index 100% rename from custom/patches/02-disable-wlan-power-save.patch rename to overlays/basic/patches/02-disable-wlan-power-save.patch diff --git a/custom/patches/05-enable-dhcp-eth0.patch b/overlays/basic/patches/05-enable-dhcp-eth0.patch similarity index 100% rename from custom/patches/05-enable-dhcp-eth0.patch rename to overlays/basic/patches/05-enable-dhcp-eth0.patch diff --git a/custom/root/etc/network/interfaces.d/eth0 b/overlays/basic/root/etc/network/interfaces.d/eth0 similarity index 100% rename from custom/root/etc/network/interfaces.d/eth0 rename to overlays/basic/root/etc/network/interfaces.d/eth0 diff --git a/custom/patches/03-enable-nginx-camera.patch b/overlays/camera-native/patches/03-enable-nginx-camera.patch similarity index 100% rename from custom/patches/03-enable-nginx-camera.patch rename to overlays/camera-native/patches/03-enable-nginx-camera.patch diff --git a/custom/patches/04-enable-moonraker-camera.patch b/overlays/camera-native/patches/04-enable-moonraker-camera.patch similarity index 100% rename from custom/patches/04-enable-moonraker-camera.patch rename to overlays/camera-native/patches/04-enable-moonraker-camera.patch diff --git a/custom/root/etc/init.d/S99fluidd_camera b/overlays/camera-native/root/etc/init.d/S99fluidd_camera similarity index 100% rename from custom/root/etc/init.d/S99fluidd_camera rename to overlays/camera-native/root/etc/init.d/S99fluidd_camera diff --git a/overlays/camera-new/patches/02-disable-native-camera.patch b/overlays/camera-new/patches/02-disable-native-camera.patch new file mode 100644 index 00000000..6158fc40 --- /dev/null +++ b/overlays/camera-new/patches/02-disable-native-camera.patch @@ -0,0 +1,11 @@ +--- a/etc/init.d/S99camera_service ++++ b/etc/init.d/S99camera_service +@@ -3,6 +3,8 @@ + # Start/stop camera_service process + # + ++[ -e /oem/.camera-native ] || exit 0 ++ + log() + { + logger -p user.info -t "camera[$$]" -- "$1" diff --git a/overlays/camera-new/patches/04-enable-moonraker-camera-firame.patch b/overlays/camera-new/patches/04-enable-moonraker-camera-firame.patch new file mode 100644 index 00000000..61030b98 --- /dev/null +++ b/overlays/camera-new/patches/04-enable-moonraker-camera-firame.patch @@ -0,0 +1,12 @@ +--- a/home/lava/origin_printer_data/config/moonraker.conf ++++ b/home/lava/origin_printer_data/config/moonraker.conf +@@ -46,3 +46,9 @@ + mqtt_protocol: v5 + status_interval: 2 + default_qos: 1 ++[timelapse] ++[webcam case] ++service: iframe ++stream_url: /webcam/player ++snapshot_url: /webcam/snapshot.jpg ++aspect_ratio: 16:9 diff --git a/overlays/camera-new/patches/04-enable-moonraker-camera-mjpg.patch.bak b/overlays/camera-new/patches/04-enable-moonraker-camera-mjpg.patch.bak new file mode 100644 index 00000000..e102346a --- /dev/null +++ b/overlays/camera-new/patches/04-enable-moonraker-camera-mjpg.patch.bak @@ -0,0 +1,14 @@ +--- a/home/lava/origin_printer_data/config/moonraker.conf ++++ b/home/lava/origin_printer_data/config/moonraker.conf +@@ -46,3 +46,11 @@ + mqtt_protocol: v5 + status_interval: 2 + default_qos: 1 ++[timelapse] ++[webcam case] ++service: mjpegstreamer-adaptive ++stream_url: /webcam/stream.mjpg ++snapshot_url: /webcam/snapshot.jpg ++target_fps: 15 ++target_fps_idle: 5 ++aspect_ratio: 16:9 diff --git a/overlays/camera-new/root/etc/init.d/S99v4l2-mpp-mipi b/overlays/camera-new/root/etc/init.d/S99v4l2-mpp-mipi new file mode 100755 index 00000000..8568b680 --- /dev/null +++ b/overlays/camera-new/root/etc/init.d/S99v4l2-mpp-mipi @@ -0,0 +1,65 @@ +#!/bin/sh + +[ ! -e /oem/.camera-native ] || exit 0 + +CMD_CAPTURE=/usr/local/bin/capture-mipi-mpp +CMD_CAPTURE_ARGS="--device /dev/video11 --format nv12 --jpeg-quality 7 --jpeg-sock /tmp/capture-mipi-jpeg.sock --mjpeg-sock /tmp/capture-mipi-mjpeg.sock --h264-sock /tmp/capture-mipi-h264.sock" + +CMD_HTTP=/usr/local/bin/stream-http.py +CMD_HTTP_ARGS="--jpeg-sock /tmp/capture-mipi-jpeg.sock --mjpeg-sock /tmp/capture-mipi-mjpeg.sock --h264-sock /tmp/capture-mipi-h264.sock --bind 127.0.0.1" + +CMD_MQTT=/usr/local/bin/stream-mqtt.py +CMD_MQTT_ARGS="--jpeg-sock /tmp/capture-mipi-jpeg.sock --publish-dir /home/lava/printer_data/camera" + +start() { + if [ ! -e /dev/video11 ]; then + echo "MIPI camera device /dev/video11 not found, not starting." + return + fi + + printf "Starting capture-mipi-mpp: " + start-stop-daemon -S -b -q -m -p /var/run/capture-mipi-mpp.pid -x $CMD_CAPTURE -- $CMD_CAPTURE_ARGS + echo "OK" + + printf "Starting stream-http: " + start-stop-daemon -S -b -q -m -p /var/run/stream-mipi-http.pid -c lava -x /usr/bin/python3 -- $CMD_HTTP $CMD_HTTP_ARGS + echo "OK" + + printf "Starting stream-mqtt: " + start-stop-daemon -S -b -q -m -p /var/run/stream-mipi-mqtt.pid -c lava -x /usr/bin/python3 -- $CMD_MQTT $CMD_MQTT_ARGS + echo "OK" +} + +stop() { + printf "Stopping stream-mqtt: " + start-stop-daemon -K -q -p /var/run/stream-mipi-mqtt.pid + echo "OK" + + printf "Stopping stream-http: " + start-stop-daemon -K -q -p /var/run/stream-mipi-http.pid + echo "OK" + + printf "Stopping capture-mipi-mpp: " + start-stop-daemon -K -q -p /var/run/capture-mipi-mpp.pid + echo "OK" +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart|reload) + stop + sleep 1 + start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 + ;; +esac + +exit 0 diff --git a/overlays/camera-new/root/etc/init.d/S99v4l2-mpp-usb b/overlays/camera-new/root/etc/init.d/S99v4l2-mpp-usb new file mode 100755 index 00000000..5d62cea7 --- /dev/null +++ b/overlays/camera-new/root/etc/init.d/S99v4l2-mpp-usb @@ -0,0 +1,62 @@ +#!/bin/sh + +[ ! -e /oem/.camera-native ] || exit 0 + +CMD_CAPTURE=/usr/local/bin/capture-usb-mpp +CMD_CAPTURE_ARGS="--device /dev/video18 --jpeg-sock /tmp/capture-usb-jpeg.sock --mjpeg-sock /tmp/capture-usb-mjpeg.sock --h264-sock /tmp/capture-usb-h264.sock" + +CMD_HTTP=/usr/local/bin/stream-http.py +CMD_HTTP_ARGS="--jpeg-sock /tmp/capture-usb-jpeg.sock --mjpeg-sock /tmp/capture-usb-mjpeg.sock --h264-sock /tmp/capture-usb-h264.sock --bind 127.0.0.1 --port 8081" + +start() { + if [ ! -e /dev/video18 ]; then + echo "USB camera device /dev/video18 not found, not starting." + return + fi + + printf "Starting capture-usb-mpp: " + start-stop-daemon -S -b -q -m -p /var/run/capture-usb-mpp.pid -x $CMD_CAPTURE -- $CMD_CAPTURE_ARGS + echo "OK" + + printf "Starting stream-http: " + start-stop-daemon -S -b -q -m -p /var/run/stream-usb-http.pid -c lava -x /usr/bin/python3 -- $CMD_HTTP $CMD_HTTP_ARGS + echo "OK" + + printf "Starting stream-mqtt: " + start-stop-daemon -S -b -q -m -p /var/run/stream-usb-mqtt.pid -c lava -x /usr/bin/python3 -- $CMD_MQTT $CMD_MQTT_ARGS + echo "OK" +} + +stop() { + printf "Stopping stream-mqtt: " + start-stop-daemon -K -q -p /var/run/stream-usb-mqtt.pid + echo "OK" + + printf "Stopping stream-http: " + start-stop-daemon -K -q -p /var/run/stream-usb-http.pid + echo "OK" + + printf "Stopping capture-usb-mpp: " + start-stop-daemon -K -q -p /var/run/capture-usb-mpp.pid + echo "OK" +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart|reload) + stop + sleep 1 + start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 + ;; +esac + +exit 0 diff --git a/overlays/camera-new/root/home/lava/moonraker/moonraker/components/timelapse.py b/overlays/camera-new/root/home/lava/moonraker/moonraker/components/timelapse.py new file mode 100644 index 00000000..5df0ba04 --- /dev/null +++ b/overlays/camera-new/root/home/lava/moonraker/moonraker/components/timelapse.py @@ -0,0 +1,22 @@ +from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from confighelper import ConfigHelper + +class Timelapse: + + def __init__(self, confighelper: ConfigHelper) -> None: + self.confighelper = confighelper + self.server = confighelper.get_server() + + # setup eventhandlers and endpoints + file_manager = self.server.lookup_component("file_manager") + camera_path = file_manager.datapath.joinpath("camera") + file_manager.register_directory("timelapse", + str(camera_path), + full_access=True + ) + +def load_component(config: ConfigHelper) -> Timelapse: + return Timelapse(config) diff --git a/overlays/camera-new/scripts/01-v4l2-mpp.sh b/overlays/camera-new/scripts/01-v4l2-mpp.sh new file mode 100755 index 00000000..fff07826 --- /dev/null +++ b/overlays/camera-new/scripts/01-v4l2-mpp.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +ROOT_DIR="$(realpath "$(dirname "$0")/../../..")" + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 " + exit 1 +fi + +set -eo pipefail + +TARGET_DIR="$ROOT_DIR/tmp/v4l2-mpp" + +if [[ ! -d "$TARGET_DIR" ]]; then + git clone https://github.com/paxx12/v4l2-mpp.git "$TARGET_DIR" +fi + +echo ">> Compiling MPP library..." +"$TARGET_DIR/deps/compile_mpp.sh" + +echo ">> Compiling v4l2-mpp applications..." +make -C "$TARGET_DIR" install DESTDIR="$1" diff --git a/scripts/create_custom_firmware.sh b/scripts/create_firmware.sh similarity index 60% rename from scripts/create_custom_firmware.sh rename to scripts/create_firmware.sh index c8976226..2f7eb62f 100755 --- a/scripts/create_custom_firmware.sh +++ b/scripts/create_firmware.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -if [[ $# -ne 3 ]]; then - echo "Usage: $0 " +if [[ $# -lt 3 ]]; then + echo "Usage: $0 [overlays...]" exit 1 fi @@ -11,6 +11,7 @@ IN_FIRMWARE="$(realpath "$1")" TEMP_DIR="$(realpath -m "$2")" OUT_FIRMWARE="$(realpath -m "$3")" ROOT_DIR="$(realpath "$(dirname "$0")/..")" +shift 3 rm -rf "$TEMP_DIR" @@ -23,17 +24,28 @@ unsquashfs -d "$TEMP_DIR/rootfs" "$TEMP_DIR/rk-unpacked/rootfs.img" echo ">> Add kernel modules to rootfs..." "$ROOT_DIR/scripts/helpers/compile_kernel_modules.sh" "$TEMP_DIR/rootfs/info/config-6.1" "$ROOT_DIR/tmp/kernel" "$TEMP_DIR/rootfs/lib/modules/" -echo ">> Applying custom patches..." -for patchfile in "$ROOT_DIR/custom/patches/"*.patch; do - echo "[+] Applying patch: $(basename "$patchfile")" - patch -d "$TEMP_DIR/rootfs" -p1 < "$patchfile" +for overlay; do + echo ">> Applying overlay $overlay..." + if [[ -d "$overlay/patches/" ]]; then + for patchfile in "$overlay/patches/"*.patch; do + echo "[+] Applying patch: $(basename "$patchfile")" + patch -d "$TEMP_DIR/rootfs" -p1 < "$patchfile" + done + fi + + if [[ -d "$overlay/scripts/" ]]; then + for scriptfile in "$overlay/scripts/"*.sh; do + echo "[+] Running script: $(basename "$scriptfile")" + ./"$scriptfile" "$TEMP_DIR/rootfs" + done + fi + + if [[ -d "$overlay/root/" ]]; then + echo ">> Copying custom files..." + cp -rv "$overlay/root/." "$TEMP_DIR/rootfs/" + fi done -if [[ -d "$ROOT_DIR/custom/root/" ]]; then - echo ">> Copying custom files..." - cp -rv "$ROOT_DIR/custom/root/." "$TEMP_DIR/rootfs/" -fi - echo ">> Create squash filesystem..." mksquashfs "$TEMP_DIR/rootfs" "$TEMP_DIR/rk-unpacked/rootfs-v2.img" -comp gzip diff --git a/scripts/dev/save-patch.sh b/scripts/dev/save-patch.sh index e304c9e9..fd8b50fa 100755 --- a/scripts/dev/save-patch.sh +++ b/scripts/dev/save-patch.sh @@ -1,8 +1,8 @@ #!/bin/bash -if [[ $# -ne 2 ]]; then - echo "Usage: $0 " - echo "Example: $0 02-disable-wlan-power-save /path/to/02-disable-wlan-power-save.patch" +if [[ $# -ne 3 ]]; then + echo "Usage: $0 " + echo "Example: $0 camera-native 02-disable-wlan-power-save /path/to/02-disable-wlan-power-save.patch" exit 1 fi @@ -12,12 +12,15 @@ if [[ ! -d tmp/extracted/rootfs.original ]]; then unsquashfs -d tmp/extracted/rootfs.original tmp/extracted/rk-unpacked/rootfs.img fi -PATCH_NAME="$1" -shift +OVERLAY_NAME="$1" +PATCH_NAME="$2" +shift 2 + +mkdir -p "overlays/$OVERLAY_NAME/patches" for patch_file; do diff \ --label "a/$patch_file" \ --label "b/$patch_file" \ -uNr tmp/extracted/{rootfs.original,rootfs}/"$patch_file" -done > "custom/patches/$PATCH_NAME.patch" +done > "overlays/$OVERLAY_NAME/patches/$PATCH_NAME.patch" diff --git a/scripts/dev/upgrade-firmware.sh b/scripts/dev/upgrade-firmware.sh index 6a702044..7b0a61cd 100755 --- a/scripts/dev/upgrade-firmware.sh +++ b/scripts/dev/upgrade-firmware.sh @@ -1,13 +1,13 @@ #!/bin/bash -if [[ $# -ne 1 ]]; then - echo "usage: $0 " +if [[ $# -ne 2 ]]; then + echo "usage: $0 " exit 1 fi set -xe -rm -f firmware/firmware_custom.bin -make custom_firmware -scp tmp/custom/update.img "$1:/tmp/" +rm -f "firmware/firmware_$2.bin" +make "${2}_firmware" +scp "tmp/$2/update.img" "$1:/tmp/" ssh "$1" /home/lava/bin/systemUpgrade.sh upgrade soc /tmp/update.img