Bridge call or notify and Threading

New to Q and looking to understand more of the bridge and using it in a thread. My issue is the bridge.call or notify. when used in the task below the board hangs. I believe I have setup and called the correct functions. Any pointers would be great!

Notes:

  • With bridge call commented out, all tasks run fine and code works.
  • Notify or call have the same issue.
  • Added a delay to see if the issue with calling bridge too fast was the cause. no luck.
  • because the MCU hangs but the MPU stays active I do believe the issue is with the MCU part of the code.
  • I am using the board directly to code and test. Monitor, Keyboard and mouse attached.

MCU code:
Main sketch - Starts 4 tasks and 2 queues

#include "queue.h"

/* size of stack area used by each thread */
#define STACKSIZE 512

/* scheduling priority used by each thread */
#define PRIORITY 7

void setup() {
  blink3_setup();
  MPU_setup();
  LEDMatrix_setup();
  enc_setup();
}

// Queues
K_MSGQ_DEFINE(qData,sizeof(int32_t[2]),4,1);
K_MSGQ_DEFINE(QmpuData,sizeof(int32_t[2]),4,1);

// Threads
K_THREAD_DEFINE(blink3_id, STACKSIZE, tBlink3, NULL, NULL, NULL, PRIORITY, 0, 0);
K_THREAD_DEFINE(MPUsend_id, STACKSIZE, tMPUSend, NULL, NULL, NULL, PRIORITY, 0, 0);
K_THREAD_DEFINE(LEDM_id, STACKSIZE, tLEDmatrix, NULL, NULL, NULL, PRIORITY, 0, 0);
K_THREAD_DEFINE(ENC_id, STACKSIZE, tEncoder, NULL, NULL, NULL, PRIORITY, 0, 0);

void loop() {
  
   // Let Magic happen
  
}

Task that calls bridge.call or notify both yield the same result

#include "Arduino_RouterBridge.h"

void MPU_setup() {
  Bridge.begin();
}

void tMPUSend() {
  struct Package_data  mcuData;
  
  while(1){
    //int ret = k_msgq_get(&QmpuData,&mcuData, K_MSEC(4000));
    mcuData.pos = 66;
    mcuData.speed = 55;
    int ret = 0; 
    
    if (ret == 0){
       Bridge.call("From_MCU", mcuData.pos, mcuData.speed);
    }else{ //no message in queue
       Bridge.call("From_MCU", 0, 0);
    }
    delay(500); //debug delay
  }
}

MPU code:
Just a basic print something if it gets a call

from arduino.app_utils import *
import time
from arduino.app_utils import App, Bridge

def loop():
    time.sleep(1)

def From_MCU(pos: int32, speed: int32)):
    print("hello from mcu")

Bridge.provide("From_MCU", From_MCU)

App.run(user_loop=loop)

Pointing to any documents or examples that cover threading with the bridge might be helpful. I have review a fair bit of posts.

1.
Your app of post #1 is not compiled in App Lab. Briefly describe the purpose of your app.

2.
Run the following simple app under App Lab in PC host mode (not SBC) and report the result: (MCU sends these data items to MPU using Bridge.call() at 1 sec intervals: 1234, 12.37, "Green")

Sketch:

#include<Arduino_RouterBridge.h>

void setup() {
  Bridge.begin();
  delay(5000);  //wait until Linux/MPU has started
}

void loop() {
  Bridge.call("get_data", 1234, 12.37, "Green");  //tag, 3 datae items
  delay(1000);   //repeating intervals
}

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
----------------------

3.
If 2. works, then add bink LED_BUILTIN sketch as thread with 2. and run it.

Hi @castillog can you provide a complete minimal example of the issue please?

Sorry, not at computer to look it up, but I know that @Merlin513 and I have discussed some of the issues with creating threads using the macro is problematic, instead use api to create thread.

It was also discussed on at least one github issue.

@KurtE is correct in terms of using certain thread macros. For instance if you define a thread using k_thread_define the Uno Q fails to run and throws a Bus Fault, example

#define STACKSIZE 1024

/* STEP 5 - Change the priority of thread0 to 6 */
#define THREAD0_PRIORITY 7
#define THREAD1_PRIORITY 7

/*
 * TODO - how to change timeslice programatically
 * timeslice applies to threads of same priority
  */

#define time_slice 10

void thread0(void)
{
	while (1) {
		Serial.print("Hello, I am thread0\n");
		k_busy_wait(time_slice * 100000);
	}
}

void thread1(void)
{
	while (1) {
		Serial.print("Hello, I am thread1\n");
		k_busy_wait(time_slice * 100000);
	}
}

K_THREAD_DEFINE(thread0_id, STACKSIZE, thread0, NULL, NULL, NULL, THREAD0_PRIORITY, 0, 0);
K_THREAD_DEFINE(thread1_id, STACKSIZE, thread1, NULL, NULL, NULL, THREAD1_PRIORITY, 0, 0);

void setup() {
  Serial.begin(115200);
  //while (!Serial && millis() < 5000) {};
  delay(2000);
  Serial.println("Threading time slice sketch  started");
k_tid_t our_tid = k_sched_current_thread_query();
  int main_pri = k_thread_priority_get(our_tid);
  Serial.print("main TID: ");
  Serial.print((uint32_t)our_tid, HEX);
  Serial.print(" pri: ");
  Serial.println(main_pri);
  printk("main TID:%x pri:%d\n", (uint32_t)our_tid, main_pri);
  //k_thread_priority_set(our_tid, THREAD0_PRIORITY+1);
  //main_pri = k_thread_priority_get(our_tid);
  //Serial.print("\tupdated pri: ");
  //Serial.println(main_pri);
  //printk("main TID:%x pri:%d\n", (uint32_t)our_tid, main_pri);
}

void loop() {
  // put your main code here, to run repeatedly:
  k_msleep(time_slice);
}

However, if you instead you use the k_thread_create method it threading will work, for instance

/* main.c - Hello World demo */

/*
 * Copyright (c) 2012-2014 Wind River Systems, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


/* size of stack area used by each thread */
#define STACKSIZE 1024

/* scheduling priority used by each thread */
#define PRIORITY 7

/* delay between greetings (in ms) */
#define SLEEPTIME 500


K_THREAD_STACK_DEFINE(threadA_stack_area, STACKSIZE);
static struct k_thread threadA_data;

/* threadA is a static thread that is spawned automatically */

void threadA(void *dummy1, void *dummy2, void *dummy3)
{
	ARG_UNUSED(dummy1);
	ARG_UNUSED(dummy2);
	ARG_UNUSED(dummy3);

	Serial.print("thread_a: thread started \n");

	while (1)
	{
		Serial.print("thread_a: thread loop \n");
		k_msleep(SLEEPTIME);
	}

}


void setup() {
  Serial.begin(115200);
  //while (!Serial && millis() < 5000) {}
  Serial.println("Threading 1 start");
	k_tid_t tid = k_thread_create(&threadA_data, threadA_stack_area,
			K_THREAD_STACK_SIZEOF(threadA_stack_area),
			threadA, NULL, NULL, NULL,
			PRIORITY, 0, K_FOREVER);
  Serial.print("Thread ID: ");
  Serial.println((uint32_t)tid);
	k_thread_name_set(&threadA_data, "thread_a");

	k_thread_start(&threadA_data);
  Serial.println("End Setup");
}

void loop() {
  // put your main code here, to run repeatedly:
  k_msleep(SLEEPTIME);

}

@castillog

1.
Here is an example of a four thread sketch which blinks four onboard LEDs at different rates under IDE 2.3.10; where, the threads are created using k_thread_create() function.

#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() {
  k_sleep(K_FOREVER);
}

2.
Example of 1. running under App Lab along with data transfer from MCU to MPU over the Router Bridge.

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
----------------------

Thank you all for the thoughtful input. Let me give it a shot and update with my findings.

Side question:
What is the best way to upload the Project file with all code to the post? Had a few requests for the full project. 7z files are not allowed.

I should note here that it the threads are creating and running fine without the bridge call. I will include my 4 here for reference. I am going to try the thread create but if it works I will have no idea why. Don't love those type of solutions but will take it on faith.

Note the use of the the i2c bus on the encoder.
Thread 1

void blink3_setup() {
  pinMode(LED3_R, OUTPUT);
  pinMode(LED3_G, OUTPUT);
  pinMode(LED3_B, OUTPUT);
}

void tBlink3() {
  
 //Note LED 1 and 2 are not connected to MCU only MPU
  while(1){
    analogWrite(LED3_R, 255);
    analogWrite(LED3_G, 0);
    analogWrite(LED3_B, 0);
  
    delay(500);
  
    analogWrite(LED3_R, 0);
    analogWrite(LED3_G, 0);
    analogWrite(LED3_B, 0);
  
    delay(500);
  }

}

Thread 2

#include "Arduino_RouterBridge.h"

void MPU_setup() {
  Bridge.begin();
  delay(4000); 
}

void tMPUSend() {
  struct Package_data  mcuData;
  
  while(1){
    //int ret = k_msgq_get(&QmpuData,&mcuData, K_MSEC(4000));
    mcuData.pos = 66;
    mcuData.speed = 55;
    int ret = 0; 
    
    if (ret == 0){
      // Bridge.call("From_MCU", mcuData.pos, mcuData.speed);
    }else{ //no message in queue
       Bridge.call("From_MCU", 0, 0);
    }
    delay(500); //debug delay
  }
}

Thread 3

#include "ArduinoGraphics.h"
#include "Arduino_LED_Matrix.h"

Arduino_LED_Matrix matrix;

void LEDMatrix_setup() {
  matrix.begin();
  matrix.textFont(Font_4x6);
  matrix.textScrollSpeed(200);
  matrix.clear();

}


void tLEDmatrix() {
  int count;
  struct Package_data  cData;
  
  while(1){
  
      int ret = k_msgq_get(&qData,&cData, K_MSEC(4000));
      
      if (ret == 0){
        matrix.clear();
        count = cData.pos;
        matrix.beginText(0, 1, 1, 0, 0); // X, Y, then R, G, B
        matrix.print(String(count));
        matrix.endText();
  
     }else{
        matrix.beginText(0, 1, 1, 0, 0); // X, Y, then R, G, B
        matrix.print("Err");
        matrix.endText();
     }

    
  }
}

Thread 4

#include "Modulino.h"
ModulinoKnob encoder;

#define PLUSEPERROT 30

void enc_setup(){
  Modulino.begin();
  encoder.begin();
  pinMode(LED4_B, OUTPUT);
  
}

void tEncoder(){
  
  struct Package_data pData;
  int SampleCycle = 10;
  int PastValue = 0;
  int LoopCounter = 0;
  
  while(1){
    //Read encoder value
    int value = encoder.get();

    //Reset the count when pressed
    if (encoder.isPressed()){
      encoder.set(0);
      PastValue = 0;
    }

    //Pack the Position to send
    pData.pos = value;
    
    //Pack the speed
    pData.speed = ((value-PastValue)/PLUSEPERROT) * ((60/1000)*SampleCycle);
    PastValue = value;

    //Place data in queue to send to LEDMatrix
    int ret = k_msgq_put(&qData, &pData, K_NO_WAIT);
    if(ret != 0){
     //No room in queue
    }
    //Place data in queue to send to MPU
    //int ret1 = k_msgq_put(&QmpuData, &pData, K_NO_WAIT);
    //if(ret1 != 0){
    // //No room in queue
    //}

    // Ramp LED
    LoopCounter++;
    if (LoopCounter> 255){LoopCounter = 0;}
    analogWrite(LED4_B, LoopCounter);

    //Pause task
    k_sleep(K_MSEC(SampleCycle));
    
  }
}

You will also need this header file:

extern struct k_msgq qData;
extern struct k_msgq QmpuData;


struct Package_data {
  int32_t pos;
  int32_t speed;
};

And here is the main Sketch:

#include "queue.h"

/* size of stack area used by each thread */
#define STACKSIZE 512

/* scheduling priority used by each thread */
#define PRIORITY 7

void setup() {
  blink3_setup();
  MPU_setup();
  LEDMatrix_setup();
  enc_setup();
}

// Queues
K_MSGQ_DEFINE(qData,sizeof(int32_t[2]),4,1);
K_MSGQ_DEFINE(QmpuData,sizeof(int32_t[2]),4,1);

// Threads
K_THREAD_DEFINE(blink3_id, STACKSIZE, tBlink3, NULL, NULL, NULL, PRIORITY, 0, 0);
K_THREAD_DEFINE(MPUsend_id, STACKSIZE, tMPUSend, NULL, NULL, NULL, PRIORITY, 0, 0);
K_THREAD_DEFINE(LEDM_id, STACKSIZE, tLEDmatrix, NULL, NULL, NULL, PRIORITY, 0, 0);
K_THREAD_DEFINE(ENC_id, STACKSIZE, tEncoder, NULL, NULL, NULL, PRIORITY, 0, 0);

void loop() {
  
   // Let Magic happen
  
}

This should build and run for anyone. The code will get the encoders value and display it on the LED Matrix by way of a queue between tasks. There is also a blinking led. If I add a simple blink into the tMPUSend thread all is fine.

I have simply provided you a learning curver which you like or not that is your full liberty; but, as a foum member you may maintain some kind of discipline.

See my post below, Hats off for the help.

Thank you.

Ok, Not a great solution but a working one.

Thanks to GolamMostafa,
Notes on the fix.
-I am current unable to run in PC mode so this was all done in SBC.

Fix, part 1:
Python source must work and run. used Mostafa's post as a base line.
Produced this:

import time
from arduino.app_utils import App, Bridge


def From_MCU(pos, speed):
    print(pos)
    print(speed)
    print("hello from mcu")


print("Hello world!")


def main():
    Bridge.provide("From_MCU", From_MCU)

    while True:
       time.sleep(10)

if __name__ == "__main__":
    main()

Fix, part 2: Delay in a spot.

#include "Arduino_RouterBridge.h"

void MPU_setup() {
  Bridge.begin();
  delay(5000); //<- Some of time worked
}

void tMPUSend() {
  struct Package_data  mcuData;
  delay(5000);  // This one seems to more of the time.
  
  while(1){
    int ret = k_msgq_get(&QmpuData,&mcuData, K_MSEC(4000));
    
    if (ret == 0){
       Bridge.call("From_MCU", mcuData.pos, mcuData.speed);
    }else{ //no message in queue
       Bridge.call("From_MCU", 0, 0);
    }
    delay(500); //Give Python app time 
  }
}

Still digging in to why but for now it is working. Seems we need a flag from the MPU to then ok the MCU function to send data via Bridge. Next, going to try a send via MPU to MCU as that flag. hoping that will remove the need for the random delay.

Final goal is to send as much data as I can to the MPU for logging. After this has proved stable going to work on packing more data into bigger packets for the bridge.

Also worth noting that the final 500ms delay is needed and I am sure can be tuned lower but that rate should work if I can pass larger data arrays.

Thanks all for the help. happy coding.

You cam with the problem of pushing UNO Q into nag state while deaing with Bridge.call() function along with thread creation.

How have you solved it -- by crtaeting thread using macro or create() function?

Post it as a zip file. 7-zip allows you to export as a zip. Better way is to use the export app function in applab which creates a zip file that you can post and makes it easy to import back into applab that anyone can use.

Here is my final solution to get the MCU to start sending data as MPU comes up.

In MPU, Note the "Bridge.call" comes before loop.

def From_MCU(pos, speed):
    global Data_Pos
    global Data_Speed                                 
    Data_Pos = float(pos)
    Data_Speed = float(speed)
    print(speed)


def get_events():
    print("Hello world!")
    Bridge.provide("From_MCU", From_MCU)
    print("Letting MCU know we are ready")
    Bridge.call("mpurunning")                         
                    
    while True:
        ts = int(datetime.datetime.now().timestamp() * 1000)
        
        
        db.write_sample('cpu', Data_Pos, ts)
        ui.send_message('cpu_usage', {"value": Data_Pos,"ts": ts})
        
        db.write_sample('mem', Data_Speed, ts)
        ui.send_message('memory_usage', {"value": Data_Speed ,"ts": ts})
        
        time.sleep(.1)

#if __name__ == "__main__":
#    main()

App.run(user_loop=get_events)

Next in the MCU task:

#include "Arduino_RouterBridge.h"

void MPU_Running() {
  gMPU_Running =1;
}

void MPU_setup() {
  Bridge.begin();
  Bridge.provide("mpurunning", MPU_Running);
}

void tMPUSend() {
  struct Package_data  mcuData;
  
  while(1){
    //Get data from queue if present
    int ret = k_msgq_get(&QmpuData,&mcuData, K_MSEC(4000));

    //Send data to the MPU if it is runing
    if (gMPU_Running == 1){
      if (ret == 0){
         Bridge.call("From_MCU", mcuData.pos, mcuData.speed);
      }else{ //no message in queue
         Bridge.call("From_MCU", 0, 0);
      }
    }
    delay(200); //Give Python app time 
  
  }
}

Main in the MCU looks like this, The "gMPU_Running" Var is a global var setup in the header.
Most likely will not need it there long term.

#include "queue.h"

/* size of stack area used by each thread */
#define STACKSIZE 512

/* scheduling priority used by each thread */
#define PRIORITY 7

bool gMPU_Running = 0;

void setup() {
  blink3_setup();
  MPU_setup();
  LEDMatrix_setup();
  enc_setup();
  
}

// Queues
K_MSGQ_DEFINE(qData,sizeof(int32_t[2]),1,1);
K_MSGQ_DEFINE(QmpuData,sizeof(int32_t[2]),1,1);

// Threads
K_THREAD_DEFINE(blink3_id, STACKSIZE, tBlink3, NULL, NULL, NULL, PRIORITY, 0, 0);
K_THREAD_DEFINE(MPUsend_id, STACKSIZE, tMPUSend, NULL, NULL, NULL, PRIORITY, 0, 0);
K_THREAD_DEFINE(LEDM_id, STACKSIZE, tLEDmatrix, NULL, NULL, NULL, PRIORITY, 0, 0);
K_THREAD_DEFINE(ENC_id, STACKSIZE, tEncoder, NULL, NULL, NULL, PRIORITY, 0, 0);

void loop() {
  
   // Let Magic happen
  
}

In short the MPU calls a function that sets a flag in the MCU to start sending. I need to account for a condition when the Bridge disconnects/Reconnects but that is on my todo list.

Next up InfluxDB and the Brick... New post time.