feat(uno-q): bundle the granular skill set alongside arduino-uno-q on every node

Vendors the fork's 11 granular UNO Q skills (bridge, flashing, led-matrix,
uno-q-hardware, sketch-patterns, modulino, linux-led, audio, vision, wireless,
arduino-app-lab) next to the comprehensive arduino-uno-q skill, and installs the
whole set into every agent's workspace on each board.

Why both: the comprehensive skill is the rich cloud reference (read_skill →
references); the granular skills are keyword-triggered and match the fork's eager
skill-inliner rules, so the on-board Qwen auto-inlines them (no read_skill
round-trip). flashing + led-matrix carry the exact uno_q_flash + frame-API /
ArduinoGraphics-not-installed detail that makes flashing reliable.

- push-skill.sh generalized: a single skill dir (has SKILL.md) OR a parent dir
  installs every skill under it; provision-fleet now ships all of skills/.
- Verified on board 65301572: cloud/Sonnet-5 lists all 12 skills.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Omar Sobh
2026-07-16 09:32:11 -07:00
co-authored by Claude Opus 4.8
parent d2135a1938
commit 62c435c648
14 changed files with 555 additions and 37 deletions
+20 -10
View File
@@ -76,12 +76,22 @@ does client-side STT (Web Speech API) and plays the streamed `tts_chunk` audio;
the server `handle_voice_event` path is finished as part of this work. **This is the server `handle_voice_event` path is finished as part of this work. **This is
the one modality that needs an on-board build + hardware test to verify.** the one modality that needs an on-board build + hardware test to verify.**
## Default agent skill (arduino-uno-q) ## Default agent skills
Every node ships with an expert **UNO Q skill** (`skills/arduino-uno-q/` — Every node ships with a bundle of UNO Q skills (`skills/`) installed into every
`SKILL.md` + 7 `references/*.md`) so the agent knows this board's specifics agent's workspace, so the agent knows this board's specifics instead of guessing
(dual-brain architecture, the Bridge/RPC model, pin tables, the LED matrix, the generic Arduino. Two complementary sets:
`ArduinoGraphics`-not-installed gotcha, …) instead of guessing generic Arduino.
- **`arduino-uno-q`** — one comprehensive reference skill (`SKILL.md` +
7 `references/*.md`): the dual-brain mental model, App Lab vs IDE, remote access,
the Bridge/RPC deep-dive, full pin tables, wireless, and a workshop playbook.
Best for the **cloud** agent via `read_skill` → references.
- **The granular set** (`bridge`, `flashing`, `led-matrix`, `uno-q-hardware`,
`sketch-patterns`, `modulino`, `linux-led`, `audio`, `vision`, `wireless`,
`arduino-app-lab`) — small, keyword-triggered skills. The fork's eager
skill-inliner has rules for these names, so the **on-board Qwen** auto-inlines
them (no `read_skill` round-trip). `flashing` + `led-matrix` carry the exact
`uno_q_flash` + frame-API / `ArduinoGraphics`-not-installed detail.
**It must live in each agent's own workspace, not a shared bundle.** ZeroClaw's **It must live in each agent's own workspace, not a shared bundle.** ZeroClaw's
`read_skill` returns only `SKILL.md`; the agent reads a `references/*.md` on demand `read_skill` returns only `SKILL.md`; the agent reads a `references/*.md` on demand
@@ -290,9 +300,9 @@ cloud-with-fallback. In APESS, the team's provider/fallback toggle picks the ali
`systemd/zeroclaw-reload-watcher.service`; no-root: launch via `setsid nohup`. `systemd/zeroclaw-reload-watcher.service`; no-root: launch via `setsid nohup`.
- `zeroclaw-lockdown.sh` — on-board: the harden step — flips `require_pairing=true`, - `zeroclaw-lockdown.sh` — on-board: the harden step — flips `require_pairing=true`,
reloads, and mints a pair code (open → locked). reloads, and mints a pair code (open → locked).
- `push-skill.sh` — host: install an Anthropic-style skill (`SKILL.md` + `references/`) - `push-skill.sh` — host: install a skill (or a whole skills dir) into **every
into **every agent's workspace** on a board. Used to ship the default agent's workspace** on a board. `provision-fleet.sh` runs it per board over
`skills/arduino-uno-q` skill; `provision-fleet.sh` runs it per board. `skills/`.
- `skills/arduino-uno-q/` — the default UNO Q expert skill (`SKILL.md` + - `skills/` — the bundled UNO Q skills (comprehensive `arduino-uno-q` + the
7 `references/*.md`). Installed per-agent so each node has it by default. granular set). Installed per-agent so each node has them by default.
- `recover-uno-q.sh` — host-side post-disconnect recovery (re-tunnel + health-check). - `recover-uno-q.sh` — host-side post-disconnect recovery (re-tunnel + health-check).
+6 -5
View File
@@ -77,11 +77,12 @@ provision() { # kit serial -> 0 ok / 1 fail
echo " ok — modalities (reload-watcher up; lockdown staged)" echo " ok — modalities (reload-watcher up; lockdown staged)"
fi fi
# default skill — install the arduino-uno-q skill (SKILL.md + references) into # default skills — install every bundled skill (the comprehensive arduino-uno-q
# every agent's workspace so each node has it by default. Best-effort. # reference + the fork's granular set) into every agent's workspace, so each
if [ -x "$HERE/push-skill.sh" ] && [ -r "$HERE/skills/arduino-uno-q/SKILL.md" ]; then # node has them by default. Best-effort.
if "$HERE/push-skill.sh" "$serial" "$HERE/skills/arduino-uno-q" >/dev/null 2>&1; then if [ -x "$HERE/push-skill.sh" ] && [ -d "$HERE/skills" ]; then
echo " ok — skill (arduino-uno-q → agent workspaces)" if "$HERE/push-skill.sh" "$serial" "$HERE/skills" >/dev/null 2>&1; then
echo " ok — skills (all bundled → agent workspaces)"
else else
echo " ! skill install failed (onboarding still ok)" echo " ! skill install failed (onboarding still ok)"
fi fi
+44 -22
View File
@@ -1,9 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Install an Anthropic-style skill (a directory with SKILL.md + a references/ # Install Anthropic-style skills (each a directory with SKILL.md, optionally a
# subfolder) into EVERY agent's workspace on a board. # references/ subfolder) into EVERY agent's workspace on a board.
# #
# Why per-agent workspace and not a shared bundle: ZeroClaw's `read_skill` # Why per-agent workspace and not a shared bundle: ZeroClaw's `read_skill`
# returns only SKILL.md; the skill's `references/*.md` are read via the general # returns only SKILL.md; a skill's `references/*.md` are read via the general
# `file_read` tool, which is workspace-sandboxed. So references are only # `file_read` tool, which is workspace-sandboxed. So references are only
# reachable when the skill lives under an agent's own workspace # reachable when the skill lives under an agent's own workspace
# (`~/.zeroclaw/agents/<alias>/workspace/skills/<name>/`). A shared/skills bundle # (`~/.zeroclaw/agents/<alias>/workspace/skills/<name>/`). A shared/skills bundle
@@ -11,35 +11,57 @@
# #
# The skill NAME is the directory name (not the frontmatter `name`). # The skill NAME is the directory name (not the frontmatter `name`).
# #
# ./push-skill.sh <adb-serial> [skill-dir] # default skill: skills/arduino-uno-q # ./push-skill.sh <adb-serial> [path]
# path with a SKILL.md → install that one skill
# path that CONTAINS skill dirs → install every skill under it
# (default: this script's skills/ directory — all bundled skills)
set -uo pipefail set -uo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)" HERE="$(cd "$(dirname "$0")" && pwd)"
SERIAL="${1:?usage: push-skill.sh <adb-serial> [skill-dir]}" SERIAL="${1:?usage: push-skill.sh <adb-serial> [skill-dir | skills-parent-dir]}"
SKILL_DIR="${2:-$HERE/skills/arduino-uno-q}" SRC="${2:-$HERE/skills}"
ZC="/home/arduino/.zeroclaw" ZC="/home/arduino/.zeroclaw"
[ -r "$SKILL_DIR/SKILL.md" ] || { echo "no SKILL.md in $SKILL_DIR" >&2; exit 1; } # Resolve the list of skill dirs to install: either SRC itself (single skill) or
NAME="$(basename "$SKILL_DIR")" # each immediate child of SRC that has a SKILL.md.
skills=()
if [ -r "$SRC/SKILL.md" ]; then
skills=("$SRC")
else
for d in "$SRC"/*/; do
[ -r "${d}SKILL.md" ] && skills+=("${d%/}")
done
fi
[ "${#skills[@]}" -gt 0 ] || { echo "no skills found under $SRC" >&2; exit 1; }
# Discover agent aliases from the board's config-created workspaces; fall back to # Discover agent aliases from the board's config-created workspaces; fall back to
# the standard set if the listing is empty. # the standard set if the listing is empty.
agents="$(adb -s "$SERIAL" shell "ls $ZC/agents 2>/dev/null" 2>/dev/null | tr -d '\r' | tr '\n' ' ')" agents="$(adb -s "$SERIAL" shell "ls $ZC/agents 2>/dev/null" 2>/dev/null | tr -d '\r' | tr '\n' ' ')"
[ -n "${agents// /}" ] || agents="default cloud local" [ -n "${agents// /}" ] || agents="default cloud local"
ok=0 install_one() { # skill_dir alias -> 0 ok / 1 fail
for a in $agents; do local sdir="$1" alias="$2" name dest
a="$(printf '%s' "$a" | tr -d ' \r')" name="$(basename "$sdir")"
[ -z "$a" ] && continue dest="$ZC/agents/$alias/workspace/skills/$name"
dest="$ZC/agents/$a/workspace/skills/$NAME" adb -s "$SERIAL" shell "mkdir -p '$dest'" >/dev/null 2>&1 || return 1
adb -s "$SERIAL" shell "mkdir -p '$dest/references'" >/dev/null 2>&1 || { echo " ! $a: mkdir failed"; continue; } adb -s "$SERIAL" push "$sdir/SKILL.md" "$dest/" >/dev/null 2>&1 || return 1
if adb -s "$SERIAL" push "$SKILL_DIR/SKILL.md" "$dest/" >/dev/null 2>&1 \ # ship a references/ subfolder if the skill has one
&& adb -s "$SERIAL" push "$SKILL_DIR/references/." "$dest/references/" >/dev/null 2>&1; then if [ -d "$sdir/references" ]; then
echo " ok — $a ($dest)" adb -s "$SERIAL" shell "mkdir -p '$dest/references'" >/dev/null 2>&1
ok=$((ok + 1)) adb -s "$SERIAL" push "$sdir/references/." "$dest/references/" >/dev/null 2>&1 || return 1
else
echo " ! $a: push failed"
fi fi
return 0
}
total_ok=0
for a in $agents; do
a="$(printf '%s' "$a" | tr -d ' \r')"; [ -z "$a" ] && continue
n=0
for sdir in "${skills[@]}"; do
install_one "$sdir" "$a" && n=$((n + 1))
done done
echo "installed skill '$NAME' into $ok agent workspace(s) on $SERIAL" echo " ok — $a ($n/${#skills[@]} skills)"
[ "$ok" -gt 0 ] total_ok=$((total_ok + n))
done
echo "installed ${#skills[@]} skill(s) across the board's agent workspaces ($total_ok pushes) on $SERIAL"
[ "$total_ok" -gt 0 ]
@@ -0,0 +1,34 @@
---
name: arduino-app-lab
description: Arduino App Lab concepts on the Uno Q — Apps, Bricks, and the dual-side (Python + sketch) project structure. Load this when the user mentions App Lab, Bricks, or how a Uno Q "app" is organized across the Linux and Arduino sides.
---
# Arduino App Lab (Uno Q)
App Lab is Arduino's environment for Uno Q projects that span **both** sides of the
board. A project ("App") bundles a Linux-side program (usually Python) and an
Arduino sketch, wired together over the Bridge.
## Structure
- **App** — the whole project (Python + sketch + metadata, e.g. `app.yaml`).
- **Bricks** — reusable building blocks (a sensor, an actuator, a UI panel) you
compose into an App.
- **Bridge** — the RPC glue between the Python (MPU) and sketch (MCU) halves (see
the `bridge` skill).
The uno-q-bridge app that ZeroClaw deploys (`zeroclaw peripheral setup-uno-q`) is
itself an App Lab app: a `sketch/` on the MCU plus a `python/` socket server on the
Linux side exposing GPIO over TCP :9999.
## How this maps to ZeroClaw tools
- Flashing an App's sketch → `uno_q_flash` (compile + SWD flash).
- The Python side's GPIO service → `gpio_read` / `gpio_write`.
- You do not need the App Lab GUI to flash from ZeroClaw — `uno_q_flash` drives
`arduino-cli` + OpenOCD directly.
## Pitfalls
- An App's two halves run on different processors — pins live on the MCU, network
and AI on the MPU. Don't try to reach Arduino pins from Python except via Bridge.
+31
View File
@@ -0,0 +1,31 @@
---
name: audio
description: Audio on the Arduino Uno Q — microphone capture and audio output via ALSA on the Linux side, plus speech (whisper/vosk/espeak). Load this for recording sound, playing audio, or speech-to-text / text-to-speech on the board.
---
# Audio on the Uno Q (Linux side)
Audio endpoints (mic, headphone/line-out via JMISC) are **Linux/MPU** devices,
driven through **ALSA**. Sketches on the MCU do not handle audio.
## Record / play (ALSA)
```sh
arecord -d 5 -f cd /tmp/clip.wav # record 5s
aplay /tmp/clip.wav # play
arecord -l # list capture devices
```
## Speech (Python, MPU)
- **STT:** `whisper` (or `vosk` for a lighter offline model).
- **TTS:** `espeak` / `espeak-ng` (offline, no key).
```sh
espeak-ng "Sketch flashed."
```
## Pitfalls
- No Arduino audio libraries — audio is a Linux-side capability.
- Keep models small; the MPU is also hosting the LLM (~1.1 GB RSS) on the 4 GB SKU.
+50
View File
@@ -0,0 +1,50 @@
---
name: bridge
description: Arduino Bridge — the RPC layer that lets Python on the Uno Q's Linux side (MPU) call functions on the Arduino sketch (MCU) and vice-versa. Load this before any project that combines a Python program with an Arduino sketch, or asks how to send data between the Linux side and the sketch.
---
# Bridge — Inter-Processor RPC on the Uno Q
The Uno Q is two computers on one board:
- **MPU** (QRB2210, Debian Linux) — Python, web servers, AI models, OpenCV, Wi-Fi.
- **MCU** (STM32U585, Zephyr + Arduino) — `.ino` sketches; owns every Arduino
header pin (digital, PWM, ADC, I²C, SPI, UART, CAN).
**Bridge** is Arduino's software RPC layer so either side can call the other.
The MCU has **no Wi-Fi and no Linux**; the MPU has **no direct Arduino pins**. Use
Bridge whenever a project needs *both* sides.
## When to use Bridge
| Scenario | Why |
|---|---|
| Python reads a sensor wired to A0 | MCU owns the pin → Python calls the MCU |
| Sketch needs internet/weather data | MCU has no Wi-Fi → MCU calls the MPU |
| An AI model on Linux drives an LED | Python runs the model → calls the MCU |
| Sketch streams data to a web dashboard | MCU samples → MPU serves HTTP/WebSocket |
## Two ways to reach the MCU from ZeroClaw
1. **Runtime GPIO via the deployed Bridge app** — use the `gpio_read` / `gpio_write`
tools. These talk to the uno-q-bridge app (TCP :9999) that must already be
running on the board (`zeroclaw peripheral setup-uno-q` deploys it). Good for
toggling pins on a sketch that is already flashed.
2. **Flash a whole sketch** — use `uno_q_flash` (see the `flashing` skill) to
compile and program a full `.ino`, including one that `#include <Bridge.h>` and
exposes its own RPC services.
## Sketch side (MCU)
```cpp
#include <Bridge.h>
void setup() { Bridge.begin(); }
void loop() { /* expose services / handle calls */ }
```
## Pitfalls
- The MCU has **no Wi-Fi peripheral** — `#include <WiFi.h>` will not compile for
`arduino:zephyr:unoq`. Network access is the MPU's job, reached via Bridge.
- `gpio_read`/`gpio_write` require the bridge app running; if they error with a
connection failure, the app isn't up.
+33
View File
@@ -0,0 +1,33 @@
---
name: flashing
description: How to compile and flash sketches to the Arduino Uno Q MCU over SWD using the uno_q_flash tool. Load this whenever the user asks to upload, flash, run, program, or build a sketch on the Uno Q.
---
# Flashing the Uno Q MCU
Use the **`uno_q_flash`** tool. The MPU programs the MCU's flash directly over
SWD (bit-banged GPIO via OpenOCD `linuxgpiod`) — no USB cable, no SSH.
## How to use it
- `action = "upload"` + `code` (full `.ino`) — compile then flash. The sketch
runs immediately (`reset run`). **This is what you call to make a sketch run.**
- `action = "compile"` + `code` — verify it builds, don't flash.
- `action = "detect"` — list boards.
When the user asks to upload/flash/run a sketch, CALL the tool with
`action="upload"`. Do not explain the steps in prose. Always provide the
complete sketch (`setup()` + `loop()`).
## Under the hood (for context, not for the user)
- Compile: `arduino-cli compile --fqbn arduino:zephyr:unoq --export-binaries`.
- Flash: OpenOCD writes the image to the bank-2 partition at **`0x8100000`**
(the correct sketch address; `0x80F0000` is reserved and never executes).
- Requires on-device `arduino-cli` and `/opt/openocd` with `openocd_gpiod.cfg`.
## If it fails
- Compile errors come back in the tool result — fix the sketch and retry.
- "OpenOCD not found" → the Zephyr core's OpenOCD isn't installed at `/opt/openocd`.
- Remember the 3.3 V rule and that PWM is only D3/D5/D6/D9/D10/D11.
+89
View File
@@ -0,0 +1,89 @@
---
name: led-matrix
description: Drive the Arduino Uno Q's built-in 13x8 blue LED matrix — draw frames with the always-bundled Arduino_LED_Matrix frame API, or scroll text with ArduinoGraphics when it's installed. Load this whenever the user mentions the LED matrix, drawing pixels, or displaying words on the board.
---
# Uno Q Built-in LED Matrix
The Uno Q has a built-in **13 columns × 8 rows** (104-pixel) **blue** LED matrix,
driven by the STM32U585 MCU. (It is NOT an LCD, and NOT red.)
## Prefer the frame API (always available)
`Arduino_LED_Matrix` is bundled with the `arduino:zephyr` core — **no lib install**.
The **frame API** works on every core version and is the safe default. A frame is
**four `uint32_t` words** (128 bits; the first 104 map to the pixels). `loadFrame`
displays one:
```cpp
#include "Arduino_LED_Matrix.h"
Arduino_LED_Matrix matrix;
const uint32_t FULL[4] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; // all on
const uint32_t BLANK[4] = {0, 0, 0, 0}; // all off
void setup() {
matrix.begin(); // REQUIRED — without it the matrix stays dark
}
void loop() {
matrix.loadFrame(FULL);
delay(500);
matrix.loadFrame(BLANK);
delay(500);
}
```
- `matrix.loadFrame(const uint32_t[4])` — show a raw frame.
- `matrix.renderBitmap(bitmap, rows, cols)` — show an 8×13 `uint8_t` bitmap (1 = on).
- The matrix is **charlieplexed**: the MCU must keep running to refresh it. If the
sketch halts or stops driving it, the display freezes/goes dark — a *static*
pattern that never changes usually means the sketch isn't running (see `flashing`).
## Scrolling text (needs `ArduinoGraphics` — install it first)
Text scrolling needs `ArduinoGraphics`. On core 0.51.0 the matrix lib includes it
**conditionally** (`__has_include`), so it is NOT present by default — a scroll
sketch fails with `'endText'/'SCROLL_LEFT' not declared`. Install it once (needs
network), then it stays available offline:
```
arduino-cli lib install ArduinoGraphics # run with TMPDIR=/tmp on the Uno Q
```
Verified working sketch on core 0.51.0 with ArduinoGraphics@1.1.5 (compiles to ~7.6 KB):
```cpp
#include "ArduinoGraphics.h" // MUST come before Arduino_LED_Matrix.h (defines SCROLL_LEFT)
#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix; // ArduinoLEDMatrix and Arduino_LED_Matrix are aliases
void setup() {
matrix.begin();
matrix.textFont(Font_5x7); // fits the 8-row height
matrix.textScrollSpeed(80); // ms per step; lower = faster
}
void loop() {
matrix.beginText(0, 1, 0xFF, 0xFF, 0xFF); // x, y, R, G, B — the 5-arg form
matrix.print(" HELLO UNO Q "); // pad with spaces so it scrolls in and out
matrix.endText(SCROLL_LEFT);
delay(200);
}
```
If installing a library isn't an option (fully offline, no network ever), scroll a
hand-rendered font with the frame API instead: build an 8×N `uint8_t` bitmap of the
message and `renderBitmap(window, 8, 13)` a sliding 13-column window each step.
## Pitfalls
- **`ArduinoGraphics` may be absent** (core 0.51.0) — prefer the frame API; only use
the text API when the sketch is known to compile against a core that bundles it.
- Always call `matrix.begin()` in `setup()`, or the matrix stays dark.
- Include order for the text path: `ArduinoGraphics.h` **before** `Arduino_LED_Matrix.h`.
- Use the **5-argument** `beginText(x, y, R, G, B)` form; the 3-arg form is a
training-data artifact.
- It's a monochrome **blue** matrix — RGB args only set on/off, not hue.
+30
View File
@@ -0,0 +1,30 @@
---
name: linux-led
description: The Arduino Uno Q's MPU-side RGB LEDs, driven from Linux via /sys/class/leds (active-low). Load this to control the on-board Linux LEDs, as opposed to LEDs wired to Arduino pins.
---
# Linux-side RGB LEDs (MPU)
The Uno Q has RGB LEDs owned by the **MPU (Linux)**, exposed under
`/sys/class/leds/*` (e.g. `red:user`, `green:user`, `blue:bt`). These are separate
from the MCU-owned LEDs you drive with `digitalWrite`/`analogWrite` in a sketch.
## Use the `sysfs_led` tool
- `action=list` — enumerate available LEDs.
- `action=set`, `led=<name>`, `brightness=0..255` — set brightness in **natural
sense** (0 = off, 255 = full). The tool handles the active-low inversion and
scales to each LED's `max_brightness` for you.
## Under the hood
The LEDs are **active-low**: writing `0` to `<led>/brightness` turns it fully ON,
`max_brightness` turns it OFF. `sysfs_led` inverts your natural-sense value so you
never have to think about it.
## Which LED tool to use
- On-board **Linux** RGB LEDs → `sysfs_led` (this skill).
- LEDs wired to **Arduino pins**, or the built-in D13 → flash a sketch with
`uno_q_flash`, or toggle via `gpio_write` on a running sketch.
- The 13×8 blue **LED matrix** → the `led-matrix` skill (it's an MCU device).
+38
View File
@@ -0,0 +1,38 @@
---
name: modulino
description: Plug-and-play Modulino sensors/actuators for the Arduino Uno Q — Qwiic I²C modules (Buzzer, Distance, Knob, Movement, Pixels, Thermo, Touch) and their I²C addresses. Load this when a project uses a Modulino, the Qwiic connector, or asks about a named sensor module.
---
# Modulino — Qwiic Plug-and-Play I²C
Modulinos are Arduino's Qwiic (I²C) plug-and-play modules. Chain them into the
Uno Q's **Qwiic connector (I2C4)** — no soldering, no pull-ups to add. Use the
`Modulino` Arduino library on the MCU, or scan the bus with the `i2cdetect` tool.
## Default I²C addresses
| Modulino | Address |
|---|---|
| Buzzer | 0x3C |
| Distance (ToF) | 0x29 |
| Knob (rotary) | 0x76 |
| Movement (IMU) | 0x6A |
| Pixels (LEDs) | 0x6C |
| Thermo (temp/humidity) | 0x44 |
| Touch | 0x5A |
## Sketch pattern (MCU)
```cpp
#include <Modulino.h>
ModulinoKnob knob;
void setup() { Modulino.begin(); knob.begin(); }
void loop() { int v = knob.get(); /* ... */ }
```
## Tips
- Confirm a module is present with `i2cdetect` (scan the Qwiic bus) — the address
should match the table above.
- Qwiic is 3.3 V, like the rest of the Arduino headers.
- The Movement Modulino (0x6A) is an IMU — handy for the workshop's sense loop.
@@ -0,0 +1,64 @@
---
name: sketch-patterns
description: Canonical Arduino sketch templates for the Uno Q (blink, fade/PWM, button, analog read) and how to upload them. Load this whenever the user asks to write, run, upload, or flash a sketch, or asks for blink/fade/button/sensor code.
---
# Uno Q Sketch Patterns
Target the MCU with FQBN `arduino:zephyr:unoq`. Sketches are 3.3 V.
## Uploading
To run a sketch on the board, call the **`uno_q_flash`** tool with
`action` = `upload` and the full `.ino` in `code`. It compiles and flashes over
SWD. Do not describe the steps — emit the tool call.
## Blink (built-in LED, D13)
```cpp
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(500);
}
```
## Fade (PWM — only D3, D5, D6, D9, D10, D11)
```cpp
const int PIN = 9; // must be a ~ PWM pin
void setup() { pinMode(PIN, OUTPUT); }
void loop() {
for (int v = 0; v <= 255; v++) { analogWrite(PIN, v); delay(4); }
for (int v = 255; v >= 0; v--) { analogWrite(PIN, v); delay(4); }
}
```
## Button (input with pull-up)
```cpp
const int BTN = 2;
void setup() {
pinMode(BTN, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, digitalRead(BTN) == LOW ? HIGH : LOW);
}
```
## Analog read (0–3.3 V only!)
```cpp
void setup() { Serial.begin(115200); }
void loop() {
int raw = analogRead(A0); // 0..1023
float volts = raw * 3.3 / 1023.0; // NEVER exceed 3.3 V on A0-A5
Serial.println(volts);
delay(200);
}
```
@@ -0,0 +1,39 @@
---
name: uno-q-hardware
description: Arduino Uno Q board pinout and voltage rules — PWM pins, 3.3V ADC limits, I2C/SPI/Serial pins, and the MPU vs MCU split. Load this before answering any question that names a pin (D0-D21, A0-A5) or a voltage, or before writing a sketch that uses analogRead/analogWrite/Wire/SPI/Serial.
---
# Arduino Uno Q Hardware
Two processors talk over an RPC Bridge:
- **MPU (Linux)** — Qualcomm QRB2210, 4× Cortex-A53. Runs ZeroClaw, the LLM,
arduino-cli, OpenOCD. MPU I/O is **1.8 V** and is **not** reachable from sketches.
- **MCU (Arduino)** — STM32U585, Cortex-M33, Zephyr + Arduino core. Runs `.ino`
sketches. **Every Arduino header (JDIGITAL, JANALOG, JSPI, Qwiic) is 3.3 V.**
Sketches always target the MCU (`arduino:zephyr:unoq`).
## Quick facts
- **PWM pins:** D3, D5, D6, D9, D10, D11 (the `~` pins). `analogWrite` only works here.
- **Built-in LED:** D13.
- **I2C2** (`Wire`): SDA = D20, SCL = D21. **I2C3** (`Wire1`): SDA = A4, SCL = A5.
**I2C4** = the Qwiic connector (plug-and-play Modulino sensors).
- **SPI (SPI2):** D10 = CS, D11 = MOSI, D12 = MISO, D13 = SCK.
- **Serial:** `Serial` = D0 (RX), D1 (TX).
- **DAC:** A0 = DAC0, A1 = DAC1 (12-bit; `analogWriteResolution(12)`).
- **CAN:** FDCAN1 on D4 (TX), D5 (RX) — needs a transceiver (e.g. TJA1051T/3).
## Voltage safety — call this out every time
- ADC inputs A0–A5 accept **0–3.3 V only** (abs max ~3.6 V). **5 V on A0–A5 damages
the STM32U585.** Unlike a classic 5 V Uno.
- `analogRead()` returns 0–1023; volts = `raw * 3.3 / 1023.0`.
- For a 5 V sensor, divide down: 5 V → 10 kΩ → A0 → 20 kΩ → GND.
## On-board LEDs
- RGB LED 1/2 are MPU-owned (`/sys/class/leds/*`, use the `sysfs_led` tool).
- RGB LED 3/4 are MCU-owned (`analogWrite`/`digitalWrite` in a sketch).
- All RGB LEDs are **active-low** (write 0 = on). `sysfs_led` inverts this for you.
+38
View File
@@ -0,0 +1,38 @@
---
name: vision
description: Camera and computer vision on the Arduino Uno Q — MIPI-CSI-2 camera via V4L2/GStreamer and OpenCV on the Linux side. Load this for capturing images/frames, camera pipelines, or running vision on the board.
---
# Vision on the Uno Q (Linux side)
The camera (MIPI-CSI-2) is a **Linux/MPU** device exposed via **V4L2**
(`/dev/video0`). Capture and processing run on the MPU, not the MCU.
## Capture a frame
Use the **`camera`** tool — it runs a single-frame GStreamer pipeline and writes a
JPEG (args: `device`, `width`, `height`, `output` under /tmp, /var/tmp, or $HOME).
Equivalent pipeline it runs:
```sh
gst-launch-1.0 v4l2src device=/dev/video0 num-buffers=1 \
! video/x-raw,width=1280,height=720 ! videoconvert ! jpegenc \
! filesink location=/tmp/frame.jpg
```
## Processing (Python, MPU)
```python
import cv2
img = cv2.imread("/tmp/frame.jpg")
# detect / annotate / stream ...
```
The Adreno 702 GPU provides hardware H.264/H.265/VP9 encode/decode for streaming.
## Pitfalls
- The camera is not accessible from a sketch — it's a Linux device. Drive vision
from Python (MPU); use Bridge if a sketch needs the result.
- `output` for `camera` must be an absolute path under /tmp, /var/tmp, or $HOME.
+39
View File
@@ -0,0 +1,39 @@
---
name: wireless
description: Wi-Fi and Bluetooth on the Arduino Uno Q — both radios live on the Linux (MPU) side, not the MCU. Load this for questions about connecting to Wi-Fi, scanning networks, Bluetooth/BLE, or why WiFi.h won't compile in a sketch.
---
# Wireless on the Uno Q (Linux side only)
The Wi-Fi 5 + Bluetooth 5.1 radio (WCN3980) is on the **MPU (Linux)** side. The
**MCU has no wireless peripheral** — sketches cannot do Wi-Fi/BT directly. A sketch
that needs the network must call the MPU over the Bridge (see the `bridge` skill).
## Wi-Fi (Linux)
Managed by **NetworkManager** on the Debian side:
```sh
nmcli device wifi list # scan
nmcli device wifi connect "<SSID>" password "<PSK>"
nmcli connection show --active # verify
```
Use the `network` tool for a read-only view of interfaces + the default gateway.
## Bluetooth / BLE (Linux)
Managed by **BlueZ**:
```sh
bluetoothctl # scan on / pair / connect
```
From Python: **bleak** (central/client) or **bless** (peripheral/server).
## Pitfalls
- `#include <WiFi.h>` / `<WiFiNINA.h>` do **not** compile for
`arduino:zephyr:unoq` — there is no MCU Wi-Fi.
- To get sensor data online: MCU samples the pin → Bridge → Python (MPU) makes the
HTTP request / serves the dashboard.