Programming Tutorial

Arduino
UNO Q Dual-Brain · Linux + Real-Time MCU

The UNO Q is unlike any Arduino before it. It combines a full Debian Linux computer with a real-time microcontroller — all on a single board. This tutorial teaches you how to program both sides using Python and C++.

Python 3 C++ Sketch Debian Linux Arduino Bridge App Lab IDE
Qualcomm Dragonwing™ QRB2210 MPU Quad-Core Cortex-A53 2.0 GHz · Debian Linux Adreno GPU · 2× ISP STM32U585 MCU Cortex-M33 Zephyr + Arduino Bridge RPC LPDDR4X RAM 2 GB / 4 GB eMMC Storage 16 GB / 32 GB Wi-Fi 5 + BT 5.1 Dual-band USB-C Qwiic 8×13 LED Matrix + 4 RGB LEDs PWR MPU → MCU →

// 00 — The Board

What Makes the UNO Q Different?

The Arduino UNO Q (released late 2025) is not a classic microcontroller board. It packs two completely separate processors on one board, connected internally through a software bridge. Understanding this "dual-brain" architecture is the first and most important concept in UNO Q programming.

Brain #1 — The Computer

Qualcomm Dragonwing™ QRB2210

MPU · Runs Debian Linux

  • Architecture Quad-Core ARM Cortex-A53
  • Clock speed 2.0 GHz
  • GPU Adreno (AI/ML acceleration)
  • OS Debian Linux
  • RAM 2 GB or 4 GB LPDDR4X
  • Storage 16 GB or 32 GB eMMC
  • Language Python, any Linux app

Brain #2 — The Controller

STMicroelectronics STM32U585

MCU · Runs Zephyr + Arduino Core

  • Architecture ARM Cortex-M33
  • Analog pins A0 – A5 (0–5 V, 12-bit ADC)
  • Digital pins D0 – D13 (5 V logic)
  • PWM pins D3, D5, D6, D9, D10, D11
  • OS Zephyr RTOS
  • Language C++ (Arduino sketches)
  • I²C / SPI / UART Yes — standard headers
💡

Mental model: Think of the UNO Q as a Raspberry Pi (MPU) and an Arduino Uno (MCU) soldered onto the same board and able to talk to each other. Python lives on the Pi side. C++ sketches live on the Arduino side. The Bridge library lets them call each other's functions.

The Three Ways to Develop

ModeToolWhat Gets ProgrammedBest For
App Lab (recommended) Arduino App Lab IDE Both MPU (Python) + MCU (C++) together Full projects, beginners, AI features
PC-connected IDE Arduino IDE 2.x via USB MCU only (C++ sketch) Classic Arduino workflows
Headless Linux SSH + CLI (VS Code, vim…) MPU only (Python / any language) Servers, AI, networking

// 01 — Setup

Getting Your UNO Q Ready

STEP 01

Power Up the Board

Connect the UNO Q to your computer using a USB-C cable. This single cable handles power, programming, HDMI video output, and USB peripherals (via a dongle). A 5 V / 3 A power supply ensures stability when using multiple peripherals.

The first boot takes 1–2 minutes as the Debian OS initialises. The 8×13 LED matrix will display a startup animation — wait for it to settle before connecting from App Lab.

STEP 02

Install Arduino App Lab (Recommended IDE)

App Lab is the official IDE for the UNO Q. It lets you write Python (MPU side) and C++ sketches (MCU side) in one project, and deploy both with a single click. Download it at app.arduino.cc — compatible with Windows 10+, macOS 11+, and Ubuntu 22.04+. It also comes pre-installed on the UNO Q's own Debian OS if you use it as a standalone computer.

Prefer classic Arduino IDE 2.x? That still works — but it can only program the MCU side (C++ sketches). You will not be able to deploy Python to the MPU from Arduino IDE.

STEP 03

Select Your Board in App Lab

Open App Lab → New Project → choose Arduino UNO Q from the board selector. App Lab will auto-detect the board over USB and show both the MPU and MCU subsystems in the project explorer.

BASH — Alternative: verify via SSH (headless mode)
# The UNO Q runs an SSH server on its Debian OS by default.
# Connect your PC and UNO Q to the same Wi-Fi network, then:
ssh arduino@arduino-uno-q.local

# Once in, verify the Linux environment:
uname -a
# Linux arduino-uno-q 6.x.x #1 SMP aarch64 GNU/Linux

python3 --version
# Python 3.11.x

STEP 04

Understand the Project Structure in App Lab

A UNO Q project in App Lab contains three folders:

FolderRuns OnLanguagePurpose
sketch/STM32 MCUC++ (.ino)Real-time GPIO, sensors, motors
app/Qualcomm MPUPython (.py)AI, networking, logic, UI
bricks/BothYAML configPre-built feature modules (Bricks)

// 02 — C++ Sketch (STM32 MCU)

Programming the Real-Time Controller

The MCU runs your C++ sketch via the Arduino Core on Zephyr OS. Every sketch you already know works here — pinMode(), digitalWrite(), analogRead() — but now the STM32U585 is much more capable than a classic Uno's ATmega328P.

💡

Key difference from classic Uno: The sketch runs on the MCU subsystem — the STM32 chip. The GPIO headers (D0–D13, A0–A5) are connected to this MCU, NOT to the Qualcomm MPU. Always write pin control code in C++, not Python.

Sketch Anatomy

Every sketch has the same two required functions — nothing has changed here from classic Arduino:

C++ — sketch/sketch.ino — anatomy
// sketch/sketch.ino
// This file runs on the STM32U585 MCU

void setup() {
  // Runs ONCE at power-on or reset.
  // Configure pin modes, start Serial, initialise sensors.
}

void loop() {
  // Runs FOREVER in a tight loop.
  // Read sensors, drive outputs, respond to events.
}

Sketch 1 — Blink the Built-In LED

The UNO Q has a built-in LED on pin 13 (connected to the MCU) plus an 8×13 LED matrix driven by the MPU side. Start with pin 13 to confirm your sketch uploads correctly.

C++ — sketch/blink.ino
// Blink built-in LED — confirms MCU sketch is running
const int LED = 13;        // D13 → MCU GPIO

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(115200);    // higher baud than classic Uno — STM32 is faster
  Serial.println("UNO Q MCU ready");
}

void loop() {
  digitalWrite(LED, HIGH);
  delay(500);
  digitalWrite(LED, LOW);
  delay(500);
}

Sketch 2 — Read an Analog Sensor

Wire a potentiometer (or any 0–5 V analog source) to pin A0. The STM32U585 has a 12-bit ADC — values range from 0 to 4095 (vs. 0–1023 on classic Uno).

C++ — sketch/analog_read.ino
// Read A0 (12-bit: 0–4095) and print to Serial
const int SENSOR = A0;

void setup() {
  analogReadResolution(12);  // enable 12-bit mode (UNO Q specific!)
  Serial.begin(115200);
}

void loop() {
  int raw = analogRead(SENSOR);
  float volts = raw * (5.0 / 4095.0);
  Serial.print("Raw: "); Serial.print(raw);
  Serial.print("  Volts: "); Serial.println(volts);
  delay(200);
}

UNO Q gotcha: Always call analogReadResolution(12) in setup(). Without it, you may get 10-bit behaviour. Scale accordingly: 12-bit → divide by 4095, 10-bit → divide by 1023.

Sketch 3 — PWM Output (LED Dimmer)

PWM is available on pins D3, D5, D6, D9, D10, D11 — the same pins marked with ~ on the UNO headers.

C++ — sketch/pwm_fade.ino
// PWM fade on D9 — 0 to 255 and back
const int PWM_PIN = 9;

void setup() {
  pinMode(PWM_PIN, OUTPUT);
}

void loop() {
  // Fade in
  for (int i = 0; i <= 255; i++) {
    analogWrite(PWM_PIN, i);
    delay(8);
  }
  // Fade out
  for (int i = 255; i >= 0; i--) {
    analogWrite(PWM_PIN, i);
    delay(8);
  }
}

Pin Reference — MCU Side

PinModeNotes
D0 (RX) / D1 (TX)Digital / UARTSerial communication — avoid for GPIO if using Serial.print
D2Digital / InterruptINT0 external interrupt
D3~Digital / PWM / InterruptINT1 + 8-bit PWM
D4 – D8Digital I/OStandard GPIO, 5 V logic
D9~, D10~, D11~Digital / PWM8-bit PWM; D10/D11 also SPI CS/MOSI
D12, D13Digital / SPID13 = built-in LED + SPI CLK
A0 – A5Analog In / Digital12-bit ADC (0–5 V); call analogReadResolution(12)
SDA (A4) / SCL (A5)I²CUse Wire library for I²C sensors

// 03 — Python (Qualcomm MPU)

Programming the Linux Side

The MPU runs full Debian Linux with Python 3 pre-installed. You write Python in the app/main.py file inside your App Lab project. This side handles everything high-level: networking, file I/O, AI models, web servers, computer vision — anything you'd do on a Linux PC.

Python Hello World on the UNO Q

PYTHON — app/main.py
# app/main.py — runs on the Qualcomm MPU (Debian Linux)
import time

print("Hello from UNO Q Linux side!")
print(f"Running on Debian, Python {__import__('sys').version}")

while True:
    print("MPU is alive...")
    time.sleep(1)

Controlling the Built-In 8×13 LED Matrix

The UNO Q has an 8-row × 13-column LED matrix and 4 RGB LEDs that are connected to the MPU side (not the MCU). Control them in Python using the arduinoio library (pre-installed on the board).

LIVE DEMO — 8×13 LED Matrix (click to toggle)

PYTHON — app/led_matrix.py
# Control the 8x13 LED matrix from Python (MPU side)
# Uses the arduinoio library pre-installed on Debian
from arduinoio import LEDMatrix
import time

matrix = LEDMatrix()

# Turn on a single LED at row 0, column 0
matrix.set_pixel(0, 0, True)

# Write text scrolling across the matrix
matrix.print_text("UNO Q")

# Draw a pattern using a 2D list (8 rows x 13 cols)
pattern = [
    [1,0,1,0,1,0,1,0,1,0,1,0,1],  # row 0
    [0,1,0,1,0,1,0,1,0,1,0,1,0],  # row 1
    # ... rows 2-7 ...
]
matrix.set_pattern(pattern)
time.sleep(3)
matrix.clear()

Reading Sensors from Python via Bridge

You cannot call analogRead() directly from Python — that function belongs to the MCU. Instead, you use the Bridge library to ask the MCU for sensor values. See Section 04 — Bridge for the full explanation.

Python: Installing Libraries (Debian Linux)

BASH — SSH into UNO Q Linux side
# The UNO Q runs full Debian — use standard apt and pip
sudo apt update
sudo apt install python3-opencv    # computer vision
sudo apt install python3-flask     # web server
pip3 install numpy pandas          # data science

# Check what's pre-installed:
pip3 list | grep arduino
# arduinoio     1.x.x  ← the Bridge + hardware library

// 04 — The Bridge Library

Making Python and C++ Talk to Each Other

The Bridge is Arduino's RPC (Remote Procedure Call) library that lets your Python app on the MPU call functions defined in your C++ sketch on the MCU — and vice versa. This is the magic that makes the UNO Q unique.

Python App app/main.py from arduinoio import Bridge Runs on Qualcomm MPU Arduino Bridge (RPC) Internal high-speed bus function calls ↔ data exchange C++ Sketch sketch/sketch.ino #include <Bridge.h> Runs on STM32 MCU call() request response

Bridge Example: Python Reads an MCU Sensor

This is a complete two-file project. First the C++ sketch exposes a function via Bridge, then Python calls it.

C++ — sketch/sketch.ino — expose sensor via Bridge
#include <Bridge.h>

const int SENSOR_PIN = A0;

// This function is called by Python through the Bridge.
void getSensorValue(BridgeClient& client) {
  analogReadResolution(12);
  int raw    = analogRead(SENSOR_PIN);
  float volts = raw * (5.0f / 4095.0f);
  client.println(volts, 3);  // send 3-decimal float back to Python
}

// Register the function so Python can find it by name.
BridgeServer server;

void setup() {
  Bridge.begin();
  server.begin();
  server.addHandler("sensor", getSensorValue);
}

void loop() {
  server.process();   // keeps the Bridge running — must be in loop()
}
PYTHON — app/main.py — call MCU sensor via Bridge
from arduinoio import Bridge
import time

bridge = Bridge()
bridge.begin()  # connect to MCU over the internal bus

print("Polling analog sensor from Python via Bridge...")

while True:
    # Call the "sensor" handler registered in the C++ sketch
    response = bridge.call("sensor")
    voltage  = float(response.strip())

    print(f"A0 voltage: {voltage:.3f} V")

    if voltage > 3.5:
        print("  ⚡ High voltage detected — trigger Python-side alert!")

    time.sleep(0.5)

The Bridge pattern in one sentence: C++ sketch registers named handlers → Python calls them by name → MCU executes the GPIO operation → returns data to Python. The two processors stay in sync without any manual serial wiring.

Bridge: Python Controls MCU Output (LED via Python)

C++ — sketch/sketch.ino — LED control via Bridge
#include <Bridge.h>
const int LED = 13;

void setLED(BridgeClient& client) {
  String state = client.readStringUntil('\n').trim();
  digitalWrite(LED, (state == "1") ? HIGH : LOW);
  client.println("OK");
}

BridgeServer server;

void setup() {
  Bridge.begin();
  pinMode(LED, OUTPUT);
  server.begin();
  server.addHandler("led", setLED);
}

void loop() { server.process(); }
PYTHON — app/main.py — toggle LED from Python
from arduinoio import Bridge
import time

bridge = Bridge()
bridge.begin()

print("Python blinking D13 via Bridge...")

for _ in range(10):
    bridge.call("led", "1")   # LED on
    time.sleep(0.4)
    bridge.call("led", "0")   # LED off
    time.sleep(0.4)

print("Done.")

// 05 — Projects

Put It All Together

PROJECT A

Smart Temperature Monitor

Wire a DS18B20 or NTC thermistor to A0. The C++ sketch reads the sensor and exposes it via Bridge. The Python app polls it every second, logs to a CSV on the eMMC, and hosts a live Flask web page on the local Wi-Fi network so you can see the graph from any device.

💡

Use pandas and matplotlib on the Python side for analysis. The UNO Q's 2 GHz quad-core makes data science libraries fast enough for real-time plotting.

PROJECT B

Object Detection with a USB Camera

Plug a USB webcam into the UNO Q USB-C port via a dongle. Use opencv-python on the Python (MPU) side to run object detection. When a target is detected, Python calls a Bridge function in the C++ sketch to trigger an LED or buzzer on the GPIO pins — pure hardware response to a vision event.

PYTHON — app/vision.py (key excerpt)
import cv2
from arduinoio import Bridge

bridge = Bridge(); bridge.begin()
cap    = cv2.VideoCapture(0)  # USB webcam

while True:
    ret, frame = cap.read()
    # ... run detection model ...
    if person_detected:
        bridge.call("alert", "1")  # buzzer ON via MCU

PROJECT C

Qwiic / Modulino Sensor Chain

The UNO Q has a Qwiic connector that speaks I²C. Snap in Modulino nodes (no soldering!) for buttons, distance sensors, RGB LEDs, knobs, and more. Control them from either the Python side (via arduinoio) or the C++ sketch via the standard Wire library.

Modulino NodeSensor/ActuatorBest Controlled From
Modulino Pixels8× RGB LED stripPython (effects) or C++ (timing)
Modulino Buttons3 tactile buttonsC++ (interrupt-driven) or Python
Modulino DistanceVL53L4CD ToFC++ for low-latency robotics
Modulino KnobRotary encoder + LEDC++ (precise angle tracking)
Modulino ThermoHS3003 temp + humidityPython (CSV logging)

PROJECT D

Wi-Fi Remote Control Dashboard

Use Python's Flask to host a web app on the UNO Q's dual-band Wi-Fi. Any browser on your network can toggle LEDs, read sensors, and stream the LED matrix state — no extra hardware needed beyond the board itself.

PYTHON — app/dashboard.py
from flask   import Flask, jsonify, request
from arduinoio import Bridge
import threading

app    = Flask(__name__)
bridge = Bridge(); bridge.begin()
lock   = threading.Lock()

@app.route("/led", methods=["POST"])
def led():
    state = request.json.get("state", "0")
    with lock:
        bridge.call("led", state)
    return jsonify(ok=True)

@app.route("/sensor")
def sensor():
    with lock:
        v = float(bridge.call("sensor").strip())
    return jsonify(voltage=v)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
# Access at: http://arduino-uno-q.local:5000

// 06 — Quick Reference

UNO Q Cheat Sheet

TaskLanguageSideKey Function / Tool
Read a pin (D2–D13)C++MCUdigitalRead(pin)
Write a pinC++MCUdigitalWrite(pin, HIGH/LOW)
Read analog sensorC++MCUanalogRead(A0..A5) — set resolution first
PWM outputC++MCUanalogWrite(pin, 0–255) on ~pins
I²C sensorC++MCU#include <Wire.h>
Expose data to PythonC++MCUBridge.h + BridgeServer.addHandler()
Call MCU functionPythonMPUbridge.call("handler_name")
LED matrixPythonMPUarduinoio.LEDMatrix
Wi-Fi / networkingPythonMPUstandard socket, requests, flask
Computer visionPythonMPUcv2 (OpenCV) on Debian
AI inferencePythonMPUtflite / ONNX + Adreno GPU
Deploy full projectBothBothArduino App Lab → Run button

Common Beginner Mistakes

MistakeWhy it FailsFix
Calling analogRead() in PythonPython runs on MPU; GPIO is MCU-onlyUse Bridge to ask MCU for the value
Forgetting analogReadResolution(12)May default to 10-bit, wrong scaleAdd to setup() in every sketch
Opening Serial Monitor in Arduino IDE while App Lab is runningPort conflict; both try to own the portUse only one tool at a time
Forgetting server.process() in loop()Bridge never receives Python callsAlways put it in the loop
Expecting Wi-Fi from the MCU sketchWi-Fi is on the MPU sideAll wireless code goes in Python
Using delay() in loop with Bridge activedelay() blocks server.process()Use millis()-based non-blocking timing