Bridge.begin() crashes if Linux isn't running

I want to have the MPU and MCU work independently. I'm programming the MCU with Arduino IDE and the MPU with Arduino App Lab. As long as I don't have an .INO sketch, it won't overwrite the existing sketch. So far so good. However, I'm using "Immediate" startup mode and want to use the Bridge function. Bridge.begin() crashes if Linux isn't running. How can I determine if Linux is running without my loop being blocked?

I don't know anything about the Uno Q. Does Bridge.begin() crash or does your code crash after that?

What I can see is that Bridge.begin() returns a bool so you can test if it succeeded. That might help if your code crashes after Bridge.begin().

No, the setup only has Bridge.begin(); and in the loop, I'm controlling an LED. When Linux is running, this works fine. When Linux isn't running, the script crashes.

Post your Arduino sketch and Python script.

Are you sure that you can proram MPU using Arduino IDE?

GolamMostafa,

No, it's the other way around, my mistake.
I program the MCU with the Arduino IDE. I've corrected it in the question. So, it's about the Arduino Sketch. When Linux is running, it works fine. If Linux isn't running, the LED4_G doesn't even turn on. Even if I remove Bridge.provide(). Bridge.begin() crashes the script.

#include <Arduino_RouterBridge.h>


constexpr int BUTTON_PIN = 2;


bool bridge_ready = false;
bool led_is_on = false;
bool isOffPhase;

// Debounce
bool last_button_read = HIGH;
bool stable_button_state = HIGH;
unsigned long last_change_ms = 0;
constexpr unsigned long DEBOUNCE_MS = 30;


void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode (LED4_G, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  digitalWrite(LED4_G, HIGH);

  apply_led(false);


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

  digitalWrite(LED4_G, LOW);
}

void loop() {

isOffPhase = ((millis() / 1000) % 2);
 digitalWrite(LED4_G, isOffPhase);



  // Lees knop
  bool reading = digitalRead(BUTTON_PIN);

  // Debounce: detecteer verandering
  if (reading != last_button_read) {
    last_change_ms = millis();
    last_button_read = reading;
  }

  // Als reading stabiel is na debounce-tijd, accepteer nieuwe state
  if ((millis() - last_change_ms) > DEBOUNCE_MS) {
    if (reading != stable_button_state) {
      stable_button_state = reading;

      // Actie op "press": HIGH->LOW (door pull-up)
      if (stable_button_state == LOW) {
        led_is_on = !led_is_on;
        apply_led(led_is_on);


      }
    }
  }
}

// ---- Bridge functies ----
void set_led_state(bool state) {

  led_is_on = state;
  apply_led(led_is_on);

  }

bool get_led_state() {
  return led_is_on;
}

// ---- Hardware helper ----
void apply_led(bool state) {
  // LOW = LED aan op veel Arduino boards met built-in LED (UNO-achtig)
  digitalWrite(LED_BUILTIN, state ? LOW : HIGH);
}

from arduino.app_utils import *
from arduino.app_bricks.web_ui import WebUI
import time
import threading

ui = WebUI()

_last_state = None
_lock = threading.Lock()

def read_mcu_led_state():

    return Bridge.call("get_led_state")

def get_led_status_from_mcu():
    state = read_mcu_led_state()
    return {
        "led_is_on": state,
        "status_text": "LED IS ON" if state else "LED IS OFF"
    }

def broadcast_state_if_changed(target_client=None):
    global _last_state
    with _lock:
        state = read_mcu_led_state()
        if state != _last_state:
            _last_state = state
            payload = {
                "led_is_on": state,
                "status_text": "LED IS ON" if state else "LED IS OFF"
            }
            ui.send_message("led_status_update", payload, target_client)

def toggle_led_state(client, data):
    # Vraag waarheid op, flip, zet op MCU
    current = read_mcu_led_state()
    Bridge.call("set_led_state", not current)

    # Daarna: lees terug van MCU en push naar UI
    broadcast_state_if_changed()

def on_get_initial_state(client, data):
    # UI vraagt init state: stuur direct MCU-status naar die client
    broadcast_state_if_changed(target_client=client)

def watcher_loop():
    # Poll elke 200 ms; alleen bij verandering broadcasten
    while True:
        try:
            broadcast_state_if_changed()
        except Exception:
            # optioneel: logging
            pass
        time.sleep(0.2)

ui.on_message("toggle_led", toggle_led_state)
ui.on_message("get_initial_state", on_get_initial_state)

# Start watcher thread
threading.Thread(target=watcher_loop, daemon=True).start()

App.run()

The code is a modified version of "Blink LED with UI."
In this case, the MCU is responsible for the LED's status and can operate independently of Python. Python is used only for visualization.

Do you want to say that sketch of #6 and script of #7 are not working in co-operative mode?

Please, tell in plain text the sequence of dvents taking place between MCU and MPU in the programs of #6 and #7.

Yes, they work fine together, as long as Linux is running. You can choose to have the MCU boot directly on power-on. Linux isn't ready yet, so the script crashes at Bridge.begin().

If Linux isn't ready, then there is no bridge available to Bridge.begin() yet, but it sounds like the function should fail in a 'nice' way so the sketch could programmed to wait for it, rather than just causing it to crash. Might it be that `Bridge.provide()' is actually causing the crash because Bridge.begin() has not yet initialized?

Given the information in post #2, a delay loop waiting for the Bridge to be ready before calling Bridge.provide() might help. There are probably various ways to set this up but one possibility, using your bridge_ready variable, might be:

unsigned long timeout = 60000;
while ( !bridge_ready && millis()<timeout ) {
    bridge_ready = Bridge.begin();
    delay(200);
};

if ( bridge_ready ){
    Bridge.provide("set_led_state", set_led_state);
    Bridge.provide("get_led_state", get_led_state);
}else{
    // Indicate failure to start bridge within timeout period
}

Don't know offhand how long it takes for the bridge to start. That would have to be determined by trial and error. I set one a minute wait, but the the example can be tweaked as needed. This is similar in principle to a loop waiting for Serial to start.

If that still crashes the sketch, then the crash may well be caused by Bridge.begin().

This is exactly what I've already done. Even if I remove Bridge.provide, the script crashes. This happens only when Linux isn't present. Otherwise, everything works fine.

curious..
try adding this to top of setup..

  pinMode(LED_BUILTIN, OUTPUT);
  while(!Serial1){
  digitalWrite(LED_BUILTIN, LOW);
  delay(100);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);    
  }

bridge uses Serial1, maybe a little too early..

good luck.. ~q

Sounds like Bridge.begin() isn't behaving nicely.

Interesting idea from @qubits-us. Would be interested to know how you get on.

I thought I had something but still fighting the battle..
i would recommend downgrading following this post or roll your sleeves up and join the battle..
strange days.. ~q

As said, I have no knowledge of the Uno Q. The above however sounds very dangerous if I look at, what I think is, the begin() method.

    // Initialize the bridge
    bool begin(unsigned long baud=DEFAULT_SERIAL_BAUD) {
        k_mutex_init(&read_mutex);
        k_mutex_init(&write_mutex);
        k_mutex_init(&bridge_mutex);

        if (is_started()) return true;

        k_mutex_lock(&bridge_mutex, K_FOREVER);

        serial_ptr->begin(baud);
        transport = new SerialTransport(*serial_ptr);

        client = new RPCClient(*transport);
        server = new RPCServer(*transport);

        upd_stack_area = k_thread_stack_alloc(UPDATE_THREAD_STACK_SIZE, 0);
        upd_tid = k_thread_create(&upd_thread_data, upd_stack_area,
                                UPDATE_THREAD_STACK_SIZE,
                                updateEntryPoint,
                                NULL, NULL, NULL,
                                UPDATE_THREAD_PRIORITY, 0, K_NO_WAIT);
        k_thread_name_set(upd_tid, "bridge");

        bool res = false;
        started = call(RESET_METHOD).result(res) && res;
        k_mutex_unlock(&bridge_mutex);
        return res;
    }

Dynamic memory allocation so one might easily run out of memory if that is called repeatedly.

And to make matters worse (my opinion), no error checking on the result of the dynamic memory allocation.

Note:
I did upgrade to 0.53.0 this morning; 0.52.0's begin method looked slightly different (if I recall correctly).

Then why not using the following codes at MCU side to delay the sketch execution unitl the MPU has started?

 boolean start = false;

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

Point noted. Thanks. Should have looked at the code underlying the method.
It does appear to confirm that this call starts Serial1 though, assuming that is what the serial_ptr variable points to.

because the mcu locks hard when calling bridge.begin..
this is mysketch, it fails to begin when un-commenting the bridge.provide..
~q

Please, elaborate. I have been learning the UNO Q system slowly.

I wish I could..
If you look at the sketch, bridge.provide is well below bridge.begin..
it makes no sense, so i would have to say something in the rpc server or how the 2 are interacting, it's like i'm stuck waiting forever for a mutex..
all i got right now is a led to blink..
~q