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:
co-authored by
Claude Opus 4.8
parent
d2135a1938
commit
62c435c648
+20
-10
@@ -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).
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
echo " ok — $a ($n/${#skills[@]} skills)"
|
||||||
|
total_ok=$((total_ok + n))
|
||||||
done
|
done
|
||||||
echo "installed skill '$NAME' into $ok agent workspace(s) on $SERIAL"
|
echo "installed ${#skills[@]} skill(s) across the board's agent workspaces ($total_ok pushes) on $SERIAL"
|
||||||
[ "$ok" -gt 0 ]
|
[ "$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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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).
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
Reference in New Issue
Block a user