Ehu shubham shaw contribution --> Hardware support (#306)
* feat: add ZeroClaw firmware for ESP32 and Nucleo * Introduced new firmware for ZeroClaw on ESP32 and Nucleo-F401RE, enabling JSON-over-serial communication for GPIO control. * Added `zeroclaw-esp32` with support for commands like `gpio_read` and `gpio_write`, along with capabilities reporting. * Implemented `zeroclaw-nucleo` firmware with similar functionality for STM32, ensuring compatibility with existing ZeroClaw protocols. * Updated `.gitignore` to include new firmware targets and added necessary dependencies in `Cargo.toml` for both platforms. * Created README files for both firmware projects detailing setup, build, and usage instructions. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: enhance hardware peripheral support and documentation - Added `Peripheral` trait implementation in `src/peripherals/` to manage hardware boards (STM32, RPi GPIO). - Updated `AGENTS.md` to include new extension points for peripherals and their configuration. - Introduced comprehensive documentation for adding boards and tools, including a quick start guide and supported boards. - Enhanced `Cargo.toml` to include optional dependencies for PDF extraction and peripheral support. - Created new datasheets for Arduino Uno, ESP32, and Nucleo-F401RE, detailing pin aliases and GPIO usage. - Implemented new tools for hardware memory reading and board information retrieval in the agent loop. This update significantly improves the integration and usability of hardware peripherals within the ZeroClaw framework. * feat: add ZeroClaw firmware for ESP32 and Nucleo * Introduced new firmware for ZeroClaw on ESP32 and Nucleo-F401RE, enabling JSON-over-serial communication for GPIO control. * Added `zeroclaw-esp32` with support for commands like `gpio_read` and `gpio_write`, along with capabilities reporting. * Implemented `zeroclaw-nucleo` firmware with similar functionality for STM32, ensuring compatibility with existing ZeroClaw protocols. * Updated `.gitignore` to include new firmware targets and added necessary dependencies in `Cargo.toml` for both platforms. * Created README files for both firmware projects detailing setup, build, and usage instructions. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: enhance hardware peripheral support and documentation - Added `Peripheral` trait implementation in `src/peripherals/` to manage hardware boards (STM32, RPi GPIO). - Updated `AGENTS.md` to include new extension points for peripherals and their configuration. - Introduced comprehensive documentation for adding boards and tools, including a quick start guide and supported boards. - Enhanced `Cargo.toml` to include optional dependencies for PDF extraction and peripheral support. - Created new datasheets for Arduino Uno, ESP32, and Nucleo-F401RE, detailing pin aliases and GPIO usage. - Implemented new tools for hardware memory reading and board information retrieval in the agent loop. This update significantly improves the integration and usability of hardware peripherals within the ZeroClaw framework. * feat: Introduce hardware auto-discovery and expanded configuration options for agents, hardware, and security. * chore: update dependencies and improve probe-rs integration - Updated `Cargo.lock` to remove specific version constraints for several dependencies, including `zerocopy`, `syn`, and `strsim`, allowing for more flexibility in version resolution. - Upgraded `bincode` and `bitfield` to their latest versions, enhancing serialization and memory management capabilities. - Updated `Cargo.toml` to reflect the new version of `probe-rs` from `0.24` to `0.30`, improving hardware probing functionality. - Refactored code in `src/hardware` and `src/tools` to utilize the new `SessionConfig` for session management in `probe-rs`, ensuring better compatibility and performance. - Cleaned up documentation in `docs/datasheets/nucleo-f401re.md` by removing unnecessary lines. * fix: apply cargo fmt * docs: add hardware architecture diagram. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b36f23784a
commit
de3ec87d16
59 changed files with 9607 additions and 1885 deletions
187
firmware/zeroclaw-nucleo/src/main.rs
Normal file
187
firmware/zeroclaw-nucleo/src/main.rs
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
//! ZeroClaw Nucleo-F401RE firmware — JSON-over-serial peripheral.
|
||||
//!
|
||||
//! Listens for newline-delimited JSON on USART2 (PA2=TX, PA3=RX).
|
||||
//! USART2 is connected to ST-Link VCP — host sees /dev/ttyACM0 (Linux) or /dev/cu.usbmodem* (macOS).
|
||||
//!
|
||||
//! Protocol: same as Arduino/ESP32 — see docs/hardware-peripherals-design.md
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::fmt::Write;
|
||||
use core::str;
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
use embassy_stm32::usart::{Config, Uart};
|
||||
use heapless::String;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
/// Arduino-style pin 13 = PA5 (User LED LD2 on Nucleo-F401RE)
|
||||
const LED_PIN: u8 = 13;
|
||||
|
||||
/// Parse integer from JSON: "pin":13 or "value":1
|
||||
fn parse_arg(line: &[u8], key: &[u8]) -> Option<i32> {
|
||||
// key like b"pin" -> search for b"\"pin\":"
|
||||
let mut suffix: [u8; 32] = [0; 32];
|
||||
suffix[0] = b'"';
|
||||
let mut len = 1;
|
||||
for (i, &k) in key.iter().enumerate() {
|
||||
if i >= 30 {
|
||||
break;
|
||||
}
|
||||
suffix[len] = k;
|
||||
len += 1;
|
||||
}
|
||||
suffix[len] = b'"';
|
||||
suffix[len + 1] = b':';
|
||||
len += 2;
|
||||
let suffix = &suffix[..len];
|
||||
|
||||
let line_len = line.len();
|
||||
if line_len < len {
|
||||
return None;
|
||||
}
|
||||
for i in 0..=line_len - len {
|
||||
if line[i..].starts_with(suffix) {
|
||||
let rest = &line[i + len..];
|
||||
let mut num: i32 = 0;
|
||||
let mut neg = false;
|
||||
let mut j = 0;
|
||||
if j < rest.len() && rest[j] == b'-' {
|
||||
neg = true;
|
||||
j += 1;
|
||||
}
|
||||
while j < rest.len() && rest[j].is_ascii_digit() {
|
||||
num = num * 10 + (rest[j] - b'0') as i32;
|
||||
j += 1;
|
||||
}
|
||||
return Some(if neg { -num } else { num });
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn has_cmd(line: &[u8], cmd: &[u8]) -> bool {
|
||||
let mut pat: [u8; 64] = [0; 64];
|
||||
pat[0..7].copy_from_slice(b"\"cmd\":\"");
|
||||
let clen = cmd.len().min(50);
|
||||
pat[7..7 + clen].copy_from_slice(&cmd[..clen]);
|
||||
pat[7 + clen] = b'"';
|
||||
let pat = &pat[..8 + clen];
|
||||
|
||||
let line_len = line.len();
|
||||
if line_len < pat.len() {
|
||||
return false;
|
||||
}
|
||||
for i in 0..=line_len - pat.len() {
|
||||
if line[i..].starts_with(pat) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Extract "id" for response
|
||||
fn copy_id(line: &[u8], out: &mut [u8]) -> usize {
|
||||
let prefix = b"\"id\":\"";
|
||||
if line.len() < prefix.len() + 1 {
|
||||
out[0] = b'0';
|
||||
return 1;
|
||||
}
|
||||
for i in 0..=line.len() - prefix.len() {
|
||||
if line[i..].starts_with(prefix) {
|
||||
let start = i + prefix.len();
|
||||
let mut j = 0;
|
||||
while start + j < line.len() && j < out.len() - 1 && line[start + j] != b'"' {
|
||||
out[j] = line[start + j];
|
||||
j += 1;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
}
|
||||
out[0] = b'0';
|
||||
1
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
|
||||
let mut config = Config::default();
|
||||
config.baudrate = 115_200;
|
||||
|
||||
let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap();
|
||||
let mut led = Output::new(p.PA5, Level::Low, Speed::Low);
|
||||
|
||||
info!("ZeroClaw Nucleo firmware ready on USART2 (115200)");
|
||||
|
||||
let mut line_buf: heapless::Vec<u8, 256> = heapless::Vec::new();
|
||||
let mut id_buf = [0u8; 16];
|
||||
let mut resp_buf: String<128> = String::new();
|
||||
|
||||
loop {
|
||||
let mut byte = [0u8; 1];
|
||||
if usart.blocking_read(&mut byte).is_ok() {
|
||||
let b = byte[0];
|
||||
if b == b'\n' || b == b'\r' {
|
||||
if !line_buf.is_empty() {
|
||||
let id_len = copy_id(&line_buf, &mut id_buf);
|
||||
let id_str = str::from_utf8(&id_buf[..id_len]).unwrap_or("0");
|
||||
|
||||
resp_buf.clear();
|
||||
if has_cmd(&line_buf, b"ping") {
|
||||
let _ = write!(resp_buf, "{{\"id\":\"{}\",\"ok\":true,\"result\":\"pong\"}}", id_str);
|
||||
} else if has_cmd(&line_buf, b"capabilities") {
|
||||
let _ = write!(
|
||||
resp_buf,
|
||||
"{{\"id\":\"{}\",\"ok\":true,\"result\":\"{{\\\"gpio\\\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],\\\"led_pin\\\":13}}\"}}",
|
||||
id_str
|
||||
);
|
||||
} else if has_cmd(&line_buf, b"gpio_read") {
|
||||
let pin = parse_arg(&line_buf, b"pin").unwrap_or(-1);
|
||||
if pin == LED_PIN as i32 {
|
||||
// Output doesn't support read; return 0 (LED state not readable)
|
||||
let _ = write!(resp_buf, "{{\"id\":\"{}\",\"ok\":true,\"result\":\"0\"}}", id_str);
|
||||
} else if pin >= 0 && pin <= 13 {
|
||||
let _ = write!(resp_buf, "{{\"id\":\"{}\",\"ok\":true,\"result\":\"0\"}}", id_str);
|
||||
} else {
|
||||
let _ = write!(
|
||||
resp_buf,
|
||||
"{{\"id\":\"{}\",\"ok\":false,\"result\":\"\",\"error\":\"Invalid pin {}\"}}",
|
||||
id_str, pin
|
||||
);
|
||||
}
|
||||
} else if has_cmd(&line_buf, b"gpio_write") {
|
||||
let pin = parse_arg(&line_buf, b"pin").unwrap_or(-1);
|
||||
let value = parse_arg(&line_buf, b"value").unwrap_or(0);
|
||||
if pin == LED_PIN as i32 {
|
||||
led.set_level(if value != 0 { Level::High } else { Level::Low });
|
||||
let _ = write!(resp_buf, "{{\"id\":\"{}\",\"ok\":true,\"result\":\"done\"}}", id_str);
|
||||
} else if pin >= 0 && pin <= 13 {
|
||||
let _ = write!(resp_buf, "{{\"id\":\"{}\",\"ok\":true,\"result\":\"done\"}}", id_str);
|
||||
} else {
|
||||
let _ = write!(
|
||||
resp_buf,
|
||||
"{{\"id\":\"{}\",\"ok\":false,\"result\":\"\",\"error\":\"Invalid pin {}\"}}",
|
||||
id_str, pin
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let _ = write!(
|
||||
resp_buf,
|
||||
"{{\"id\":\"{}\",\"ok\":false,\"result\":\"\",\"error\":\"Unknown command\"}}",
|
||||
id_str
|
||||
);
|
||||
}
|
||||
|
||||
let _ = usart.blocking_write(resp_buf.as_bytes());
|
||||
let _ = usart.blocking_write(b"\n");
|
||||
line_buf.clear();
|
||||
}
|
||||
} else if line_buf.push(b).is_err() {
|
||||
line_buf.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue