-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy patharduino_upload.rs
More file actions
161 lines (140 loc) · 5.44 KB
/
arduino_upload.rs
File metadata and controls
161 lines (140 loc) · 5.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! Arduino upload tool — agent generates code, uploads via arduino-cli.
//!
//! When user says "make a heart on the LED grid", the agent generates Arduino
//! sketch code and calls this tool. Gloamy compiles and uploads it — no
//! manual IDE or file editing.
use crate::tools::traits::{Tool, ToolResult};
use async_trait::async_trait;
use serde_json::{json, Value};
use std::process::Command;
/// Tool: upload Arduino sketch (agent-generated code) to the board.
pub struct ArduinoUploadTool {
/// Serial port path (e.g. /dev/cu.usbmodem33000283452)
pub port: String,
}
impl ArduinoUploadTool {
pub fn new(port: String) -> Self {
Self { port }
}
}
#[async_trait]
impl Tool for ArduinoUploadTool {
fn name(&self) -> &str {
"arduino_upload"
}
fn description(&self) -> &str {
"Generate Arduino sketch code and upload it to the connected Arduino. Use when: user asks to 'make a heart', 'blink LED', or run any custom pattern on Arduino. You MUST write the full .ino sketch code (setup + loop). Arduino Uno: pin 13 = built-in LED. Saves to temp dir, runs arduino-cli compile and upload. Requires arduino-cli installed."
}
fn parameters_schema(&self) -> Value {
json!({
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Full Arduino sketch code (complete .ino file content)"
}
},
"required": ["code"]
})
}
async fn execute(&self, args: Value) -> anyhow::Result<ToolResult> {
let code = args
.get("code")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing 'code' parameter"))?;
if code.trim().is_empty() {
return Ok(ToolResult {
success: false,
output: String::new(),
error: Some("Code cannot be empty".into()),
});
}
// Check arduino-cli exists
if Command::new("arduino-cli").arg("version").output().is_err() {
return Ok(ToolResult {
success: false,
output: String::new(),
error: Some(
"arduino-cli not found. Install it: https://arduino.github.io/arduino-cli/"
.into(),
),
});
}
let sketch_name = "gloamy_sketch";
let temp_dir = std::env::temp_dir().join(format!("gloamy_{}", uuid::Uuid::new_v4()));
let sketch_dir = temp_dir.join(sketch_name);
let ino_path = sketch_dir.join(format!("{}.ino", sketch_name));
if let Err(e) = tokio::fs::create_dir_all(&sketch_dir).await {
return Ok(ToolResult {
success: false,
output: format!("Failed to create sketch dir: {}", e),
error: Some(e.to_string()),
});
}
if let Err(e) = tokio::fs::write(&ino_path, code).await {
let _ = tokio::fs::remove_dir_all(&temp_dir).await;
return Ok(ToolResult {
success: false,
output: format!("Failed to write sketch: {}", e),
error: Some(e.to_string()),
});
}
let sketch_path = sketch_dir.to_string_lossy();
let fqbn = "arduino:avr:uno";
// Compile
let compile = Command::new("arduino-cli")
.args(["compile", "--fqbn", fqbn, &sketch_path])
.output();
let compile_output = match compile {
Ok(o) => o,
Err(e) => {
let _ = tokio::fs::remove_dir_all(&temp_dir).await;
return Ok(ToolResult {
success: false,
output: format!("arduino-cli compile failed: {}", e),
error: Some(e.to_string()),
});
}
};
if !compile_output.status.success() {
let stderr = String::from_utf8_lossy(&compile_output.stderr);
let _ = tokio::fs::remove_dir_all(&temp_dir).await;
return Ok(ToolResult {
success: false,
output: format!("Compile failed:\n{}", stderr),
error: Some("Arduino compile error".into()),
});
}
// Upload
let upload = Command::new("arduino-cli")
.args(["upload", "-p", &self.port, "--fqbn", fqbn, &sketch_path])
.output();
let upload_output = match upload {
Ok(o) => o,
Err(e) => {
let _ = tokio::fs::remove_dir_all(&temp_dir).await;
return Ok(ToolResult {
success: false,
output: format!("arduino-cli upload failed: {}", e),
error: Some(e.to_string()),
});
}
};
let _ = tokio::fs::remove_dir_all(&temp_dir).await;
if !upload_output.status.success() {
let stderr = String::from_utf8_lossy(&upload_output.stderr);
return Ok(ToolResult {
success: false,
output: format!("Upload failed:\n{}", stderr),
error: Some("Arduino upload error".into()),
});
}
Ok(ToolResult {
success: true,
output:
"Sketch compiled and uploaded successfully. The Arduino is now running your code."
.into(),
error: None,
})
}
}