Arduino UNO Q and Bridge Tool: any samples and documentation?

Hi everyone just unpacked my anticipated Uno Q Christmas gift, successfully went through some issue at getting it properly work, ran through basic examples like “Blink LED”.

It’s the first time I’m facing a two processor (MCU - sketch to MPU - python) way of programming and I realized (not a very thoughtful consideration, indeed :slight_smile: ) that properly managing the Bridge Tools is key for every kind of future steps.

I read the official documentation and I’m right now in the “trial & error” phase, which is fun as in every new adventure, but I’m very lacking some simple examples end to end on how to do very simple things.

For instance how to let “Arduino” communicate the number 1 to “Pyhton” and let Python write it.

Here below the two code snippets. Surely I’m missing something “silly and basic”, sorry, but this is the reason why I’m eager to some practical examples on the “Bridge Tool “ mechanics:

Python side snippet:

from arduino.app_utils import * #comando di import di tutte le librerie utili per arduino

print("ciao brutti")

def pippottella(data: int):
  print(data)

Bridge.provide("python_function", pippottella)

pippottella

App.run()


Arduino side snippet:

#include "Arduino_RouterBridge.h"

void setup() {
Bridge.begin();
int data = 1;
Bridge.notify("python_function", data);
}

void loop() {}

message in Python Console

======== App is starting ============================
2025-10-25 07:05:07.119 INFO - [MainThread] App: App started
Activating python virtual environment
ciao brutti
======== App is starting ============================
2025-10-25 07:07:10.948 INFO - [MainThread] App: App started

What I would expected is that in Python Console after the message “ciao brutti” I found “1” provided by the execution of pippottella.

Sorry for the silly question and thanks in advance for every guidance and working samples of the same very simple use case.

Andrea

Hi @apagliari,

You are almost there! The Bridge uses an asynchronous transmission mechanism, with the Arduino Router brokering messages between all connected apps (Arduino Router being an application running as a Linux system service).

What is happening in your case is that the Sketch is sending the data before the Python script is ready to collect it. If you need to send data from the setup(), add a ~5s delay (YMMV) before the notify, or a better option is to move the notify into the loop, maybe using the famous “Blink without delay” approach.

Enjoy!

2 Likes

A working example based on Blink LED
Python:

#
# SPDX-License-Identifier: MPL-2.0

from arduino.app_utils import *
import time

led_state = False

def fromQRB(data: int):
  print(data)

Bridge.provide("fromQRB", fromQRB)

def loop():
    global led_state
    time.sleep(1)
    led_state = not led_state
    Bridge.call("set_led_state", led_state)

App.run(user_loop=loop)

Sketch:

#include <Arduino_RouterBridge.h>

uint32_t iloop = 0;

void setup() 
{
  
    pinMode(LED3_G, OUTPUT);

    Bridge.begin();
    Bridge.provide("set_led_state", set_led_state);

    delay(5000);
}

void loop() 
{
  
  iloop++;
  Bridge.call("fromQRB", iloop);
  
  delay(2000);
}

void set_led_state(bool state) 

{
    // LOW state means LED is ON
    digitalWrite(LED3_G, state ? LOW : HIGH);
}
4 Likes

Thank you @manchuino you showed me the right direction and now a working connection between “Sketch” and “Python” has been established. Thanks a lot!:grinning_face:

2 Likes

Thank you @bborredon !

You example that establish a two way simoultaneous connection between “Sketch” and “Python” it’s really interesting and right to the point I had.

My humble opinion is that this example, with maybe some further variants, should be part of an official tutorial.

Anyway thanks for your swing support!

Andrea

2 Likes

@apagliari a way to syncronize the execution between python and sketches is to use a bridge function that returns a boolean and call that function in a loop in the sketch setup. This will guarantee that once you exit the setup, the python is running:

from arduino.app_utils import *
import time

# used only for sync purposes

def linux_started():
  return True

def python_func (data: int):
  print(data)

Bridge.provide("linux_started", linux_started)
Bridge.provide("python_func", python_func)

def loop():
  time.sleep(1)
  python_func

App.run(user_loop=loop)
#include "Arduino_RouterBridge.h"

int data = 0;

void setup() {
  Bridge.begin();
  delay(2000);

  boolean start = false;

  // Wait until the python is started
  while(!start)
  {
    Bridge.call("linux_started").result(start);
  }
}

void loop() {
  data++;
  Bridge.notify("python_func", data);
  delay(1000);
}

4 Likes

@ffich that’s amazing and insightsful! Will surely adopt such a syncronization approach :+1: Thanks a lot!

1 Like

@ffich Thanks for the useful example.
I have been using Python for a short time and I have a doubt, is there any special reason for the line with 'python_func' in in the loop()
or it could be a typo?
Thank you

def loop():
  time.sleep(1)
  python_func

Hi @marco1958 ,

you are absolutely right, it’s a typo that I didn’t notice (also because it doesn’t trigger any error, as is a valid, even if not called, object). I’m pasting here the correct version of the two snippets.

python:

from arduino.app_utils import *
import time

#used only for sync purposes
def linux_started():
  return True

def python_func (data: int):
  print(data)

Bridge.provide("linux_started", linux_started)
Bridge.provide("python_func", python_func)

def loop():
  time.sleep(1)

App.run(user_loop=loop)

sketch:

#include "Arduino_RouterBridge.h"

int data = 0;

void setup() {
  Bridge.begin();
  delay(2000);

  boolean start = false;

  // Wait until the python is started
  while(!start)
  {
    Bridge.call("linux_started").result(start);
  }
}

void loop() {
  data++;
  Bridge.notify("python_func", data);
  delay(1000);
}

Thanks for the hint!

BR

F.

1 Like

I have read in Pythin language documentation that lines belong to a function are shown intended. In your above case, I don't see any intendation. Sould they be like as follows or indenttaion is opional?

def python_func (data: int):
   print(data)

Python relies on the indentation, for example for the body of a function, or the dependent code in an IF statement.

Is it mandatory or optional?

100% mandatory!

That means that the unintended Python codes of #10 will not be running -- correct?

#10 is missing the required indentation, presumably due to it being lost when the code was copied and/or marked up with Code tags.

I would expect Python errors when the code shown is run.

Ok! Let me try application of #10 as it is and report the outcome.

Yes! You are right.

All lines belonging to a function must be indented following the colon (:); otherwise, the application fails to run.

The App Lab interface should have an auto-format utility (Ctrl+T) like the IDE for both script and sketch.

By the by, is it the script or sketch that is loaded first into MPU and MCU respectively? From codes of #10, it appears that the sketch is loaded first in the MCU. Am I correct?

I’m not sure of the MPU/MCU loading sequence and timing - perhaps the Arduino team can help? I’m experimenting with the ‘linux_started’ logic from an above post.

By the way, for anyone unsure how to see the run-time Python errors, you can see them in the Python log, e.g. by clicking the ‘Connect to the board’s shell’ button near the bottom left in App Lab, and then using the command:

arduino-app-cli app list

to get your App ID (they seem to be always lower-case), and then:

arduino-app-cli app logs user:your-app-id

Hi,

Indentation was lost when I copy/pasted the script, fixed now. Sorry for that :sweat_smile:

When you lunch an app in AppLab the sketch is built by the MPU and then flashed on the MCU. After that the python script is run.

BR

1. Does this mean that the C++ compiler and the Python interpreter are pre-stored in eMMC, then they are loaded into RAM, then executed by the MPU to generate the corresponding executable files, and then te sketch is flashed into MCU and the script remains in RAM for runtime execution?

2. As a result, the execution of the sketch in the MCU tends to begin earlier but is delayed until the MPU has started execution, which you have guaranteed in #10 by including the following codes at the setup() function of the MCU sketch:

 // Wait until the python is started
  while(!start)
  {
    Bridge.call("linux_started").result(start);
  }

3. Could the above code lines of Step-2 be written as:

 // Wait until the python is started
  do
  {
    result = Bridge.call("linux_started");
  }
  while(result != true);