To keep myself accountable, I thought it best to blog my progress with the Raspberry Pi Compute Module 5 Development Kit before doing a full RoadTest review. My roadtest plan was to build a remote-control dog feeder that, with a bit of Machine Learning, could make the dogs sit and wait for their food. But to get started, it was a question of unboxing and getting some DX (Developer Experience) up and running.
What’s in the box

The kit is a CM5 Wireless (4GB RAM / 32GB eMMC), the CM5 IO Board pre-fitted inside the IO Case, the official cooler, an antenna kit, a 27W USB-C PD power supply, two HDMI-to-HDMI cables, and a USB-A to USB-C cable.

Packaging and build quality are genuinely nice — everything sits in its own tray, cables included, a pleasure to actually unpack. A few things were missing that would’ve smoothed the first hour out, though:
- No Quick start guide something short and succinct to get you up and running without having to read all the docs and second guess if you understood them correctly
- No spare jumper for the boot-mode header. Forcing USB boot mode needs a small 2-pin shorting jumper on J2, and the kit doesn’t include a spare one. Had to raid an old parts bin for it — worth having a couple of 2.54mm jumpers on hand before you start.
- No CR2032 RTC battery. Probably OK and I had one lying around anyway.
Working out how it goes together
The kit ships with a stack of datasheets rather than one guide, and it wasn’t immediately obvious which document actually covers the physical assembly — seating the CM5 onto the IO Board and closing up the IO Case. Once I found the right pictures there really was only one way it could go together, which helped, but a single quick-start card in the box pointing at “open here for assembly” would have saved some flipping between PDFs.
Actually getting the board seated and screwed into the case was fiddlier than expected in practice: four screws, four spacers, and making sure the cooler and its fan cable landed correctly along the way, all in a fairly tight fit. Nothing went wrong, it just took more care than I expected for what’s ultimately a four-screw job.

First boot: no surprises, no OS either
Before touching anything else, I hooked it up to a monitor just to see what happened. No surprise, really — the bootloader was upfront that there was no bootable image on the eMMC, and dropped straight to looking for a network boot server instead. Presumably that’s a real option if you’ve got a PXE-style network-boot server sitting on the LAN already, but that’s not something I have set up, so: on to imaging the eMMC directly instead.

Flashing the eMMC
The process is: fit the boot-mode jumper, connect USB-C from the board’s slave port to a computer, then use that computer to write an OS onto the eMMC over USB rather than booting from it.

Raspberry Pi Imager was the obvious first tool, but on its own it couldn’t find the board at all — no drive showed up anywhere for it to target. Downloading the separate rpiboot utility and running that directly fixed it immediately: it found the board straight away and confirmed the eMMC as 32GB. From there, Imager’s normal prompts took over — creating a user account, enabling SSH, and setting a hostname. In keeping with the household convention (Pollyanna and Athena are already on the network), this one’s called Quintessa.
brew install rpiboot
sudo rpiboot -d "$(brew --prefix rpiboot)"/share/rpiboot/mass-storage-gadget64
...
Sending bootcode.bin
Successful read 4 bytes
Waiting for BCM2835/6/7/2711/2712...
Second stage boot server
File read: mcb.bin
File read: memsys00.bin
File read: memsys01.bin
File read: memsys02.bin
File read: memsys03.bin
File read: bootmain
...
Second stage boot server done

CPU heat and fan test
With SSH up, first real test was seeing how the CM5’s four Cortex-A76 cores (the same BCM2712 silicon as Pi 5) handle sustained load, and whether the official cooler’s fan ever needs to kick in.
Baseline, idle:
vcgencmd measure_temp vcgencmd get_throttled vcgencmd measure_clock arm
temp=32.3'C throttled=0x0 frequency(0)=1900016256
A normalised CPU speed number, for comparison against other boards:
sudo apt-get update sudo apt-get install -y stress-ng sysbench sysbench cpu --cpu-max-prime=20000 --threads=4 run
General statistics:
total time: 10.0009s
total number of events: 40567
Latency (ms):
min: 0.98
avg: 0.99
max: 1.51
95th percentile: 0.99
sum: 39995.46
Threads fairness:
events (avg/stddev): 10141.7500/14.43
execution time (avg/stddev): 9.9989/0.00
~4,057 events/sec across 4 threads.
Then a 60-second all-core stress test, sampling temperature and clock speed every 5 seconds:
stress-ng --cpu 4 --timeout 60s --metrics-brief > /tmp/stress_result.txt 2>&1 &
STRESS_PID=$!
for i in $(seq 1 12); do
sleep 5
t=$(vcgencmd measure_temp)
c=$(vcgencmd measure_clock arm | cut -d= -f2)
echo "t=+$((i*5))s $t clock=${c}Hz"
done
wait $STRESS_PID
vcgencmd measure_temp
vcgencmd get_throttled
cat /tmp/stress_result.txt
t=+5s temp=38.9'C clock=2400033792Hz t=+10s temp=41.7'C clock=2400023808Hz t=+15s temp=41.7'C clock=2400027136Hz t=+20s temp=42.2'C clock=2400020480Hz t=+25s temp=43.9'C clock=2400020480Hz t=+30s temp=42.8'C clock=2400030464Hz t=+35s temp=43.3'C clock=2400030464Hz t=+40s temp=44.4'C clock=2400033792Hz t=+45s temp=45.0'C clock=2400030464Hz t=+50s temp=45.5'C clock=2400030464Hz t=+55s temp=45.0'C clock=2400037120Hz t=+60s temp=41.7'C clock=2400027136Hz === after === temp=43.9'C throttled=0x0 === stress-ng result === stress-ng: metrc: [2259] stressor bogo ops real time usr time sys time bogo ops/s bogo ops/s stress-ng: metrc: [2259] (secs) (secs) (secs) (real time) (usr+sys time) stress-ng: metrc: [2259] cpu 54542 60.02 239.83 0.02 908.78 227.41
All four cores boosted to 2.4GHz immediately and held it for the whole run, temperature climbed from 35.6°C to a peak of 45.5°C, and throttled stayed 0x0 the entire time — no throttling at all.
Checked whether the fan actually spun up for any of this, via the board’s PWM fan sensor:
cat /sys/class/hwmon/hwmon2/name cat /sys/class/hwmon/hwmon2/fan1_input
pwmfan 0
0 RPM for that first run — 45°C never got close to the fan’s trip point, so the passive cooler alone handled a single 60-second burst with headroom to spare.
Ran it a few more times back-to-back right after, though, and the heat had nowhere to go between runs — by a later pass, t=+60s was reading 52.7°C, clock still pinned at 2.4GHz, and this time the fan was actually on:
cat /sys/class/hwmon/hwmon2/fan1_input
1481
1,481 RPM. So the trip point sits somewhere between 45.5°C and 52.7°C — one clean 60-second run isn’t enough to reach it, but a couple of them back-to-back is. Good to see the fan does work.
The VNC rabbit hole
Raspberry Pi OS here ships both realvnc-vnc-server and wayvnc (the desktop runs on labwc, a Wayland compositor, not X11) — raspi-config picks the right one automatically, so turning it on is one command:
sudo raspi-config nonint do_vnc 0 sudo reboot
(Interactive equivalent: sudo raspi-config → Interface Options → VNC → Yes.)
First attempt at connecting, with macOS’s built-in Screen Sharing, just silently failed:
open vnc://quintessa.local
Turned out this was never going to work. A raw RFB handshake against port 5900 shows why:
python3 -c "
import socket
s = socket.create_connection(('quintessa.local', 5900), timeout=4)
banner = s.recv(64)
s.sendall(banner)
n = s.recv(1)[0]
print('security types offered:', list(s.recv(n)))
"
security types offered: [19, 129, 5]
19 is VeNCrypt — wayvnc needs a client that actually speaks it. macOS Screen Sharing only understands classic VNC password auth and Apple’s own ARD scheme, neither of which is in that list, so it just hangs with no useful error.
Next tried RealVNC Connect:
brew install --cask realvnc-connect
This immediately prompted for a sign-in/trial — turns out that’s just it pushing its own cloud-relay service. You can skip that entirely and type an address straight into its search bar for a direct LAN connection, no account needed — but in the end went with a plainer option instead and removed it:
brew uninstall --cask --zap realvnc-connect
brew install --cask tigervnc-viewer
TigerVNC connected fine — it speaks VeNCrypt natively. But the redraw was painfully slow. Chased that down and found three compounding causes, none of them TigerVNC’s fault:
-
Two separate
wayvncprocesses were running at once. Alongside the oneraspi-configenabled, Raspberry Pi Connect — Raspberry Pi’s own cloud remote-access tool — was already active and signed in from first-boot setup, running its ownwayvncinstance (rpi-connect-wayvnc.service), both capturing the same compositor output at once:systemctl status wayvnc.service systemctl --user status rpi-connect-wayvnc.service rpi-connect status
Signed in: yes Subscribed to events: yes Screen sharing: allowed (0 sessions active) Remote shell: allowed (0 sessions active)
- A real monitor was attached, so it wasn’t compositing some small headless framebuffer — it was encoding a full 1920×1080 desktop every frame:
wlr-randr
HDMI-A-1 "Dell Inc. DELL U2312HM FX01R27KCU1L (HDMI-A-1)" Modes: 1920x1080 px, 60.000000 Hz (preferred, current) - It’s on Wi-Fi, not Ethernet, and already showing real retry traffic:
cat /proc/net/wireless
wlan0: 0000 49. -61. -256 0 0 0 881 0 0
(that’s
881retries — thelink levelcolumn reading -61dBm.)
In the end I decided I will be happy enough with SSH and to turn VNC off.
rpi-connect off sudo systemctl disable --now wayvnc.service
Going console-only
None of that is really TigerVNC’s problem to fix — for a board that’s meant to run headless as a camera/AI appliance, not something anyone sits in front of, running a full desktop plus two competing VNC captures 24/7 is just overhead for a GUI that’s rarely needed. Switched the boot target to console-only instead of chasing VNC settings further:
sudo raspi-config nonint do_boot_behaviour B1 sudo reboot
Confirmed after reboot:
systemctl get-default
multi-user.target
| Desktop, VNC off | Console-only, VNC off | |
|---|---|---|
free -h used |
449Mi | 286Mi |
top used |
431.8Mi | 270.6Mi |
labwc, pcmanfm (desktop icons), wf-panel, and the two xdg-desktop-portal processes are the difference — all present at 449Mi, all absent at 286Mi. ~163Mi, consistently, across both measurement tools — that’s the actual cost of the desktop GUI on this board, isolated from the VNC question entirely.
Switching back to desktop, if ever needed
sudo raspi-config nonint do_boot_behaviour B4 # desktop, autologin sudo reboot
(B3 for desktop with a login prompt instead of autologin, B2 for console with autologin — B1 is what we’re running now.)
Up Next
- I might do a few more tests to confirm all IO board ports work: USB 3.0, Gigabit Ethernet, maybe GPIO headers.
- Wi-Fi association check, dual-band, note RSSI and link rate
- Connect some cammeras and check them for steaming
- A CPU-only YOLOv8s inference baseline over a real camera feed
-
I have also started to train the dogs to respond to the speakers via a sound board, inspired by hambreros and tamadillo's Hall-w-EV robot voice

- and much more …