The Arduino UNO Q is not programmed like a normal single-processor Arduino board. It contains two very different computing environments: a Qualcomm Dragonwing QRB2210 running Debian Linux and an STM32U585 microcontroller running Arduino code on Zephyr OS.
Arduino App Lab is the development environment that brings those two sides together. A single App Lab project can contain Linux-side Python code, an Arduino sketch running on the STM32, optional containerised services called Bricks, and communication between all of them through Arduino Bridge/RPC.
This guide explains how that architecture works, when code should run on Linux versus the MCU, how the Bridge and Arduino Router fit together, and how to build a simple hybrid UNO Q application.
What Arduino App Lab Actually Does
Arduino App Lab is a unified development environment for boards such as the UNO Q that contain both an application processor and a microcontroller.
A typical App can include:
- Python code running on Debian Linux on the Qualcomm QRB2210.
- An Arduino sketch running on the STM32U585 microcontroller.
- Bricks, which are pre-packaged Linux-side components such as AI models, databases, web interfaces or external-service integrations.
- Bridge/RPC communication between the Linux application and the MCU.
When you press Run, App Lab can build and deploy the Linux portion, flash the microcontroller sketch, start any selected Bricks and launch the complete application as one project.
This is the important conceptual difference from Arduino IDE: Arduino IDE can program the STM32U585, but App Lab is designed to work with both processors together.
UNO Q Dual-Brain Architecture
The UNO Q contains:
- Qualcomm Dragonwing QRB2210 MPU: four Cortex-A53 cores running at up to 2.0 GHz, Debian Linux, graphics and image-processing hardware.
- STM32U585 MCU: Cortex-M33 running at up to 160 MHz, 2 MB Flash and 786 kB SRAM.
The basic architecture is:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
┌───────────────────────────────────────┐ │ Qualcomm QRB2210 │ │ Debian Linux │ │ │ │ Python / C++ / services / AI / files │ │ databases / networking / containers │ └─────────────────┬─────────────────────┘ │ │ Arduino Bridge / RPC │ ┌─────────────────▼─────────────────────┐ │ STM32U585 │ │ Arduino Core on Zephyr │ │ │ │ GPIO / ADC / PWM / CAN / SPI / I2C │ │ sensors / motors / real-time control │ └───────────────────────────────────────┘ |
The two processors are not competing to do the same work. The design is strongest when each processor handles the tasks it is naturally suited to.
For the complete hardware and pin mapping, see our Arduino UNO Q pinout guide.
What Should Run on Linux?
The QRB2210 side should normally handle jobs that benefit from a full operating system, large memory, storage and standard software libraries.
Typical Linux-side tasks include:
- Python applications;
- AI inference;
- computer vision;
- web servers;
- REST APIs;
- MQTT services;
- local databases;
- large file handling;
- USB cameras;
- networking;
- data processing;
- cloud communication;
- containerised applications;
- user interfaces.
For example, a Python application could process camera images, decide where a robot should move and send a speed command to the STM32.
What Should Run on the STM32U585?
The STM32 is the better location for work that depends on precise timing and direct peripheral control.
Examples include:
- reading encoders;
- generating PWM;
- sampling ADC inputs;
- controlling motors;
- reading fast sensors;
- CAN communication;
- handling interrupts;
- precise pulse measurement;
- safety interlocks;
- local watchdog behaviour;
- control loops.
A useful design rule is:
|
1 2 3 4 5 6 7 8 |
Complex, high-level, file/network/AI work → Linux / QRB2210 Timing-sensitive physical I/O → STM32U585 |
Why Not Just Control Everything from Linux?
Linux is excellent for application software but is not designed to guarantee microsecond-level response times from normal userspace processes.
A Python process can be delayed by:
- the Linux scheduler;
- storage access;
- network activity;
- another process using the CPU;
- memory management;
- background services.
That is not a problem for loading a web page or processing an image, but it can be a problem when generating motor PWM or measuring a precisely timed pulse.
The UNO Q solves this by delegating deterministic hardware tasks to the STM32.
What Is Arduino Bridge?
Bridge is Arduino’s Remote Procedure Call layer between the processors.
Remote Procedure Call, or RPC, means one processor can ask another processor to execute a named function and optionally return a result.
Conceptually, Linux can do this:
|
1 2 3 4 |
set_motor_speed(120) |
even though the actual function that changes the PWM output runs on the STM32.
The Bridge transports the function name and arguments to the MCU, the MCU executes the function, and a result can be returned to Linux.
What Is the Arduino Router?
On current UNO Q software, Bridge communication is coordinated by a Linux background service called arduino-router.
The Router sits underneath the user-facing Bridge API.
|
1 2 3 4 5 6 7 8 |
This is more capable than a simple serial link because several Linux applications can communicate through the same routing system.
The Router provides:
- service discovery;
- routing of named RPC functions;
- multiple simultaneous Linux clients;
- Linux-to-MCU communication;
- Linux-to-Linux RPC communication;
- MessagePack-based RPC transport.
This means one Linux process can read sensor data from the MCU while another Linux process controls an actuator, without each application attempting to open the MCU transport directly.
Reserved Router Interface
The Router owns the low-level communication path between Linux and the STM32.
On the Linux side, the internal serial interface used by the Router is:
|
1 2 3 4 |
/dev/ttyHS1 |
Do not open that device directly from your own Linux program while the Router is running. It is reserved for the Arduino communication infrastructure.
For normal applications, use Bridge/RPC instead.
The Router Unix Socket
Advanced Linux applications can access the Router through its Unix domain socket:
|
1 2 3 4 |
/var/run/arduino-router.sock |
This makes it possible to create custom clients in languages that can work with MessagePack RPC, including Python, C++, Rust or Go.
Most App Lab applications should not need to do this. The normal Bridge APIs are simpler and avoid having to implement the protocol manually.
App Lab Project Structure
A hybrid App Lab application normally separates the Linux and MCU code.
A simplified structure looks like:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
my_app/ │ ├── python/ │ └── main.py │ ├── sketch/ │ └── sketch.ino │ └── optional Bricks / application metadata |
The Python program runs on Debian. The sketch is compiled for the STM32U585.
The two parts do not share variables directly. Communication happens through Bridge calls.
Your First Hybrid App: Linux Controls an MCU Output
A simple way to understand App Lab is to let Python send a command to the STM32.
We will expose an MCU function named set_output. Python will call it once per second.
STM32 Sketch
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <Arduino_RouterBridge.h> void setOutput(bool enabled) { digitalWrite(LED_BUILTIN, enabled ? LOW : HIGH); } void setup() { pinMode(LED_BUILTIN, OUTPUT); Bridge.begin(); Bridge.provide_safe("set_output", setOutput); } void loop() { // Main real-time application can continue here. } |
The important line is:
|
1 2 3 4 |
Bridge.provide_safe("set_output", setOutput); |
This publishes the C++ function under an RPC service name that Linux can call.
Linux Python Code
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import time from arduino.app_utils import App, Bridge state = False def loop(): global state state = not state Bridge.call("set_output", state) time.sleep(1) App.run(user_loop=loop) |
The Python code does not manipulate the GPIO register itself. It asks the MCU to perform the hardware operation.
This is the core App Lab pattern:
|
1 2 3 4 5 6 7 8 9 10 |
Python decision ↓ Bridge.call(...) ↓ STM32 function ↓ physical hardware |
Bridge.begin()
The MCU sketch must initialise Bridge before using RPC:
|
1 2 3 4 |
Bridge.begin(); |
This connects the STM32-side Bridge library to the communication infrastructure managed by the Router.
Normally this belongs in setup().
Bridge.provide()
Bridge.provide() publishes an MCU function so another Bridge client can call it.
Example:
|
1 2 3 4 |
Bridge.provide("read_fast_counter", getCounter); |
Functions registered with provide() execute inside the Bridge’s high-priority RPC handling context.
That is useful for very short, thread-safe functions, but it is not the safest choice when the callback calls normal Arduino functions or shares data with the main application.
Bridge.provide_safe()
For most user-facing hardware functions, provide_safe() is the safer option.
|
1 2 3 4 |
Bridge.provide_safe("set_relay", setRelay); |
Instead of executing the function directly inside the background RPC thread, Bridge arranges for the callback to run in the normal main-loop context.
This is particularly useful when the callback uses functions such as:
digitalWrite();analogWrite();Serial.print();- libraries that are not designed for concurrent access.
For normal App Lab projects, using provide_safe() for hardware-control callbacks is a sensible default.
Avoid Nested Communication Inside provide()
One important Bridge rule is that a function running inside a normal provide() callback should not start another blocking communication transaction.
For example, avoid designs such as:
|
1 2 3 4 5 6 7 8 |
Linux calls MCU callback ↓ MCU callback immediately performs another Bridge.call() ↓ communication waits on itself |
This can create deadlocks.
Keep direct provide() callbacks short. If the function needs normal Arduino APIs or more complicated application logic, use provide_safe() or pass the request into your normal program flow.
Bridge.call()
Bridge.call() is used when the caller expects a remote function to run and return a result.
Conceptually:
|
1 2 3 4 |
temperature = Bridge.call("get_temperature") |
The caller sends the method name and any arguments, waits for the remote service and receives the returned value.
This is convenient for requests such as:
- read a sensor;
- get a configuration value;
- request a calculation;
- change an output and confirm the operation;
- ask Linux for a service result.
Bridge.notify()
notify() is the fire-and-forget form of RPC.
It sends a remote invocation without waiting for the result.
This is useful for:
- event notifications;
- telemetry messages;
- status updates;
- commands where no response is required.
For high-frequency data, avoiding unnecessary blocking responses can make the architecture more efficient.
RPC Can Work in Both Directions
Bridge is not limited to Linux commanding the MCU.
The STM32 can call services running on Linux.
This is particularly useful when the MCU needs something that is naturally provided by the Linux environment, such as:
- network access;
- filesystem operations;
- database storage;
- AI inference;
- HTTP requests;
- complex calculations;
- system time or external services.
A useful architecture might be:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
STM32 reads sensor ↓ threshold exceeded ↓ MCU calls Linux service ↓ Linux stores event in database ↓ Linux sends MQTT / HTTP notification |
The real-time side remains small and predictable while Linux handles the software-heavy part.
Linux-to-Linux RPC
The Arduino Router can also route calls between Linux applications.
This means the MCU does not have to participate in every RPC exchange.
For example:
|
1 2 3 4 5 6 7 8 |
Python camera process ↓ Arduino Router ↓ C++ control application |
This service-oriented approach can help divide a large application into separate components without forcing every process to implement its own custom communication layer.
What Are Bricks?
Bricks are reusable Linux-side components that App Lab can deploy alongside your own application.
A Brick can package functionality such as:
- an AI model;
- object classification;
- keyword spotting;
- a database;
- a web interface;
- a REST service;
- integration with an external data source.
The objective is to avoid rebuilding common infrastructure every time.
A typical workflow is:
- Create an App.
- Select any Bricks required by the project.
- Add your Linux Python code.
- Add your MCU sketch if hardware control is needed.
- Import and initialise the selected Brick in the Python application.
- Press Run.
App Lab then deploys the different components as one application.
Example: AI Camera + Motor Control
A practical UNO Q project might use:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
USB or MIPI camera ↓ Linux camera application ↓ AI Brick ↓ object position ↓ Bridge.call("steer", angle) ↓ STM32U585 ↓ PWM / motor driver |
Linux does the expensive image and AI processing. The MCU performs the real-time output control.
This is exactly the type of workload the UNO Q architecture is designed for.
Example: Smart Environmental Controller
Another project could divide the work like this:
STM32
- read temperature and humidity;
- measure fan RPM;
- control PWM fan speed;
- monitor alarm contacts;
- continue safe local control even if the Linux application restarts.
Linux
- store historical data;
- host a dashboard;
- publish MQTT;
- calculate long-term trends;
- send email or push alerts;
- download configuration updates.
This separation makes the overall system much more robust than asking one Python process to handle every hardware detail.
App Lab PC-Hosted Mode
Arduino App Lab can run on your computer and control the UNO Q remotely or over its development connection.
This is convenient because:
- the editor runs on a faster desktop or laptop;
- you can use your normal keyboard and monitor;
- the UNO Q remains dedicated to running the application;
- the project still deploys Linux and MCU components together.
For initial development, this will often be the most comfortable arrangement.
App Lab in SBC Mode
App Lab is also available directly on the UNO Q when the board is used as a standalone computer.
Connect the board to a suitable display and input devices and the development environment can run on the board itself.
Arduino recommends the 4 GB UNO Q variant for the best standalone App Lab experience, particularly for applications that use significant Linux memory.
Network Mode
App Lab can discover an UNO Q on the local network and connect to it remotely.
This allows the board to remain installed in a project while development continues from another computer on the same network.
This is useful for:
- robots;
- test rigs;
- wall-mounted controllers;
- remote sensor systems;
- projects where repeatedly connecting USB is inconvenient.
Arduino IDE vs Arduino App Lab
| Feature | Arduino IDE | Arduino App Lab |
|---|---|---|
| Program STM32U585 | Yes | Yes |
| Run Linux Python | No | Yes |
| Manage Linux application | No | Yes |
| Use App Bricks | No | Yes |
| Deploy complete Linux + MCU App | No | Yes |
| Simple Arduino-only sketch | Excellent | Yes |
| Hybrid UNO Q application | Limited | Recommended |
If your UNO Q project is only using the STM32 as a normal Arduino, Arduino IDE is perfectly valid.
If you bought the UNO Q specifically for Linux, AI or hybrid processing, App Lab is the environment that exposes the complete architecture.
Serial Debugging in Current UNO Q Software
Early UNO Q software used a Bridge-based Monitor object for MCU console output.
Current UNO Q board-platform releases support familiar Arduino serial debugging in App Lab:
|
1 2 3 4 5 6 7 8 9 10 11 |
void setup() { Serial.begin(115200); } void loop() { Serial.println("UNO Q MCU is alive"); delay(1000); } |
The older Monitor API remains available for compatibility with existing projects, but standard Serial is the better choice for new sketches.
Debugging the Arduino Router
If Bridge communication stops working, check the Router service from the Linux terminal.
Service status:
|
1 2 3 4 |
systemctl status arduino-router |
Restart it:
|
1 2 3 4 |
sudo systemctl restart arduino-router |
Follow its logs:
|
1 2 3 4 |
journalctl -u arduino-router -f |
These commands are useful when the MCU sketch appears to run correctly but Linux RPC calls are not reaching it.
Common Bridge/RPC Problems
1. Bridge.begin() Was Never Called
If the MCU sketch does not initialise Bridge, the published RPC functions will not be available.
2. Function Name Does Not Match
The string used by the caller must match the name registered by the provider.
|
1 2 3 4 5 6 |
Bridge.provide_safe("motor_speed", setMotor); Bridge.call("motor_speed", 100); |
A typo simply results in a service that cannot be found or invoked correctly.
3. Using provide() for Unsafe Arduino Calls
A callback registered through provide() runs in the background RPC context. If it interacts with libraries that expect the normal Arduino loop context, use provide_safe() instead.
4. Blocking the MCU for Too Long
If an RPC callback performs a long operation, other real-time work can suffer.
The better design is often:
|
1 2 3 4 5 6 7 8 9 10 |
RPC callback ↓ store requested state ↓ return quickly ↓ main loop performs operation |
5. Opening /dev/ttyHS1 Manually
The Router owns this interface. A custom Linux program trying to use it directly can interfere with Bridge communication.
6. Treating RPC Like a High-Speed Streaming Bus
RPC is excellent for commands, structured requests and moderate-rate telemetry. It is not automatically the right transport for every sample of a very high-rate data stream.
For fast acquisition, buffer or preprocess data on the MCU and transfer meaningful blocks or summaries rather than making thousands of tiny blocking calls.
Design RPC Functions as Services
A clean UNO Q architecture exposes meaningful operations rather than individual pin manipulations.
Instead of:
|
1 2 3 4 5 6 |
set_pin_5_high() set_pin_6_low() set_pwm_pin_9(127) |
prefer something closer to:
|
1 2 3 4 5 6 7 |
set_motor_speed(left, right) set_fan_target_rpm(1500) read_environment() stop_machine() |
This keeps the Linux application independent of the exact STM32 pin mapping and makes the interface easier to maintain.
Keep Safety Logic on the MCU
If the UNO Q controls physical equipment, do not make the Linux application the only layer responsible for immediate safety behaviour.
For example, a motor controller should not require a successful Python RPC call to stop after an encoder fault.
A better architecture is:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
STM32: local limits over-current response watchdog emergency stop safe default state Linux: high-level target navigation AI logging user interface |
The system can then remain safe even if Linux is rebooting, an application crashes or networking fails.
Use Linux for What Linux Is Good At
A common mistake with powerful hybrid boards is to move every task onto the application processor simply because it is faster.
The QRB2210 is the correct place for:
- complex algorithms;
- large libraries;
- data processing;
- files;
- network communication;
- AI models;
- user-facing services.
It is not automatically the correct place for a 20 kHz motor-control loop.
Use the MCU for What MCUs Are Good At
The STM32U585 should remain responsible for operations where timing, simplicity and direct peripheral access matter.
This gives the UNO Q something a Linux-only SBC cannot easily provide: a real microcontroller control plane directly integrated into the same board.
App Lab Bricks and AI
One of App Lab’s distinguishing features is its Brick system.
Instead of manually assembling every part of an AI pipeline, a Brick can package a ready-to-use capability and expose a Python API to the application.
A project can therefore evolve into:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
Camera ↓ AI Brick ↓ Python decision logic ↓ Bridge RPC ↓ STM32 actuator control |
This reduces the amount of infrastructure code required to combine machine learning with hardware.
UNO Q App Lab vs a Traditional Arduino Sketch
A traditional Arduino sketch usually contains the complete application:
|
1 2 3 4 5 6 7 8 9 |
setup() loop() sensors outputs communications application logic |
An UNO Q App can be more modular:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Linux application ├── networking ├── AI ├── database ├── web UI └── high-level state machine STM32 sketch ├── sensors ├── motors ├── PWM ├── CAN └── safety control Bridge └── service interface between them |
This architecture scales much better as projects become more complex.
When App Lab Is Worth Using
App Lab makes the most sense when a project combines at least two of the UNO Q’s major domains.
Examples include:
- Python + Arduino hardware;
- AI + motors;
- camera + GPIO;
- database + sensors;
- web dashboard + real-time control;
- Linux networking + CAN;
- computer vision + robotics;
- Bricks + physical actuators.
When Arduino IDE Alone Is Enough
If your project is simply:
- read an I2C sensor;
- control a relay;
- generate PWM;
- use CAN;
- run a normal embedded sketch;
then there is nothing wrong with using Arduino IDE and ignoring the Linux side initially.
The UNO Q’s STM32U585 is already a powerful microcontroller.
But using only the MCU also means you are leaving the QRB2210, Linux, RAM, eMMC and application ecosystem unused.
Practical Project Architecture Example
Suppose you are building a vision-guided sorting machine.
Linux / QRB2210
- capture camera frames;
- run object classification;
- maintain production statistics;
- host the operator dashboard;
- save images of rejected parts;
- send statistics to a remote server.
STM32U585
- read optical sensors;
- control conveyor PWM;
- measure encoder speed;
- fire pneumatic outputs at precise positions;
- implement emergency-stop behaviour;
- maintain safe outputs if Linux stops responding.
Bridge Interface
|
1 2 3 4 5 6 7 8 |
set_conveyor_speed(speed) reject_next_part(lane) get_machine_state() set_run_mode(mode) report_fault(code) |
That is a much cleaner design than making Python toggle individual GPIO pins while also performing image recognition.
App Lab and the Wider UNO Q Ecosystem
App Lab is the software layer that makes the UNO Q’s unusual hardware architecture practical.
Without it, developers would need to manage:
- Linux application deployment;
- MCU compilation and flashing;
- inter-processor messaging;
- AI components;
- application startup;
- multiple development environments.
App Lab does not remove the fact that there are two processors, but it makes the boundary between them much easier to work with.
If you are still deciding whether UNO Q is the right board for your project, our UNO Q vs UNO R4 WiFi comparison, UNO Q vs GIGA R1 WiFi comparison and UNO Q vs Portenta X8 comparison explain where it fits in the wider Arduino range.
Final Thoughts
Arduino App Lab is the piece that turns the UNO Q from “a Linux processor and an STM32 on the same PCB” into one coherent development platform.
The core design principle is simple:
- run complex software on the QRB2210 under Debian;
- run deterministic hardware control on the STM32U585;
- connect the two through Bridge/RPC;
- use Bricks when a reusable Linux or AI component already exists.
The Arduino Router underneath Bridge gives the system a service-oriented communication layer rather than a fragile collection of custom serial messages. Linux applications can call MCU functions, the MCU can use Linux services, and multiple Linux applications can participate in the same RPC environment.
Once that architecture is understood, UNO Q development becomes much easier. Do not ask which processor should run the entire project. Split the application according to what each processor does best, then use Bridge to define a clean interface between them.
That is the real purpose of App Lab: treating Linux software, Arduino firmware and AI components as parts of the same application rather than as separate projects that happen to share a board.