| name | arduino-unoq-applab |
| description | Comprehensive guide for adapting applications for the Arduino UNO Q, integrating them into the Arduino App Lab ecosystem as apps and bricks, deploying Edge Impulse ML models, and building Flask-based web UIs. Use this skill when users ask about Arduino UNO Q development, App Lab apps/bricks, ML model deployment, or converting existing projects into App Lab-compatible applications. |
| argument-hint | ["topic or task"] |
| allowed-tools | Read, Grep, Glob, WebFetch, Write, Edit, Bash |
Arduino UNO Q & Arduino App Lab Development Guide
Table of Contents
- Arduino UNO Q Hardware
- Arduino App Lab Overview
- App Structure & Configuration
- Bricks System
- App Lab CLI Reference
- Bridge: MCU-MPU Communication
- Deploying Edge Impulse ML Models
- Building Flask Web Apps for App Lab
- Converting Existing Projects to App Lab Apps
- Camera & Video Handling on UNO Q
- Storage & Dependency Management
- Reference Example: Object Detection with Flask
- Quick Reference Commands
Arduino UNO Q Hardware
Overview
The Arduino UNO Q is a hybrid board combining a powerful MPU and MCU in the classic UNO form factor. It is the first product from the Qualcomm-Arduino partnership.
Specifications
Main Processor (MPU) - Qualcomm Dragonwing QRB2210:
- Quad-core Arm Cortex-A53 at 2.0 GHz
- Adreno 702 GPU (supports ML inference and 3D graphics)
- Dual 13MP Image Signal Processors (ISPs) - supports 2 cameras simultaneously (or single 25MP at 30fps)
- Runs Debian-based Linux
- AI inference on CPU and GPU
Microcontroller (MCU) - STM32U585:
- Arm Cortex-M33 up to 160 MHz
- 2 MB flash
- 786 KB SRAM
- Integrated floating-point unit
- Runs Arduino Code on Zephyr RTOS
SKU Variants:
| Variant | RAM | eMMC Storage | SKU |
|---|
| Standard | 2 GB | 16 GB | ABX00162 |
| Extended | 4 GB | 32 GB | ABX00173 |
Connectivity:
- Wi-Fi 5 Dual-band 2.4/5 GHz (integrated antenna)
- Bluetooth 5.1 (onboard antenna)
- WCBN3536A wireless module
- USB-C: power (5V 3A / 7-24 VDC barrel), DisplayPort/HDMI, USB hub
I/O:
- 47 digital I/O pins, 6x 14-bit analog inputs, 2x 12-bit DACs, 6x PWM
- 3x USART, 2x UART, 4x I2C, 3x SPI, CAN-FD
- Qwiic connector for Modulino nodes
- Compatible with classic UNO shields
- MIPI-CSI camera and MIPI-DSI display connectors
- Dual RGB LEDs, 8x13 LED matrix
Dimensions: 53.34 mm x 68.58 mm
Usage Modes
- Desktop (Tethered): USB-connected to PC running Arduino App Lab
- Network: Over local Wi-Fi (requires initial USB setup)
- Standalone (SBC): Connect monitor + keyboard + mouse via USB-C hub, runs as single-board computer (recommended with 4GB variant)
Key Filesystem Paths on the Board
/home/arduino/ # User home directory
/home/arduino/ArduinoApps/ # All App Lab applications
/home/arduino/ArduinoApps/<app-name>/ # Individual app directory
/home/arduino/.arduino-bricks/ # Installed bricks
/home/arduino/.arduino-bricks/ei-models/ # Edge Impulse model storage
Arduino App Lab Overview
Arduino App Lab is the unified IDE preloaded on the UNO Q. It combines Arduino Sketches, Python scripts, and containerized AI models in one interface.
Key Features
- Unified development for Linux (Python) and MCU (Arduino C++)
- Pre-loaded AI model bricks: object detection, anomaly detection, image classification, sound recognition, keyword spotting
- Automatic library dependency installation
- Web-based IDE accessible via
http://<device-ip>:7000 or USB
- Docker-based brick containers for AI workloads
- Bridge for MCU-MPU inter-process communication
Platform Compatibility (for App Lab desktop client)
- Windows 10+ (64-bit)
- macOS 11+ (64-bit)
- Ubuntu 22.04+
- Debian Trixie (64-bit)
Connection Methods
- USB: Direct connection, first-time setup
- Wi-Fi: After initial USB setup, access via
http://<boardname>.local:7000
- SSH:
ssh arduino@<boardname>.local or ssh arduino@<device-ip>
- ADB:
adb shell for low-level access
App Structure & Configuration
App Directory Layout
/home/arduino/ArduinoApps/<app-name>/
+-- app.yaml # App metadata, brick dependencies, ports (read-only in IDE)
+-- main.py # Primary Python entry point (runs on Linux MPU)
+-- sketch/
| +-- sketch.ino # MCU code (runs on STM32, optional)
+-- README.md # Documentation (optional)
+-- models/ # ML model files (optional, for custom apps)
+-- assets/ # Images, videos, static files (optional)
+-- python/ # Additional Python modules (optional)
Apps can be:
- Python only:
main.py runs on the Linux MPU
- Sketch only:
sketch.ino runs on the MCU
- Both: Python + Sketch communicating via Bridge
Sketch Configuration (sketch/sketch.yaml)
When including an Arduino sketch, you must create a sketch.yaml file to specify the board profile and libraries with version numbers:
profiles:
default:
platforms:
- platform: arduino:zephyr
libraries:
- Arduino_RouterBridge (0.2.2)
- ArduinoGraphics (1.1.4)
- dependency: Arduino_RPClite (0.2.0)
- dependency: ArxContainer (0.7.0)
- dependency: ArxTypeTraits (0.3.2)
- dependency: DebugLog (0.8.4)
- dependency: MsgPack (0.4.2)
default_profile: default
Key points:
- Library names MUST include version in parentheses:
LibraryName (x.y.z)
- Use
dependency: prefix for transitive dependencies
- No
fqbn needed - platform: arduino:zephyr is sufficient
Arduino_LED_Matrix is bundled with the Zephyr core (no need to list)
- Without version numbers, you'll get "Library not found" errors
app.yaml Configuration
name: My Application
description: "What this app does"
icon: "icon-emoji-here"
ports: [5001]
bricks: []
App Execution Model
- Only ONE app can run per board at a time
App.run() must appear at the end of main.py when using bricks
- Code after
App.run() will NOT execute
- Sketch compilation occurs at launch (can take up to a minute)
- For standalone Flask apps (without bricks), just run
python3 main.py
Bricks System
What Are Bricks?
Bricks are reusable, modular software components that launch as separate processes alongside apps. They can be Python modules or Docker containers providing specialized functionality.
Key Characteristics
- Launch in parallel with the app
- Can be AI models (Docker containers), web servers, API connectors
- Multiple bricks can run simultaneously
- Each brick has its own API documentation in the IDE
- Imported as Python modules in
main.py
IMPORTANT: Always prefer using a brick when one exists for the task. Bricks are the standard App Lab pattern and provide tested, containerized functionality. Only fall back to direct SDK usage (e.g., edge_impulse_linux.image.ImageImpulseRunner) when no suitable brick is available. If no brick exists for a common capability, consider whether it makes sense to build a new brick — this packages the functionality as a reusable component that other developers can benefit from in the App Lab ecosystem.
Available Brick Categories
| Category | Description |
|---|
| AI - Audio | Sound classification, keyword spotting |
| AI - Computer Vision | Object detection, image classification, anomaly detection |
| AI - Sensor data | Motion detection, sensor analysis |
| API | REST API handlers, external data sources |
| IoT | IoT connectivity and protocols |
| Storage | Data persistence |
| Web User Interface | Web UI hosting via WebSockets at port 7000 |
Pre-loaded AI Bricks
| Brick | Import | Use Case |
|---|
video_object_detection | from arduino.app_bricks.video_objectdetection import VideoObjectDetection | Detect objects using camera (YoloX Nano default) |
ei_keyword_spotting | from arduino.app_bricks.keyword_spotting import KeywordSpotting | Voice command recognition |
ei_image_classification | from arduino.app_bricks.image_classification import ImageClassification | Classify images |
ei_anomaly_detection | from arduino.app_bricks.anomaly_detection import AnomalyDetection | Visual anomaly detection |
ei_sound_classification | from arduino.app_bricks.sound_classification import SoundClassification | Audio classification |
motion_detection | from arduino.app_bricks.motion_detection import MotionDetection | Sensor-based motion detection |
web_ui | from arduino.app_bricks.web_ui import WebUI | Web UI hosting (port 7000) |
Using Bricks in Code
from arduino.app_bricks.web_ui import WebUI
from arduino.app_bricks.video_objectdetection import VideoObjectDetection
web = WebUI()
detector = VideoObjectDetection(confidence=0.7)
def handle_detections(detections):
best_label = max(detections, key=detections.get)
print(f"Detected: {best_label} ({detections[best_label]:.0%})")
detector.on_detect_all(handle_detections)
App.run()
VideoObjectDetection Brick API
| Method | Description |
|---|
VideoObjectDetection(confidence=0.3, debounce_sec=2.0) | Constructor. confidence sets the detection threshold, debounce_sec sets minimum seconds between repeated detections of the same object |
on_detect(label, callback) | Register a callback for a specific object class. Callback takes no arguments |
on_detect_all(callback) | Register a callback for all detections. Callback receives a dict mapping labels to confidence scores |
override_threshold(value) | Change the confidence threshold at runtime |
start() / stop() | Control the detection lifecycle |
The brick manages the USB camera and runs inference in a Docker container. When using a custom Edge Impulse model, set EI_OBJ_DETECTION_MODEL in app.yaml bricks variables.
Edge Impulse Model Variables
When using AI bricks with custom Edge Impulse models, set these in app.yaml:
| Variable | Use Case |
|---|
EI_OBJ_DETECTION_MODEL | Object detection |
EI_CLASSIFICATION_MODEL | General classification |
EI_IMAGE_CLASSIFICATION_MODEL | Image classification |
EI_KEYWORD_SPOTTING_MODEL | Keyword/voice spotting |
EI_AUDIO_CLASSIFICATION_MODEL | Audio classification |
EI_MOTION_DETECTION_MODEL | Motion/sensor detection |
EI_V_ANOMALY_DETECTION_MODEL | Visual anomaly detection |
web_ui Brick Details
- Serves HTML/CSS/JS from an
assets folder
- Communicates with Python via WebSockets
- Available at
http://localhost:7000
- Use this brick when you want a simple UI tightly integrated with App Lab
App Lab CLI Reference
The arduino-app-cli is pre-installed on the Arduino UNO Q for managing apps without the desktop IDE.
App Management
arduino-app-cli app new "my-app"
arduino-app-cli app start "/home/arduino/ArduinoApps/my-app"
arduino-app-cli app start user:my-app
arduino-app-cli app start examples:blink
arduino-app-cli app stop "/home/arduino/ArduinoApps/my-app"
arduino-app-cli app list
arduino-app-cli app logs /home/arduino/ArduinoApps/my-app --all
Brick Management
arduino-app-cli brick list
arduino-app-cli brick details arduino:<brick-name>
System Commands
arduino-app-cli system update
arduino-app-cli system set-name "my-board"
arduino-app-cli system network enable
arduino-app-cli system network disable
arduino-app-cli system cleanup
Bridge: MCU-MPU Communication
The Bridge enables bidirectional communication between the MCU (sketch.ino) and MPU (main.py) using MessagePack RPC over UART (Serial1 at 115200 baud).
Architecture
- Router: Hosted on the MPU as the
Arduino-Router service
- Topology: Star network with the Router as the central hub
- Protocol: MessagePack RPC with frame format
[type, id, method, parameters]
Arduino Side (MCU) - Arduino_RouterBridge Library
#include <Arduino_RouterBridge.h>
void setup() {
Bridge.begin();
Bridge.provide("my_function", myHandler);
Bridge.provide_safe("set_led", setLedHandler);
}
void myHandler(String data) {
Serial.println("Received: " + data);
}
void setLedHandler(bool state) {
digitalWrite(LED_BUILTIN, state ? HIGH : LOW);
}
void loop() {
int result = 0;
Bridge.call("python_function", 42).result(result);
Bridge.notify("sensor_data", analogRead(A0));
delay(100);
}
Python Side (MPU)
from arduino.bridge import Bridge
bridge = Bridge()
@bridge.on_call("python_function")
def handle_call(value):
return value * 2
@bridge.on_notify("sensor_data")
def handle_sensor(value):
print(f"Sensor: {value}")
bridge.call("my_function", "Hello from Python")
bridge.notify("set_led", True)
App.run()
Key Methods
| Method | Direction | Description |
|---|
provide(name, handler) | MCU | Expose function for Python to call |
provide_safe(name, handler) | MCU | Thread-safe version for GPIO operations |
call(name, args...) | Both | Synchronous call, waits for response |
notify(name, args...) | Both | Fire-and-forget, no response |
Best Practices
- Use
provide_safe() for handlers that use digitalWrite(), analogRead(), etc.
- Use
notify() for telemetry/status updates (faster than call())
- Use
call() when you need a return value
- Check
call().result(var) return value - returns false on failure
LED Matrix Control (8x13 Built-in Display)
The Arduino UNO Q has a built-in 8x13 LED matrix (monochrome) controlled via the Arduino_LED_Matrix library.
Required Libraries
#include <ArduinoGraphics.h>
#include <Arduino_LED_Matrix.h>
Basic Setup
ArduinoLEDMatrix matrix;
void setup() {
matrix.begin();
}
Core Methods
| Method | Description |
|---|
matrix.begin() | Initialize the LED matrix |
matrix.beginDraw() / matrix.endDraw() | Frame drawing operations |
matrix.clear() | Clear all pixels |
matrix.set(x, y, r, g, b) | Set pixel (any non-zero = on) |
matrix.stroke(color) | Set drawing color |
Text Display Methods (with ArduinoGraphics)
| Method | Description |
|---|
matrix.textFont(Font_4x6) | Set font (Font_4x6, Font_5x7) |
matrix.textScrollSpeed(ms) | Set scroll speed in milliseconds |
matrix.beginText(x, y, color) | Start text at position |
matrix.println(text) | Print text |
matrix.endText(SCROLL_LEFT) | End text with scroll direction |
Example: Scrolling Text Display
#include <ArduinoGraphics.h>
#include <Arduino_LED_Matrix.h>
ArduinoLEDMatrix matrix;
void displayText(String text) {
matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
matrix.textScrollSpeed(80);
matrix.textFont(Font_4x6);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println(text);
matrix.endText(SCROLL_LEFT);
matrix.endDraw();
}
void setup() {
matrix.begin();
displayText("Hello!");
}
void loop() {
delay(100);
}
Scroll Directions
SCROLL_LEFT - Text scrolls from right to left
SCROLL_RIGHT - Text scrolls from left to right
Complete Example: Python to LED Matrix via Bridge
This example shows how to send OCR results (or any text) from Python to the MCU's LED matrix.
Arduino Sketch (sketch/sketch.ino)
#include <Arduino_RouterBridge.h>
#include <ArduinoGraphics.h>
#include <Arduino_LED_Matrix.h>
ArduinoLEDMatrix matrix;
String currentText = "";
bool newTextAvailable = false;
void displayText(String text) {
if (text.length() > 0 && text != currentText) {
currentText = text;
newTextAvailable = true;
}
}
void setup() {
Serial.begin(115200);
matrix.begin();
Bridge.begin();
Bridge.provide("display_text", displayText);
matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
matrix.textFont(Font_4x6);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println("Ready");
matrix.endText(SCROLL_LEFT);
matrix.endDraw();
}
void loop() {
if (newTextAvailable) {
matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
matrix.textScrollSpeed(80);
matrix.textFont(Font_4x6);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println(currentText);
matrix.endText(SCROLL_LEFT);
matrix.endDraw();
newTextAvailable = false;
}
delay(50);
}
Python Code (main.py or web_inference.py)
_bridge = None
_last_text = ""
try:
from arduino.bridge import Bridge
_bridge = Bridge()
except ImportError:
pass
def send_to_led_matrix(text: str) -> None:
"""Send text to the MCU's LED matrix display."""
global _last_text
if _bridge and text and text != _last_text:
try:
_bridge.notify("display_text", text)
_last_text = text
except Exception as e:
print(f"Bridge error: {e}")
Deploying Edge Impulse ML Models
Step 1: Export from Edge Impulse Studio
- Train your impulse in Edge Impulse Studio
- Go to Deployment
- Select the target hardware as Arduino UNO Q
- Export as
.eim file for:
- "Arduino UNO Q" (CPU inference)
- "Linux aarch64" (CPU inference)
- "Linux Arduino UNO Q (GPU)" (GPU-accelerated inference via Adreno 702)
Step 2: Transfer Model to Device
https://docs.edgeimpulse.com/hardware/deployments/run-arduino-app-lab#download-and-select-the-new-custom-model
scp your-model.eim arduino@<device-ip>:/home/arduino/.arduino-bricks/ei-models/
Step 3: Make Model Executable
chmod +x /home/arduino/.arduino-bricks/ei-models/*.eim
chmod +x /home/arduino/ArduinoApps/my-app/models/*.eim
Step 4: Run
arduino-app-cli app start user:my-app
cd /home/arduino/ArduinoApps/my-app
python3 main.py
source .venv/bin/activate && python3 main.py
Using Edge Impulse SDK Directly (Without Bricks)
For custom Flask apps that bypass the brick system:
from edge_impulse_linux.image import ImageImpulseRunner
runner = ImageImpulseRunner('./models/model.eim')
model_info = runner.init()
features = frame_rgb.flatten().tolist()
result = runner.classify(features)
runner.stop()
Building Flask Web Apps for App Lab
Architecture Pattern
For custom web UIs (instead of the web_ui brick), use Flask on port 5001:
app-name/
+-- app.yaml # ports: [5001], bricks: []
+-- python/
| +-- main.py # Flask app entry point
| +-- requirements.txt # Python dependencies
| +-- templates/
| | +-- index.html # Web UI
| | +-- assets/ # CSS, JS, images
| +-- utils/
| +-- mock_dependencies.py # Dependency mocks (optional)
+-- models/ # .eim model files
+-- assets/ # Images, videos for inference
app.yaml for Flask Apps
name: My Custom Detection App
description: "Real-time detection with Flask web UI"
icon: "icon-emoji-here"
ports: [5001]
bricks: []
Mock Dependencies (Required for edge_impulse_linux)
Create python/utils/mock_dependencies.py to avoid pyaudio/six import issues:
import sys
import builtins
def apply_mocks():
"""Apply mocks for six and pyaudio to avoid dependency issues."""
class MockSixMovesQueue:
Queue = None
class MockSixMoves:
queue = MockSixMovesQueue()
class MockSix:
moves = MockSixMoves()
sys.modules['six'] = MockSix()
sys.modules['six.moves'] = MockSixMoves()
sys.modules['six.moves.queue'] = MockSixMovesQueue()
class MockPyAudio:
pass
sys.modules['pyaudio'] = MockPyAudio()
builtins.pyaudio = MockPyAudio()
Call apply_mocks() at the top of main.py BEFORE importing edge_impulse_linux.
Requirements.txt for Flask Apps
edge-impulse-linux==1.2.2
opencv-python-headless
flask
numpy
requests
IMPORTANT: Use opencv-python-headless (not opencv-python) on the UNO Q - the headless variant is smaller and doesn't need GUI libraries.
Flask App Template
"""
Flask web application for Edge Impulse inference on Arduino UNO Q.
"""
from utils.mock_dependencies import apply_mocks
apply_mocks()
import cv2
import numpy as np
from flask import Flask, render_template, Response, jsonify, request
from edge_impulse_linux.image import ImageImpulseRunner
app = Flask(__name__)
runner = ImageImpulseRunner('./models/model.eim')
model_info = runner.init()
@app.route('/')
def index():
return render_template('index.html')
@app.route('/video_feed')
def video_feed():
"""MJPEG video stream endpoint."""
def generate():
cap = cv2.VideoCapture(2, cv2.CAP_V4L2)
while True:
ret, frame = cap.read()
if not ret:
break
_, jpeg = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 80])
yield (b'--frame\r\nContent-Type: image/jpeg\r\n\r\n' + jpeg.tobytes() + b'\r\n')
return Response(generate(), mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001, threaded=True)
Converting Existing Projects to App Lab Apps
When adapting an existing Python project to run as an Arduino App Lab application, follow these steps:
1. Create App Structure
arduino-app-cli app new "my-app"
mkdir -p /home/arduino/ArduinoApps/my-app
2. Create app.yaml
name: My Converted App
description: "Adapted from existing project"
icon: "icon-emoji-here"
ports: [5001]
bricks: []
3. Set Up Python Environment
cd /home/arduino/ArduinoApps/my-app
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
4. Adapt the Code
Key changes needed when converting:
- Port: Use port 5001 (port 7000 is reserved for App Lab IDE)
- Host: Bind to
0.0.0.0 (not localhost) so it's accessible over the network
- Camera: Use V4L2 backend and correct device index (see Camera section below)
- Models: Ensure
.eim files are chmod +x and use linux-aarch64 architecture
- OpenCV: Use
opencv-python-headless instead of opencv-python
- Dependencies: Add
mock_dependencies.py for edge_impulse_linux compatibility
- Python version: Python 3.10+ syntax (e.g.,
str | None) is supported on the UNO Q
5. Launch with App Lab CLI
arduino-app-cli app start user:my-app
cd /home/arduino/ArduinoApps/my-app
source .venv/bin/activate
python3 python/main.py
6. Access the Web UI
Open http://<device-ip>:5001 from any browser on the same network.
Camera & Video Handling on UNO Q
Video Device Mapping
The UNO Q exposes multiple /dev/video* devices. Not all are cameras:
| Device | Type | Description |
|---|
/dev/video0 | Encoder | Qualcomm Venus video encoder (NOT a camera) |
/dev/video1 | Decoder | Qualcomm Venus video decoder (NOT a camera) |
/dev/video2 | RGB Camera | External USB camera main stream (e.g., Logitech BRIO) |
/dev/video3 | Metadata | Camera metadata stream |
/dev/video4 | IR Camera | Infrared stream (if camera supports it) |
/dev/video5 | Metadata | IR metadata stream |
Camera Detection Best Practices
- Always filter out Venus encoder/decoder devices - they show as video devices but are NOT cameras
- Use V4L2 backend on Linux:
cv2.VideoCapture(index, cv2.CAP_V4L2)
- Check device info with
v4l2-ctl --device /dev/videoN --info to identify real cameras
- Identify IR cameras - some cameras (like Logitech BRIO) expose both RGB and IR streams
- Use
/dev/video2 as the typical default for the first USB camera's RGB stream
Camera Detection Code Pattern
import subprocess
import cv2
import glob
def detect_cameras():
"""Detect real cameras, filtering out encoders/decoders."""
cameras = []
for device in sorted(glob.glob('/dev/video*')):
index = int(device.replace('/dev/video', ''))
try:
result = subprocess.run(
['v4l2-ctl', '--device', device, '--info'],
capture_output=True, text=True, timeout=2
)
output = result.stdout
card_name = None
for line in output.split('\n'):
if 'Card type' in line:
card_name = line.split(':', 1)[1].strip()
if card_name and ('venus' in card_name.lower() or
'encoder' in card_name.lower() or
'decoder' in card_name.lower()):
continue
if 'Video Capture' not in output:
continue
cap = cv2.VideoCapture(index, cv2.CAP_V4L2)
if cap.isOpened():
ret, frame = cap.read()
cap.release()
if ret and frame is not None:
cameras.append({'index': index, 'name': card_name or f'Camera {index}'})
except Exception:
continue
return cameras
MJPEG Format for Performance
cap = cv2.VideoCapture(2, cv2.CAP_V4L2)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
Storage & Dependency Management
The Arduino UNO Q has limited storage (16GB or 32GB eMMC). The /home/arduino partition is typically 3.6GB.
Freeing Space
df -h
du -sh /home/arduino/* | sort -rh | head -20
pip cache purge
sudo apt clean && sudo apt autoremove -y
docker system prune -a
arduino-app-cli system cleanup
sudo journalctl --vacuum-size=50M
If /home Is Full, Use Root Partition
sudo mkdir -p /opt/app-venv
sudo chown arduino:arduino /opt/app-venv
python3 -m venv /opt/app-venv
source /opt/app-venv/bin/activate
pip install -r requirements.txt
Dependency Tips
- Use
opencv-python-headless instead of opencv-python (much smaller)
- Pin
edge-impulse-linux==1.2.2 for compatibility
- Use
--no-cache-dir with pip to avoid filling cache: pip install --no-cache-dir -r requirements.txt
Reference Example: Object Detection with Flask
Based on the Edge Impulse Arduino App Lab example:
Project Structure
example-app/
+-- app.yaml
+-- models/
| +-- model-linux-aarch64.eim
+-- assets/
| +-- sample-image.jpg
+-- python/
| +-- main.py
| +-- requirements.txt
| +-- templates/
| | +-- index.html
| | +-- assets/
| +-- utils/
| +-- mock_dependencies.py
+-- .python-version # 3.13.5
app.yaml
name: Custom object detection with Edge Impulse
description: "Real-time object detection using Edge Impulse models and Flask"
icon: "icon-emoji-here"
ports: [5001]
bricks: []
requirements.txt
edge-impulse-linux==1.2.2
opencv-python-headless
flask
numpy
requests
Key Features of the Example
- Multi-source input: camera, RTSP streams, image files, video files
- Real-time inference with MJPEG streaming (
/video_feed, /inference_feed)
- Model switching via web UI
- Frame upload to Edge Impulse Studio with bounding box labels
- Deployment workflow (build, poll status, download model)
- Runs on macOS (arm64) and Linux (aarch64)
Running on Arduino UNO Q
cd /home/arduino/ArduinoApps
git clone https://github.com/edgeimpulse/example-arduino-app-lab-object-detection-using-flask.git my-detection-app
cd my-detection-app
chmod +x models/*.eim
arduino-app-cli app start .
python3 -m venv .venv
source .venv/bin/activate
pip install --no-cache-dir -r python/requirements.txt
python3 python/main.py
Access at http://<device-ip>:5001
Quick Reference Commands
ssh arduino@<device-ip>
adb shell
arduino-app-cli app new "my-app"
arduino-app-cli app start user:my-app
arduino-app-cli app stop user:my-app
arduino-app-cli app list
arduino-app-cli app logs user:my-app --all
scp model.eim arduino@<device-ip>:/home/arduino/ArduinoApps/my-app/models/
ssh arduino@<device-ip> "chmod +x /home/arduino/ArduinoApps/my-app/models/*.eim"
arduino-app-cli brick list
arduino-app-cli brick details arduino:<brick-name>
arduino-app-cli system update
arduino-app-cli system cleanup
df -h
docker system prune -a
v4l2-ctl --list-devices
v4l2-ctl --device /dev/video2 --info
python3 -m venv .venv
source .venv/bin/activate
pip install --no-cache-dir -r requirements.txt
hostname -I
Additional Resources from Community Knowledge Base
Additional Bridge Patterns
Using call() with Return Values (MCU-side handlers)
String getValueHandler() {
return String(analogRead(A0));
}
result = bridge.call("getValueHandler") # Returns the string value
Async Notifications (Fire-and-Forget)
Bridge.notify("buttonPressed", digitalRead(BUTTON_PIN));
@bridge.on_notify("buttonPressed")
def handle_button(value):
print(f"Button state: {value}")
Multiple Parameters
bridge.call("setPWM", "9,128")
// MCU side: Parse the string
void setPWM(String params) {
int comma = params.indexOf(',');
int pin = params.substring(0, comma).toInt();
int value = params.substring(comma + 1).toInt();
analogWrite(pin, value);
}
Community Troubleshooting Additions
Common Bridge Communication Issues
| Issue | Cause | Solution |
|---|
call() returns empty | MCU not initialized | Add 2-3 second delay before first call |
call() times out | Function blocks too long | Keep handlers non-blocking, use delay(10) in loop |
| Function not found | Name mismatch | Check exact spelling matches between MCU and Python |
| Serial errors | Wrong baud rate | Verify 115200 baud on both sides |
Non-Blocking Animation Pattern (MCU)
enum AnimationState { IDLE, SCROLLING, ANIMATING };
AnimationState state = IDLE;
String displayText = "";
int scrollOffset = 0;
void loop() {
switch (state) {
case SCROLLING:
scrollOffset++;
if (scrollOffset > displayText.length() * 6) {
state = IDLE;
}
break;
case ANIMATING:
break;
}
Bridge.update();
delay(50);
}
LED Matrix I2C Troubleshooting
Wire.begin();
Serial.println("Scanning I2C devices...");
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print("Found: 0x");
Serial.println(addr, HEX);
}
}
Network Troubleshooting
nmcli device wifi list
hostname -I
ping google.com
journalctl -u NetworkManager | tail -20
Deployment via ADB (Alternative to SSH)
adb forward tcp:7000 tcp:7000
adb push local/file /home/arduino/project/
adb shell
adb logcat | grep arduino
Resources