Vendors the comprehensive UNO Q skill (SKILL.md + 7 references/*.md) and installs it into EVERY agent's workspace on each board, so agents know this board's specifics (dual-brain arch, Bridge/RPC, pin tables, LED matrix + the ArduinoGraphics-not-installed gotcha) instead of guessing generic Arduino. Why per-agent workspace: ZeroClaw's read_skill returns only SKILL.md; the agent reads references/*.md via the workspace-sandboxed file_read tool, so references are only reachable under ~/.zeroclaw/agents/<alias>/workspace/skills/. A shared/skills bundle surfaces the skill but its references get sandbox-blocked. - push-skill.sh installs a SKILL.md+references skill into every agent workspace (discovers aliases from the board); provision-fleet runs it per board. - config.template risk profile now allows + auto-approves read_skill + file_read so agents load skills without a human approver (webhook path is non-interactive). - Flattened the folded 'description: >-' to single-line (ZeroClaw's frontmatter parser is a flat scanner, not full YAML). Verified on board 65301572 with cloud/Sonnet-5: discovered arduino-uno-q → read_skill(SKILL.md) → file_read references/04-bridge-rpc.md → correct board-specific answer citing the file. Co-Authored-By: Claude Opus 4.8 <[email protected]>
5.4 KiB
UNO Q — Pins, Buses & Controlling Attached Components
All physical I/O belongs to the MCU (STM32). You control attached components by writing an Arduino sketch that owns the pin/bus. If Linux/Python needs to be involved, expose the control as a Bridge function and call it from Python (see the Bridge reference). MCU I/O is 3.3 V logic.
Digital pins
47 digital pins total (22 on the UNO-style header, 25 on the JMISC connector). UNO-style header mapping:
| MCU pin | Arduino | Function |
|---|---|---|
| PB7 | D0 / RX | GPIO / UART RX |
| PB6 | D1 / TX | GPIO / UART TX |
| PB3 | D2 | GPIO |
| PB0 | D3 | GPIO / OPAMP OUT / PWM |
| PA12 | D4 / FDCAN1_TX | GPIO / CAN TX |
| PA11 | D5 / FDCAN1_RX | GPIO / CAN RX / PWM |
| PB1 | D6 | GPIO / PWM |
| PB2 | D7 | GPIO |
| PB4 | D8 | GPIO |
| PB8 | D9 | GPIO / PWM |
| PB9 | D10 / SS | GPIO / SPI SS / PWM |
| PB15 | D11 / MOSI | GPIO / SPI MOSI / PWM |
| PB14 | D12 / MISO | GPIO / SPI MISO |
| PB13 | D13 / SCK | GPIO / SPI SCK |
| PA4 | D14 / DAC0 | GPIO / ADC / DAC |
| PA5 | D15 / DAC1 | GPIO / ADC / DAC |
| PA6 | D16 | GPIO / ADC / OPAMP IN+ |
| PA7 | D17 | GPIO / ADC / OPAMP IN- |
| PC1 | D18 / SDA2 | GPIO / ADC / I2C SDA |
| PC0 | D19 / SCL2 | GPIO / ADC / I2C SCL |
| PB11 | D20 / SDA | GPIO / I2C SDA |
| PB10 | D21 / SCL | GPIO / I2C SCL |
Usage:
pinMode(pin, INPUT | OUTPUT | INPUT_PULLUP);
state = digitalRead(pin);
digitalWrite(pin, HIGH | LOW);
Button-reads-input, LED-follows example: pinMode(btn, INPUT_PULLUP); if (digitalRead(btn)==LOW) digitalWrite(led,HIGH);
Analog input (ADC) — JANALOG connector
6 channels, 14-bit ADC.
| MCU pin | Arduino | Function |
|---|---|---|
| PA4 | A0 | ADC / DAC |
| PA5 | A1 | ADC / DAC |
| PA6 | A2 | ADC / OPAMP IN+ |
| PA7 | A3 | ADC / OPAMP IN- |
| PC1 | A4 | ADC / I2C SDA |
| PC0 | A5 | ADC / I2C SCL |
analogReadResolution(14); // 0–16383
analogReference(AR_INTERNAL2V5); // set V_REF+ (see table)
int v = analogRead(A0);
Voltage-reference options: AR_INTERNAL1V5 (1.5 V), AR_INTERNAL1V8 (1.8 V), AR_INTERNAL2V05 (2.048 V), AR_INTERNAL2V5 (2.5 V), AR_EXTERNAL (2 V…VDD, external).
Analog output (DAC)
Two true DAC outputs.
| MCU pin | Arduino |
|---|---|
| PA4 | DAC0 |
| PA5 | DAC1 |
analogWriteResolution(12); // 0–4095
analogWrite(DAC0, value);
(Good for generating waveforms — the docs include a 60 Hz sine via a 256-point LUT clocked with micros().)
PWM
6 PWM-capable pins: D3, D5, D6, D9, D10, D11. Default resolution 8-bit (0–255); change with analogWriteResolution(bits). PWM frequency is fixed at 500 Hz.
analogWriteResolution(10); // 0–1023
analogWrite(D3, dutyValue);
SPI
| MCU pin | Arduino |
|---|---|
| PB9 | SS / D10 |
| PB15 | MOSI / D11 |
| PB14 | MISO / D12 |
| PB13 | SCK / D13 |
#include <SPI.h>
#define SS D10
void setup(){ pinMode(SS,OUTPUT); digitalWrite(SS,HIGH); SPI.begin(); }
void loop(){
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
digitalWrite(SS, LOW);
SPI.transfer(0x35); SPI.transfer(0xFA);
digitalWrite(SS, HIGH);
SPI.endTransaction();
}
I2C — two buses
| Bus / object | SCL | SDA | Where |
|---|---|---|---|
Wire |
D21 (PB10) | D20 (PB11) | UNO-style headers |
Wire1 |
I2C4_SCL (PD12) | I2C4_SDA (PD13) | Qwiic connector |
#include <Wire.h>
void setup(){ Wire.begin(); } // or Wire1.begin() for Qwiic
// Wire.beginTransmission(addr); Wire.write(...); Wire.endTransmission();
Qwiic connector
Plug-and-play I²C: polarized connector, daisy-chainable, built-in pull-ups, 3.3 V only — no breadboard/soldering. Use Wire1. Works directly with Arduino's Modulino sensor/actuator family for solder-free prototyping.
UART (hardware serial on the header)
| MCU pin | Arduino |
|---|---|
| PB6 | USART1_TX / D1 |
| PB7 | USART1_RX / D0 |
Use the Serial1 object for the physical D0/D1 pins on the JDIGITAL connector:
Serial1.begin(115200);
Serial1.println("Hello UNO Q");
while (Serial1.available()) { char c = Serial1.read(); /* ... */ }
Note:
Serial1here is the header UART. It is not the reserved router link — that reservedSerial1warning in the Bridge doc refers to the internal MPU↔MCU port; on the exposed headersSerial1drives D0/D1. Don't confuse the two: useSerial1for external UART devices, and never touch the internal router transport.
Serial for debugging → App Lab console
Since platform 0.55.0, plain Serial prints stream to the Arduino App Lab Console — use it like normal:
void setup(){ Serial.begin(9600); }
void loop(){ Serial.println("Hello UNO Q"); delay(1000); }
The legacy Monitor object (#include <Arduino_RouterBridge.h> → Monitor.begin()/println()) still works for backward compatibility, but use Serial for new projects.
How to actually control "our node's" attached hardware — recipe
- Identify the interface the component uses (GPIO / ADC / PWM / SPI / I2C / UART) and the matching pins above. Mind 3.3 V levels.
- Write the sketch that drives it (the standard Arduino libraries work:
Servo,Wire-based sensor libs, etc.). - If Python/AI/networking must control or read it, wrap each action in a
Bridge.provide_safe("name", fn)on the MCU andBridge.call("name", ...)from Python (see Bridge reference). - For quick prototyping of sensors/actuators, reach for Qwiic + Modulino modules to skip wiring.