Skip to content

feat(hardware): add RPi GPIO, Aardvark I2C/SPI/GPIO, and hardware plugin system#3517

Open
Extreammouse wants to merge 6 commits intomasterfrom
feat/hardware-rpi-aardvark-gpio
Open

feat(hardware): add RPi GPIO, Aardvark I2C/SPI/GPIO, and hardware plugin system#3517
Extreammouse wants to merge 6 commits intomasterfrom
feat/hardware-rpi-aardvark-gpio

Conversation

@Extreammouse
Copy link
Collaborator

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:

  • 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.

…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.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/ZcResponse protocol layer and a ToolRegistry that 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), an AardvarkTransport, 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
);
}
WPIGoat added 2 commits March 14, 2026 21:51
- 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants