feat(hardware): add RPi GPIO, Aardvark I2C/SPI/GPIO, and hardware plugin system#3517
Open
Extreammouse wants to merge 6 commits intomasterfrom
Open
feat(hardware): add RPi GPIO, Aardvark I2C/SPI/GPIO, and hardware plugin system#3517Extreammouse wants to merge 6 commits intomasterfrom
Extreammouse wants to merge 6 commits intomasterfrom
Conversation
…gin system Extends the hardware subsystem with three clusters of functionality, all feature-gated (hardware / peripheral-rpi) with no impact on default builds. Raspberry Pi native support: - src/hardware/rpi.rs: board self-discovery (model, serial, revision), sysfs GPIO pin read/write, and ACT LED control - scripts/99-act-led.rules: udev rule for non-root ACT LED access - scripts/deploy-rpi.sh, scripts/rpi-config.toml, scripts/zeroclaw.service: one-shot deployment helper and systemd service template Total Phase Aardvark USB adapter (I2C / SPI / GPIO): - crates/aardvark-sys/: new workspace crate with FFI bindings loaded at runtime via libloading; graceful stub fallback when .so is absent or arch mismatches (Rosetta 2 detection) - src/hardware/aardvark.rs: AardvarkTransport implementing Transport trait - src/hardware/aardvark_tools.rs: agent tools i2c_scan, i2c_read, i2c_write, spi_transfer, gpio_aardvark - src/hardware/datasheet.rs: datasheet search/download for detected devices - docs/aardvark-integration.md, examples/hardware/aardvark/: guide + examples Hardware plugin / ToolRegistry system: - src/hardware/tool_registry.rs: ToolRegistry for hardware module tool sets - src/hardware/loader.rs, src/hardware/manifest.rs: manifest-driven loader - src/hardware/subprocess.rs: subprocess execution helper for board I/O - src/gateway/hardware_context.rs: POST /api/hardware/reload endpoint - src/hardware/mod.rs: exports all new modules; merge_hardware_tools and load_hardware_context_prompt helpers Integration hooks (minimal surface): - src/hardware/device.rs: DeviceKind::Aardvark, DeviceRuntime::Aardvark, has_aardvark / resolve_aardvark_device on DeviceRegistry - src/hardware/transport.rs: TransportKind::Aardvark - src/peripherals/mod.rs: gate create_board_info_tools behind hardware feature - src/agent/loop_.rs: TOOL_CHOICE_OVERRIDE task-local for Anthropic provider - src/providers/anthropic.rs: read TOOL_CHOICE_OVERRIDE; add tool_choice field - Cargo.toml: add aardvark-sys to workspace and as dependency - firmware/zeroclaw-nucleo/: update Cargo.toml and Cargo.lock Non-goals: - No changes to agent orchestration, channels, providers, or security policy - No new config keys outside existing [hardware] / [peripherals] sections - No CI workflow changes Risk: Low. All new paths are feature-gated; aardvark.so loads at runtime only when present. No schema migrations or persistent state introduced. Rollback: revert this single commit.
There was a problem hiding this comment.
Pull request overview
Adds a feature-gated hardware subsystem expansion (RPi + Total Phase Aardvark) and a manifest-driven hardware tool/plugin registry, with provider-side support for Anthropic tool_choice.
Changes:
- Introduces a hardware
Transport+ZcCommand/ZcResponseprotocol layer and aToolRegistrythat loads built-ins and user plugins. - Adds Raspberry Pi self-discovery + native GPIO/ACT LED tools, plus deployment/systemd/udev helpers.
- Adds Aardvark runtime-loaded FFI crate (
aardvark-sys), anAardvarkTransport, and Aardvark-backed tools (I2C/SPI/GPIO + datasheet management).
Reviewed changes
Copilot reviewed 31 out of 35 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/providers/anthropic.rs | Adds optional tool_choice emission based on a task-local override. |
| src/agent/loop_.rs | Introduces task-local TOOL_CHOICE_OVERRIDE. |
| src/peripherals/mod.rs | Adds create_board_info_tools behind feature gating. |
| src/hardware/transport.rs | Adds transport trait/errors/kinds (incl. Aardvark). |
| src/hardware/protocol.rs | Defines the JSON line protocol structs used across transports. |
| src/hardware/gpio.rs | Adds built-in GPIO tools backed by the transport/protocol. |
| src/hardware/tool_registry.rs | Adds registry that loads built-ins + plugin tools and dispatches tool calls. |
| src/hardware/loader.rs | Scans ~/.zeroclaw/tools/*/tool.toml and validates plugin binaries. |
| src/hardware/manifest.rs | Defines the plugin manifest TOML schema. |
| src/hardware/subprocess.rs | Adds subprocess-backed tool execution with timeouts and parsing. |
| src/hardware/aardvark.rs | Adds AardvarkTransport implementation. |
| src/hardware/aardvark_tools.rs | Adds Aardvark I2C/SPI/GPIO tools. |
| src/hardware/datasheet.rs | Adds datasheet search/download/list/read tool. |
| src/hardware/rpi.rs | Adds Raspberry Pi detection + GPIO/LED/system-info tools. |
| src/hardware/mod.rs | Wires new modules and adds hardware boot/context-prompt helpers + tests. |
| src/gateway/hardware_context.rs | Adds gateway endpoints for writing/reading hardware context and reload status. |
| crates/aardvark-sys/* | Adds runtime-loaded Aardvark C-library wrapper + vendored header. |
| Cargo.toml / Cargo.lock | Adds aardvark-sys to the workspace/deps. |
| scripts/* | Adds RPi deployment script, sample config, systemd unit, and udev LED rule. |
| firmware/nucleo/* and firmware/zeroclaw-nucleo/.cargo/config.toml | Updates Nucleo firmware config/deps/lockfile. |
| docs/aardvark-integration.md / examples/hardware/aardvark/* | Adds integration documentation and example hardware context/skills. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Comment on lines
+236
to
+241
| let child_status = | ||
| timeout(Duration::from_secs(PROCESS_EXIT_TIMEOUT_SECS), child.wait()) | ||
| .await | ||
| .ok() | ||
| .and_then(|r| r.ok()); | ||
| let stderr_msg = collect_stderr(stderr_handle).await; |
Comment on lines
+716
to
+728
| // Verify that the installed HARDWARE.md (from Section 3) contains | ||
| // the device_exec rule so the LLM knows to use it for blink/loops. | ||
| // This acts as the Section 5 BUG-2 behavioral gate. | ||
| if let Some(home) = directories::BaseDirs::new().map(|d| d.home_dir().to_path_buf()) { | ||
| let hw_md = home.join(".zeroclaw").join("hardware").join("HARDWARE.md"); | ||
| if hw_md.exists() { | ||
| let content = fs::read_to_string(&hw_md).unwrap_or_default(); | ||
| assert!( | ||
| content.contains("device_exec"), | ||
| "HARDWARE.md must mention device_exec for blink/loop operations; got: {content}" | ||
| ); | ||
| } | ||
| } |
| } | ||
|
|
||
| fn is_connected(&self) -> bool { | ||
| !AardvarkHandle::find_devices().is_empty() |
Comment on lines
+205
to
+208
| tokio::task_local! { | ||
| pub(crate) static TOOL_CHOICE_OVERRIDE: Option<String>; | ||
| } | ||
|
|
Comment on lines
+16
to
+20
| //! ZeroClaw's agent loop calls [`crate::hardware::boot`] on **every** request, | ||
| //! which re-reads `~/.zeroclaw/hardware/` from disk. Writing to those files | ||
| //! therefore takes effect on the very next `/api/chat` call — no daemon restart | ||
| //! needed. The `/api/hardware/reload` endpoint verifies what is on disk and | ||
| //! reports what will be injected into the system prompt next time. |
Comment on lines
+430
to
+440
| require_pairing = false | ||
| trusted_ips = ["0.0.0.0/0"] | ||
| allow_public_bind = true | ||
| paired_tokens = [] | ||
| pair_rate_limit_per_minute = 10 | ||
| webhook_rate_limit_per_minute = 60 | ||
| trust_forwarded_headers = false | ||
| rate_limit_max_keys = 10000 | ||
| idempotency_ttl_secs = 300 | ||
| idempotency_max_keys = 10000 | ||
| webhook_secret = "mytoken123" |
Comment on lines
+61
to
+70
| async fn send(&self, cmd: &ZcCommand) -> Result<ZcResponse, TransportError> { | ||
| // Open a fresh handle per command — released when this scope ends. | ||
| let handle = self.open_handle()?; | ||
|
|
||
| let result: serde_json::Value = match cmd.cmd.as_str() { | ||
| // ── I2C ────────────────────────────────────────────────────────── | ||
| "i2c_scan" => { | ||
| handle | ||
| .i2c_enable(self.bitrate_khz) | ||
| .map_err(|e| TransportError::Other(e.to_string()))?; |
Comment on lines
+138
to
+144
| for plugin in plugins { | ||
| if tools.contains_key(&plugin.name) { | ||
| anyhow::bail!( | ||
| "duplicate tool name: plugin '{}' conflicts with an existing tool", | ||
| plugin.name | ||
| ); | ||
| } |
Closed
- struct_excessive_bools: allow on DeviceCapabilities (7 bool fields needed)
- unnecessary_debug_formatting: use .display() instead of {:?} for paths
- stable_sort_primitive: replace .sort() with .sort_unstable() on &str slices
cargo fmt was exiting with code 1 because mod.rs declared pub mod serial, uf2, pico_flash, pico_code but those files were missing from the branch. Also apply auto-formatting to loader.rs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(hardware): add RPi GPIO, Aardvark I2C/SPI/GPIO, and hardware plugin system
Extends the hardware subsystem with three clusters of functionality,
all feature-gated (hardware / peripheral-rpi) with no impact on default builds.
Raspberry Pi native support:
sysfs GPIO pin read/write, and ACT LED control
one-shot deployment helper and systemd service template
Total Phase Aardvark USB adapter (I2C / SPI / GPIO):
runtime via libloading; graceful stub fallback when .so is absent or
arch mismatches (Rosetta 2 detection)
i2c_write, spi_transfer, gpio_aardvark
Hardware plugin / ToolRegistry system:
load_hardware_context_prompt helpers
Integration hooks (minimal surface):
has_aardvark / resolve_aardvark_device on DeviceRegistry
Non-goals:
Risk: Low. All new paths are feature-gated; aardvark.so loads at runtime
only when present. No schema migrations or persistent state introduced.
Rollback: revert this single commit.