#!/bin/bash -e # CM5 fixes baked into the image, kept outside the Python package so daemon updates don't wipe them. # Target: a CM5 swapped into the Reachy Mini Wireless carrier (issue geerlingguy/raspberry-pi-pcie-devices#800). # Same BCM2712+RP1 silicon as the Pi 5; the camera path is where the two variants differ: # the Wireless camera is CSI (imx708) and NEEDS libcamera, so unlike stage-pi5-fixes.sh # nothing is disabled here, and the WebRTC encoder goes software (no HW encoder on BCM2712). echo "Fix 1/3: motor port -> /dev/ttyAMA3 (udev)" # the carrier's motor bus is on GPIO4/5: RP1 uart2 on the CM5 (ttyAMA2), not the CM4's uart3 install -d -m 0755 /etc/udev/rules.d cat > /etc/udev/rules.d/99-reachy-cm5-motor.rules <<'RULE' # /dev/ttyAMA3 = whichever motor bus exists (USB controller, or GPIO uart2) SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d3", SYMLINK+="ttyAMA3", GROUP="dialout", MODE="0660" KERNEL=="ttyAMA2", SYMLINK+="ttyAMA3", GROUP="dialout", MODE="0660" RULE echo "Fix 2/3: shutdown button -> gpiozero lgpio backend" install -d -m 0755 /etc/systemd/system/gpio-shutdown-daemon.service.d cat > /etc/systemd/system/gpio-shutdown-daemon.service.d/10-cm5-lgpio.conf <<'DROP' [Service] Environment=GPIOZERO_PIN_FACTORY=lgpio DROP source /opt/uv/env 2>/dev/null || true if [ -f /venvs/mini_daemon/bin/activate ]; then source /venvs/mini_daemon/bin/activate uv pip install lgpio || pip install lgpio || true fi echo "Fix 3/3: software H264 encoder for the WebRTC stream" # the daemon's CSI path hardcodes v4l2h264enc, a hardware element the CM5 does not have: # swap that one method for an x264enc version at import time cat > /venvs/mini_daemon/lib/python3.12/site-packages/sitecustomize.py <<'PY' # ReachyPi CM5: no hardware H264 encoder on BCM2712 -> encode the WebRTC branch in software def _sw_encoder(): try: from reachy_mini.media import media_server as ms from gi.repository import Gst except Exception: return def sw_branch(self, queue_webrtc, pipeline, webrtcsink): convert = Gst.ElementFactory.make("videoconvert") x264 = Gst.ElementFactory.make("x264enc") caps_h264 = Gst.ElementFactory.make("capsfilter") if not all([convert, x264, caps_h264]): raise RuntimeError("Failed to create x264 encoder elements") Gst.util_set_object_arg(x264, "tune", "zerolatency") Gst.util_set_object_arg(x264, "speed-preset", "ultrafast") x264.set_property("bitrate", 5000) # kbit/s, same 5 Mb/s as the hardware branch x264.set_property("key-int-max", 60) # same keyframe period caps_h264.set_property("caps", Gst.Caps.from_string( "video/x-h264,stream-format=byte-stream,alignment=au," "level=(string)3.1,profile=(string)constrained-baseline")) for elem in (convert, x264, caps_h264): pipeline.add(elem) queue_webrtc.link(convert) convert.link(x264) x264.link(caps_h264) caps_h264.link(webrtcsink) ms.GstMediaServer._build_rpi_encoder_branch = sw_branch _sw_encoder() PY echo "CM5 fixes applied."