Stock libcamera 0.2.0 (Ubuntu) and ROS Jazzy's 0.7.2 both detect the OV5647 but crash in the Pi IPA when capturing, for every stream format and size. The Raspberry Pi fork, built into /opt/libcamera-rpi, captures 1296x972 frames. capture_still.py takes a still with it, and detect_faces.py runs OpenCV YuNet (Haar fallback) on images. YuNet needs OpenCV 4.8+ and input sides in multiples of 32, so the venv carries opencv-python-headless 4.10 pinned with numpy<2. camera_ros still has to be built against the fork. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AFN8nRRCiDiMN7ChBpHJhs
Hermes robot
A small Wi-Fi robot that gives Hermes Agent eyes, ears, a voice and (optionally) a neck. All thinking happens on your Omarchy machine. The robot only takes pictures, moves its head, and carries audio.
Omarchy machine Robot (Pi Zero 2 W)
─────────────── ───────────────────
hermes-robot ─► Hermes Agent
│ voice mode / wake word
├── default mic = "Robot microphone" ◄─┐
├── default sink = "Robot speaker" ─────┤ PipeWire pulse tunnel, TCP 4713
│ └─► pipewire-pulse ─► I²S mic(s) + amp
└── MCP tools: look(), turn_head() ──────► robot_mcp_server.py, HTTP 8765
├─ Camera Module 3
└─ PCA9685 ─► 2 servos (optional)
Parts
Prices are rough single-unit figures and will vary by vendor.
| Part | Notes | ≈ USD |
|---|---|---|
| Raspberry Pi Zero 2 W (the "WH" has headers soldered) | 2.4 GHz Wi-Fi only | 15–18 |
| microSD card, 16–32 GB | 8 | |
| Raspberry Pi Camera Module 3 | Autofocus; the "Wide" variant suits a desk robot | 25 |
| Pi Zero camera cable (22-pin to 15-pin) | The camera ships with the wrong cable for a Zero | 3 |
| INMP441 I²S MEMS microphone breakout, ×2 | One works; two give a stereo pair and a better signal | 3 each |
| MAX98357A I²S amplifier breakout | 2–6 | |
| Speaker, 4–8 Ω, 2–3 W, ~40 mm | 3 | |
| 5 V 3 A USB power supply | 10 | |
| Optional head: PCA9685 16-channel servo board | Clean hardware PWM over I²C | 6 |
| Optional head: 2× SG90 servos + pan/tilt bracket | 8 | |
| Optional head: 1000 µF electrolytic capacitor | Across the servo supply, against brown-outs | 1 |
| Dupont jumper wires, a bit of perfboard | 5 |
Without the head, the robot is roughly $70; with it, about $85.
Wiring
Pin numbers are physical header pins (1–40), not GPIO numbers. The microphone(s) and amplifier share the I²S clock lines; that's expected.
Microphones (INMP441)
| INMP441 | Pi pin | |
|---|---|---|
| VDD | 1 (3.3 V) | 3.3 V only, never 5 V |
| GND | 6 (GND) | |
| SCK | 12 (GPIO18, I²S BCLK) | |
| WS | 35 (GPIO19, I²S LRCLK) | |
| SD | 38 (GPIO20, I²S DIN) | |
| L/R | GND for mic A; 3.3 V for mic B | Selects left or right channel |
With two microphones, wire both identically except L/R, so their SD lines share pin 38.
Amplifier (MAX98357A)
| MAX98357A | Pi pin | |
|---|---|---|
| Vin | 2 (5 V) | |
| GND | 9 (GND) | |
| BCLK | 12 (GPIO18) | Shared with the mics |
| LRC | 35 (GPIO19) | Shared with the mics |
| DIN | 40 (GPIO21, I²S DOUT) | |
| SD, GAIN | leave unconnected | Defaults: enabled, 9 dB gain |
| Speaker + / − | to the speaker |
Optional head (PCA9685)
| PCA9685 | Connects to | |
|---|---|---|
| VCC | Pi pin 17 (3.3 V) | Logic supply |
| GND | Pi pin 14 (GND) | |
| SDA | Pi pin 3 (GPIO2) | |
| SCL | Pi pin 5 (GPIO3) | |
| V+ terminal | 5 V from the power supply, not from the Pi's header | Servos' power; add the capacitor here |
| Channel 0 | pan servo | |
| Channel 1 | tilt servo |
Servos draw current spikes that will brown out the Pi if they share its header pins. Feed the supply's 5 V to both the Pi (micro-USB) and the PCA9685's V+ terminal, with a common ground.
Camera: the Zero cable's narrow end goes in the Pi, contacts facing the board.
The googlevoicehat-soundcard overlay is the standard trick for this exact mic + amp pairing.
Google's AIY Voice HAT used the same chips, so its driver drives both in one full-duplex sound card.
Setting up the Pi
-
Flash Raspberry Pi OS Lite (64-bit) with Raspberry Pi Imager. In its settings, set the hostname (e.g.
robot), your user, your Wi-Fi, and enable SSH. -
Boot, find the Pi's IP, and give it a DHCP reservation on your router.
-
Copy the
pi/folder over and run the installer, passing your Omarchy machine's IP:scp -r pi robot.local:~/robot-setup ssh robot.local cd ~/robot-setup && chmod +x install.sh ./install.sh 192.168.1.20 # add --servos if you built the head sudo rebootThe installer prints a token at the end. Keep it for the Hermes step.
-
After the reboot, check things on the Pi:
arecord -l # a "googlevoicehat" card should be listed wpctl status # its source and sink should appear under Audio rpicam-still -o test.jpg # camera works systemctl status robot-mcp # MCP server running -
If the camera is mounted upside down, set both
ROBOT_CAM_HFLIP=1andROBOT_CAM_VFLIP=1in/etc/robot-mcp.env, then runsudo systemctl restart robot-mcp.
Setting up Omarchy
-
Audio tunnel. Copy
omarchy/50-robot-tunnel.confto~/.config/pipewire/pipewire.conf.d/, replace bothROBOT_IPs, then:systemctl --user restart pipewire pipewire-pulse wireplumber pactl list short sources | grep robot_mic # should show the robotOmarchy's audio mixer (
wiremix) will now list "Robot speaker" and "Robot microphone". Ifpactlis missing, install it withsudo pacman -S libpulse. -
Hermes. Add
ROBOT_MCP_TOKEN=<token>to~/.hermes/.env. Mergeomarchy/hermes-config-snippet.yamlinto~/.hermes/config.yaml, withROBOT_IPreplaced. Then check the connection:hermes mcp test robotThe tools will appear in Hermes as
mcp_robot_look,mcp_robot_turn_headand so on. -
Launcher. Install the wrapper, which switches your default audio to the robot for the length of the session and restores it afterwards:
install -Dm755 omarchy/hermes-robot ~/.local/bin/hermes-robot hermes-robotInside Hermes,
/voice onstarts the conversation. Set up the wake word as described in the Hermes docs ("Wake Word") so you never need the keyboard.
First test
Say or type: "Look around and tell me what's on my desk." Hermes should call look, then
describe the scene.
One thing to confirm early: that the image actually reaches the model. The server returns a
standard MCP image block, and the test client sees it correctly. If Hermes answers as though it
saw nothing, check ~/.hermes/logs/agent.log, and make sure your model supports vision. If
Hermes turns out to drop image blocks from MCP results, the fallback is a small change to
look() to save the JPEG and return a path for Hermes's own vision tool. Tell me if that happens.
Tuning and troubleshooting
- Mic too quiet (the level bar barely moves): on the Pi, run
wpctl set-volume @DEFAULT_AUDIO_SOURCE@ 2.5. Alternatively, lowervoice.silence_thresholdin Hermes. A single mic is about 6 dB quieter than a pair, because the unused channel is averaged in. - Hermes interrupts itself: this is the robot hearing its own speaker. Raise
voice.barge_in_threshold_multiplier, point the speaker away from the mics, or enablepi/pipewire/60-echo-cancel.conf.optionaland uncomment thepulse.devicelines in the tunnel config. - Audio crackles: this means weak Wi-Fi. Raise
pulse.latencyto 300–400. The Zero 2 W has only 2.4 GHz Wi-Fi and a small antenna, so keep it within reasonable range of the router. - Robot devices vanish after the Pi reboots: the tunnels reconnect every 5 s by themselves.
Start
hermes-robotagain once they reappear. - Head turns the wrong way: set
ROBOT_PAN_INVERT=1orROBOT_TILT_INVERT=1in/etc/robot-mcp.env, then restartrobot-mcp.
Security
- The microphone stream (port 4713) and the camera server (port 8765) are open only to your Omarchy machine's IP, via nftables on the Pi.
- The camera additionally requires the bearer token.
- Traffic is unencrypted on your LAN. If that matters to you, put both machines on Tailscale and use the Pi's tailnet address in both configs.
- If your Omarchy machine's IP changes, re-run
install.shwith the new one.
Files
pi/
install.sh one-shot Pi setup
robot_mcp_server.py MCP server: look, turn_head, head_position
requirements.txt MCP Python SDK 2.x
robot-mcp.service systemd unit
nftables-robot.nft port restriction
pipewire/50-network-server.conf exposes audio to the network
pipewire/60-echo-cancel.conf.optional
omarchy/
50-robot-tunnel.conf robot audio as local devices
hermes-config-snippet.yaml MCP and voice settings for Hermes
hermes-robot launcher that swaps default audio devices