ROS never leaves the robot, by the owner's decision: the firewall drops domain 42's DDS ports on every interface but loopback (usb0 included), discovery is pinned to LOCALHOST, and hermes_mcp is the only bridge to the outside. PipeWire 1.0.5 runs as hermes's user services with its network server on TCP 4713; the firewall, loaded at boot before it, is the protection. Verified from talos: 4713 connects over usb0 and times out over Wi-Fi. CONTEXT.md brought up to date: the face service, gaze, recognition, the firewall, and what is left. 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