1. Safe Shutdown of UNO Q Board (from within App Lab)
Procedures:
(1) Stop the application in the App Lab
(2) Bring in "shell terminal" by clicking on >_ icon of the App Lab window.
(3) Type this command: sudo shutdown -H now and press the Enter key.
(4) Enter the Linux password (characters are not seen) and press the Enter key.
After you enter the password, Linux starts the shutdown process, which includes closing any open files and saving critical data to the eMMC storage. The UNO Q starts rebooting, showing an animation on the LED matrix.
(5) While the animation is ongoing, take out the USB cable from the PC.
//===================================================================
2. Blinking "red:user" led of "RGB LED 1" of UNO Q Board using App Lab
1. In Fig-1, it is seen that there are four RGB leds marked as QRB: 1 2 and STM: 3 4. The "red:user" led is under QRB: 1.
2. The red:user led is connected with an IO line (GPIO_41) of the QRB2210 Microprocessor of the UNO Q board. The connection diagram of the led is shown in Fig-2:
3. The red:user led becomes On when LOW is sserted on the cathode terminal of the LED. This is the reason for calling the led an "active low led".
Figure-1:
Figure-2:
4. Program to blink LED1_R
The MPU controls the LED under the guidance of a program, whose execution is managed by the resident Linux operating system of the QRB2210 MPU. We will write this program using the Python programming language and then upload it using App Lab to the eMMC storage of the UNO Q board. A program written in Python is referred to as a script.
In Linux, the red:user LED is exposed as a writable file through the sysfs interface. Writing 0 to this file turns the LED ON, while writing 1 turns it OFF, as the LED is connected as active-low device.
Script:(tested on 3/5/26)
import time
LED1_R = "/sys/class/leds/red:user/brightness"
LED1_G = "/sys/class/leds/green:user/brightness"
LED1_B = "/sys/class/leds/blue:user/brightness"
LED2_R = "/sys/class/leds/red:panic/brightness"
LED2_G = "/sys/class/leds/green:wlan/brightness"
LED2_B = "/sys/class/leds/blue:bt/brightness"
def set_led_brightness(led_file, value):
try:
with open(led_file, "w") as f:
f.write(f"{value}\n")
except Exception as e:
print(f"Error writing to {led_file}: {e}")
def main():
# turn off all LEDs
set_led_brightness(LED1_R, 0)
set_led_brightness(LED1_G, 0)
set_led_brightness(LED1_B, 0)
set_led_brightness(LED2_R, 0)
set_led_brightness(LED2_G, 0)
set_led_brightness(LED2_B, 0)
while True: #keep executing the sub-ordinated lines repeatedly
#blink the LED1_R (RED) segment
set_led_brightness(LED1_R, 1) #led is Off
time.sleep(1) #wailt for 1-sec
set_led_brightness(LED1_R, 0) #led is On
time.sleep(1)
if __name__ == "__main__": #Python framework to run main() only for once
main()
5. Now, create a new App inside Arduino App Lab inder title: Blink LED1_R and then copy/paste the script of Step-4 in the python section of your application.
6. Click on the Run button of App Lab and check that LED1_R is blinking at 2-sec interval.
7. Description of the working principles of the python script of Step-4
(1) Summary:
The time module of Python package is included in the script.
File paths for the Linux files for the six led devices (red:user to blue:bt) are saved into six variables: LED1_R to LED2_B.
A function named set_led_brightness() is declared and defined for subsequent writing of LOW/HIGH into briggtness files. The function contains file openining/writing methods.
A function named main() is declared and defined to shut off all the leds. After that the red:user led is continuously blinked.
(2) Detailed Description:
(a) Mening of: LED1_R = "/sys/class/leds/red:user/brightness"
"/sys/class/leds/red:user/brightness": This is the file path in Linux. Linux exposes hardware components as files, known as "sysfs" entries. That means hardware devices appear as files.LED1_R: It is a variable that stores the path to the above file.brightness: This is the Linux file (name) into which we write 0 (LOW) to turn On thered:userled and writes 1 (HIGH) to turn Off thered:userled. The led is configured as "active-low" (Fig-2).
//====================================================================
3. Continuous Blinking of LED1/2_RGB and LED3/4_RGB
Working Principle: MPU blinks LED1_R/G/B, L2_R/G/B and then instructs the MCU to blink is LED3_R/G/B, LED4_R/G/B. MCU finishes blinking and then instructs MPU to blink its leds. The cycle repeats this way. The commands excage takes place via Brdge.call()/Bridge.provide() functions over the Router Bridge.
The example is given below (tested):
Sketch:
#include<Arduino_RouterBridge.h>
String startBlink(String str)
{
if(str == "blinkled34")
{
digitalWrite(LED3_R, LOW); //LED On active LOW
delay(1000);
digitalWrite(LED3_R, HIGH);
delay(1000);
digitalWrite(LED3_G, LOW);
delay(1000);
digitalWrite(LED3_G, HIGH);
delay(1000);
digitalWrite(LED3_B, LOW);
delay(1000);
digitalWrite(LED3_B, HIGH);
delay(1000);
//-------------------------------
digitalWrite(LED4_R, LOW);
delay(1000);
digitalWrite(LED4_R, HIGH);
delay(1000);
digitalWrite(LED4_G, LOW);
delay(1000);
digitalWrite(LED4_G, HIGH);
delay(1000);
digitalWrite(LED4_B, LOW);
delay(1000);
digitalWrite(LED4_B, HIGH);
delay(1000);
//-------------------------------
return "blink12";
}
}
void setup()
{
Bridge.begin();
delay(5000);
pinMode(LED3_R, OUTPUT);
pinMode(LED3_G, OUTPUT);
pinMode(LED3_B, OUTPUT);
pinMode(LED4_R, OUTPUT);
pinMode(LED4_G, OUTPUT);
pinMode(LED4_B, OUTPUT);
//------------------------
digitalWrite(LED3_R, HIGH); //LED off; active LOW
digitalWrite(LED3_G, HIGH);
digitalWrite(LED3_B, HIGH);
digitalWrite(LED4_R, HIGH);
digitalWrite(LED4_G, HIGH);
digitalWrite(LED4_B, HIGH);
Bridge.provide("start_blink", startBlink);
}
void loop()
{
}
Script:
import time
from arduino.app_utils import App, Leds, Bridge
def loop():
# Turn on the LED red segment (1, 0, 0)
Leds.set_led1_color(1,0,0)
time.sleep(1)
Leds.set_led1_color(0,0,0)
time.sleep(1)
# Turn on the green LED segment (0, 1, 0)
Leds.set_led1_color(0,1,0)
time.sleep(1)
Leds.set_led1_color(0,0,0)
time.sleep(1)
# Turn on the blue LED segment (0, 0, 1)
Leds.set_led1_color(0,0,1)
time.sleep(1)
Leds.set_led1_color(0,0,0)
time.sleep(1)
#---------------------------------
# Turn on the LED red segment (1, 0, 0)
Leds.set_led2_color(1,0,0)
time.sleep(1)
Leds.set_led2_color(0,0,0)
time.sleep(1)
# Turn on the LED green segment (0, 1, 0)
Leds.set_led2_color(0,1,0)
time.sleep(1)
Leds.set_led2_color(0,0,0)
time.sleep(1)
# Turn on the blue LED segment (0, 0, 1)
Leds.set_led2_color(0,0,1)
time.sleep(1)
Leds.set_led2_color(0,0,0)
time.sleep(1)
#----------------------------------------
while Bridge.call("start_blink", "blinkled34",timeout=20) != "blink12":
pass # Do nothing, immediately loop back and check again
App.run(user_loop=loop)
Note:
1. Within 20 sec if "blink12" comes from MCU, the control will transfer to App.run() causing MPU to blink LED3/4_R/G/B.
2. Within 20 sec if "blink12" does not comes from MCU, the control will transfer to Operating System bypassing the App.run() framework.
3. How can the above script be modified so that control goes to App.run() even though "blink12" does not come from MCU within 20 sec?
//=========================================================================
4. Modulating LED3_RGB
void setup() {
pinMode(50,OUTPUT);
digitalWrite(50,HIGH);
}
void loop() {
int16_t RGBled = 50;
for (int16_t i = 0; i < 200; i++) {
analogWrite(RGBled,i);
delay(20);
}
analogWrite(RGBled,0);
RGBled++;
for (int16_t i = 0; i < 200; i++) {
analogWrite(RGBled,i);
delay(20);
}
analogWrite(RGBled,0);
RGBled++;
for (int16_t i = 0; i < 200; i++) {
analogWrite(RGBled,i);
delay(20);
}
analogWrite(RGBled,0);
RGBled++;
}
//===================================================================
5. Operation of UNO Q board using WiFi Network Connection** and NOT USB Connection
The intentional design of Arduino App Lab is to give preference to the USB connection if available. So if the board is discovered via the USB data cable connection, Arduino App Lab shows that connection.
If you power the UNO Q using USB power cable (D+ and D- wires are absent) from your computer and when you open Arduino App Lab you should then see the network connection to the board is shown:
1. I have powered my UNO Q from PC using a USB-C power/charger cable (D+, D- lines are absent).
2. I have opened App Lab.
3. Yes! The network connection window has appeared.
4. I have clicked on ArduinoQ. As a result, the App Lab window has appeared.
5. I have opened my LEDR_3RBlink app and clicked on the Run button. The app is running showing a blinking led on the UNO Q board.
//====================================================================
6. Hardware and Software resources needed to operate/program the Arduino UNO Q Board in "PC Host Mode"
D1: Hardware Resources
1. Arduino UNO Q Board (2 GB/16 GB or 4 GB/32 GB)
Purpose:
2. USB-A to USB-C data cable
Purpose:
3. PC with Windows 10+ (for me)
Purpose:
4. Internet connectivity
Purpose:
8.
... in progress
D2: Software Resources
1. IDE 2.3.8+
Purpose:
2. Board Package aka Hardware Core aka Platform aka arduino:zephyr aka UNO Q Zephyr Core
- It is a software package that is installed in PC hard disk and in MCU flash using BOARDS MANAGER interface of the IDE.
- The software allows the MCU of the UNO Q board to handle I/O, sensors, and actuators instantly upon booting.
3. Libraries
When install the "Arduino_RouterBridge" library via LIBRARY MANAGER interface, Arduino IDE will open an "Install library dependencies " dialog offering to install the other libraries. This is because they are specified as dependencies via the "Arduino_RouterBridge" library's metadata. So the user only needs to be sure to Click the "INSTALL ALL " button in that dialog in order to obtain installations of those other libraries.
- Arduino_RouterBridge.h
Purpose:- Its primary purpose is to act as a
Remote Procedure Call (RPC) bridge, enabling seamless data exchange between the board's two distinct processors: 64-bit QBR2210 MPU and 32-bit STM32U585 MCU.
- Its primary purpose is to act as a
- Arduino_RPClite.h
- MsgPack.h
- DebugLog.h
- ArxTypeTraits.h
- ArxContainer.h
//==============================================================
7.G Header Pins mapping
Have you tried Fig-2 of post #2. It works.
H: Internal Communication Link between MCU and MPU
Figue-1:
In Fig. 1 above, we can see four communication buses (Bus-2 does not exist). Among them, Bus-3, which is the Serial Wire Debug (SWD) bus, is dedicated to programming the MCU flash memory with code and data.
Bus-4, which appears as ttyHS1/UART2 on the MPU side and LPUART1/Serial2 on the MCU side, is dedicated to the Router Bridge for high-volume data exchange between the MPU and MCU.
This leaves Bus-5, which you can use for high-speed data exchange between the MPU and MCU. It is based on the SPI interface. To use this bus, you need to write a C++ sketch for the MCU side and a Python script for the MPU side to exchange data. You may find this thread @philippe86220 interesting.
I: Binary Breakdown of information traveling over the Router Bridge
The Complete Binary Breakdown of the arguments (tag_name, data items) of:
Bridge.call("get_data", 0x1234, 1.57, "Forum");
When serialized by MsgPack.h, the entire transmission packet expands to look like this:
94Outer Array of 4 items[type, msgid, tag, params]00type:0(Request)01msgid:1(The identifier)A8 67 65 74 5F 64 61 74 61TAG: String of length 8 ("get_data")93params: Inner Array of 3 items[0x1234, 1.57, "Arduino"]CD 12 34Unsigned 16-bit Int (0x1234)CA 3F C8 F5 C332-bit Float (1.57)A5 46 6F 72 75 6D String of length 5 ("Forum"`)
//================================================================
8.J* Sending int float and string data items by MCU to MPU using Router Bridge
sketch:
#include<Arduino_RouterBridge.h>
void setup() {
Bridge.begin();
delay(5000);
}
void loop() {
Bridge.call("get_data", 1234, 12.37, "Green");
delay(1000);
}
script:
import time
from arduino.app_utils import App, Bridge
print("Hello world!")
def getData(y, z, msg):
print(y)
print(z)
print(msg)
print("----------------------")
def main():
Bridge.provide("get_data", getData)
while True:
time.sleep(10)
if __name__ == "__main__":
main()
Output:
----------------------
1234
12.37
Green
----------------------
1234
12.37
Green
----------------------
//=====================================================================
9. Timeout if Acknowledgement does not arrive from MPU in a given time-window
Sketch:
#include<Arduino_RouterBridge.h>
bool status = false;
String value = "";
unsigned long prMillis = millis();
void setup()
{
Bridge.begin();
Monitor.begin();
do
{
RpcCall rpc = Bridge.call("is _linux_ready");
status = rpc.result(value);
}
while((status != true) && (millis() - prMillis) < 5000);
if(status == true)
{
Monitor.print(value);//Linux is ready
}
else
{
while(true) //error at Linux side
{
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN);
delay(1000);
}
}
}
Script:
from arduino.app_utils import *
import time
#used only for sync purposes
def linux_started():
return "Linux is ready"
Bridge.provide("is _linux_ready", linux_started)
def loop():
time.sleep(1)
App.run(user_loop=loop)
#include "Arduino_RouterBridge.h"
void setup() {
// Initialize communication layer
Bridge.begin();
}
void loop() {
unsigned long prMillis = millis();
bool status = false; //will turn to true when message comes from MPU
String value = ""; //value will be loaded with message that comes from MPU
do
{
RpcCall rpc = Bridge.call("get_data", 0x1234, 1.57, "Forum");
status = rpc.result(value);
}
while((status != true) && (millis() - prMillis < 20)); //timeout: 20 ms
Monitor.print("Elapsed time: "); //measuring response time or timeout period
Monitor.print(millis() - prMillis); Monitor.println(" ms");
if(status == false)
{
Monitor.println("Communication error!");
while(true) //wait for ever
{
Monitor.println("I'm in the error loop");
}
}
else
{
Monitor.println(value); //shows: Communication is ok!
}
}
//=====================================================`
10. Serial Communication Ports of STM32U585 MCU
(1) USART1 : Serial1 on DPin-0, 1 = RX/TX
(2) USART2
(3) USART3
(4) UART4
(5) UART5
(6) LPUART1 : Serial2 on Router Brigde
(7) LPUART2
M: Delaying MCU until MPU/Linux is
#include<Arduino_RouterBridge.h>
bool status = false;
String value = "";
unsigned long prMillis = millis();
void setup()
{
Bridge.begin();
Monitor.begin();
do
{
RpcCall rpc = Bridge.call("is _linux_ready");
status = rpc.result(value);
}
while((status != true) && (millis() - prMillis) < 5000);
if(status == true)
{
Monitor.print(value);//Linux is ready
}
else
{
while(true) //error at Linux side
{
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN);
delay(1000);
}
}
}
//=======================================================================
11. Here is an example of a four thread sketch which blinks four onboard LEDs at different rates under App Lab
The App
Sketch:
#include <Arduino.h>
#include <zephyr/kernel.h>
#include <Arduino_RouterBridge.h>
#define STACK_SIZE 1024
#define LED_PRIO3r 5
#define LED_PRIO4r 5
#define LED_PRIO4g 5
#define LED_PRIO4b 5
static struct k_thread led3r_thread;
static struct k_thread led4r_thread;
static struct k_thread led4g_thread;
static struct k_thread led4b_thread;
K_THREAD_STACK_DEFINE(led3r_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(led4r_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(led4g_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(led4b_stack, STACK_SIZE);
void led3r_task(void *, void *, void *) {
pinMode(LED3_R, OUTPUT);
while (1) {
digitalWrite(LED3_R, HIGH);
k_msleep(500);
digitalWrite(LED3_R, LOW);
k_msleep(500);
}
}
void led4r_task(void *, void *, void *) {
pinMode(LED4_R, OUTPUT);
while (1) {
digitalWrite(LED4_R, !digitalRead(LED4_R));
k_sleep(K_SECONDS(1));
}
}
void led4g_task(void *, void *, void *) {
pinMode(LED4_G, OUTPUT);
while (1) {
digitalWrite(LED4_G, !digitalRead(LED4_G));
k_sleep(K_SECONDS(2));
}
}
void led4b_task(void *, void *, void *) {
pinMode(LED4_B, OUTPUT);
while (1) {
digitalWrite(LED4_B, !digitalRead(LED4_B));
k_sleep(K_SECONDS(3));
}
}
void setup() {
Bridge.begin();
Monitor.begin();
delay(5000);
k_thread_create(&led3r_thread,
led3r_stack,
STACK_SIZE,
led3r_task,
NULL, NULL, NULL,
LED_PRIO3r,
0,
K_NO_WAIT);
k_thread_create(&led4r_thread,
led4r_stack,
STACK_SIZE,
led4r_task,
NULL, NULL, NULL,
LED_PRIO4r,
0,
K_NO_WAIT);
k_thread_create(&led4g_thread,
led4g_stack,
STACK_SIZE,
led4g_task,
NULL, NULL, NULL,
LED_PRIO4g,
0,
K_NO_WAIT);
k_thread_create(&led4b_thread,
led4b_stack,
STACK_SIZE,
led4b_task,
NULL, NULL, NULL,
LED_PRIO4b,
0,
K_NO_WAIT);
}
void loop() {
Bridge.call("get_data", 1234, 12.37, "Green"); //tag, 3 datae items
delay(1000); //repeating intervals
//k_sleep(K_FOREVER);
}
Script:
import time
from arduino.app_utils import App, Bridge
print("Hello world!")
def getData(y, z, msg):
print(y)
print(z)
print(msg)
print("----------------------")
def main():
Bridge.provide("get_data", getData)
while True:
time.sleep(10)
if __name__ == "__main__":
main()
Output on Python Console:
1234
12.37
Green
----------------------
1234
12.37
Green
----------------------
1234
12.37
Green
----------------------
1234
12.37
Green
----------------------
//==================================================================
##12. Docker Container's Private Mini Filesystem**
Comments are welcome regarding the conceptual accuracy of the following explanation, rather than strict technical definitions. Feel free to elaborate on any points if helpful. This is an expanded summary based on the insights shared by @ptillisch in post #14 of this thread.
-
My app/project named
adcWrite, created using App Lab, is automatically saved in the following directory on the eMMC of the host Linux OS:/home/arduino/ArduinoApps/adcWrite/ -
In my app, the Python script periodically writes data to a file named adc.txt using the absolute path inside the container:
FILE_PATH = "/home/arduino/adc.txt" -
The
/home/arduino/directory referenced in Step 2 is a pre-configured directory that belongs to the container's private mini filesystem (writable layer) residing on the eMMC. It is completely isolated from the host Linux OS's /home/arduino directory, even though both have used the same directory name. The identical naming convention is not a coincidence but a design convenience, as is known from AI. -
While the app is running, adc.txt is not visible when exploring the host's
/home/arduino/directory via a host shell terminal. The Files Panel of App Lab also does not display adc.txt. -
The container’s private /home/arduino/ directory serves as temporary storage. The
adc.txtis permanently lost whenever the container is removed, rebuilt, or re-deployed. -
If I use
FILE_PATH = "/app/adc.txt"in place ofFILE_PATH = "/home/arduino/adc.txt"in the Python script, then writing to/app/adc.txtindeed places the data directly into/home/arduino/ArduinoApps/adcWrite/adc.txton the host eMMC. Data is not written into/app/adc.txt; instead,/app/adc.txtworks as a pointer to the project folder on the host eMMC. -
/app/is a pre-configured directory inside the container's Private Mini Filesystem linked to the host project folder (/home/arduino/ArduinoApps/adcWrite/). Writing to/app/adc.txtbypasses the container's private layer and writes directly to physical host eMMC storage. Consequently, if the container is removed, rebuilt, or deleted, adc.txt remains safely intact on the host OS eMMC.
... in progress







