Teensy 3.x multithreading library first release

yes uart, most people use widgets but i decided to learn and use photoshop to make animations. beware this lcd has a mcu built in and can run standalone, however, i prefer to use teensy as host since thats what im familiar with

im also jumping 10 frames as per teensy code, however, theres 230 frames i can incrementally go smooth, if i need smoother i can just redo the images again in photoshop with... 500 frames ;) this is where the map function will save you time :)
 
IMG_0042.jpgwel they have many but i have the 7" as per video, uLCD-70DT
the code is dead simple one liners in teensy/arduino, not pages and pages of lcd code
the images and lcd layout, theres a separate IDE for that which even a non programmer can get up and running, once i photoshop the custom digits i can "upload" it to the car lcd
 
Last edited:
Yes 4d systems i came across their lcds once but they a expensive:) thank you for your help. But I am affraid some people are going to be very angry. This thread is about multitasking :) i hope the admin can forgive me for trashing the thread and if he can delete the posts :)
 
i sorta hope this thread turns out well, its amazing how much throughput of data goes during canbus traffic and 24/7 read/write, multitasking can be necessary
 
What kind of displays do you use is not these? Graphical? I have a ili9341 laying around:) maybe i can adapt the code

That is what I am using right now... But again all of my stuff is just for my own enjoyment...

Edit: Maybe should break most of this out to new or existing thread as to not totally distract from the multithreading library... Sorry
 
Last edited:
Can someone explain me the thread.delay() function? And what if I woul use delay instead of thread.delay()? Woul it stop both threads?

A regular "delay(100)" would use up 100 ms of CPU time doing nothing in a while loop. "threads.delay(100)" would give these 100 ms of CPU time to another thread. So by using "threads.delay()" instead of "delay()" you are making your other threads run faster.
 
Hi Tady

I finally got around to trying to use the lib in an actually situation = Reading 4 Sonar Sensors using the newping library example. The problem I am seeing is that the sensors are not always returning results using it in a multithread test. I am not sure if its me in how I am coding or maybe some conflict with the newping library. There are a couple of other ways to implement this but I wanted to what the problem is with this example first. One other thing is it seems very choppy and slower than just calling the function.

Thanks
Mike

Here are the results:

Code:
0=0cm 1=13cm 2=21cm 3=44cm 
0=0cm 1=0cm 2=20cm 3=45cm 
0=16cm 1=0cm 2=21cm 3=45cm 
0=16cm 1=999cm 2=0cm 3=29cm 
0=16cm 1=13cm 2=0cm 3=39cm

The actual code I am using for the test is:

Code:
#include <Arduino.h>
#include "Threads.h"
#include <NewPing.h>

#define SONAR_NUM     4 // Number or sensors.
#define MAX_DISTANCE 200 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 45 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(27, 26, MAX_DISTANCE), //lower left sensor
  NewPing(36, 35, MAX_DISTANCE), //front middle
  NewPing(30, 29, MAX_DISTANCE), //lower rigth sensro
  NewPing(34, 33, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
};

int id1, id2, id3;
volatile int p1 = 0;
volatile int p2 = 0;
volatile int p3 = 0;
#define delayx delay

void my_priv_func1(){
  while(1){
  for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through all the sensors.
    if (millis() >= pingTimer[i]) {         // Is it this sensor's time to ping?
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  // Set next time this sensor will be pinged.
      if (i == 0 && currentSensor == SONAR_NUM - 1) oneSensorCycle(); // Sensor ping cycle complete, do something with the results.
      sonar[currentSensor].timer_stop();          // Make sure previous timer is canceled before starting a new ping (insurance).
      currentSensor = i;                          // Sensor being accessed.
      cm[currentSensor] = 0;                      // Make distance zero in case there's no ping echo for this sensor.
      sonar[currentSensor].ping_timer(echoCheck); // Do the ping (processing continues, interrupt will call echoCheck to look for echo).
    }
  }
    //threads.yield();
  }
}
  
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  delay(5000);
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;

  // put your main code here, to run repeatedly:
  Serial.print("Thread start ");
  id1 = threads.addThread(my_priv_func1,1);
  if (threads.getState(id1) == Threads::RUNNING) Serial.println("OK");
}


void echoCheck() { // If ping received, set the sensor distance to array.
  if (sonar[currentSensor].check_timer()){
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
  } else {
    cm[currentSensor] = 999; 
  }
}

void oneSensorCycle() { // Sensor ping cycle complete, do something with the results.
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    Serial.print(i);
    Serial.print("=");
    Serial.print(cm[i]);
    Serial.print("cm ");
  }
  Serial.println();
}

void loop() {

}
 
Last edited:
Me again. Think its a conflict with the example for multiple pings in the library. I re-wrote and heres the results follow. Any suggestions for making this more efficient would be appreciated. Want to do some timing tests and will let you know how it works out.

Code:
Thread start OK
0=200cm 1=15cm 2=15cm 3=19cm 
0=21cm 1=15cm 2=16cm 3=18cm 
0=21cm 1=15cm 2=15cm 3=18cm 
0=21cm 1=15cm 2=15cm 3=18cm 
0=21cm 1=15cm 2=16cm 3=18cm

This is the version that worked:
Code:
#include <Arduino.h>
#include "Threads.h"
#include <NewPing.h>

#define SONAR_NUM     4 // Number or sensors.
#define MAX_DISTANCE 200 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 45 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.


NewPing sonarll(27, 26, MAX_DISTANCE); //lower left sensor
NewPing sonarlc(36, 35, MAX_DISTANCE); //front middle
NewPing sonarlr(30, 29, MAX_DISTANCE); //lower rigth sensro
NewPing sonarhd(34, 33, MAX_DISTANCE); // Each sensor's trigger pin, echo pin, and max distance to ping.


int id1, id2, id3;
volatile int p1 = 0;
volatile int p2 = 0;
volatile int p3 = 0;
#define delayx delay

void my_priv_func1(){
  while(1){
    cm[0] = 0;  
    unsigned int uS = sonarll.ping();
    //unsigned int uS = sonarll.ping_median();
    cm[0] = uS / US_ROUNDTRIP_CM;
    threads.delay(PING_INTERVAL);  
  
    cm[1] = 0;  
    uS = sonarlc.ping();
    //uS = sonarlc.ping_median();
    cm[1] = uS / US_ROUNDTRIP_CM;
    threads.delay(PING_INTERVAL); 
  
    cm[2] = 0;  
    uS = sonarlr.ping();
    //uS = sonarlr.ping_median();
    cm[2] = uS / US_ROUNDTRIP_CM;
    threads.delay(PING_INTERVAL);

    cm[3] = 0;  
    uS = sonarhd.ping();
    uS = sonarhd.ping_median();
    cm[3] = uS / US_ROUNDTRIP_CM;
    threads.delay(PING_INTERVAL);

    for (uint8_t i = 0; i < SONAR_NUM; i++) {
      if(cm[i] == 0) cm[i] = MAX_DISTANCE;
     }
    oneSensorCycle();
  }
}
  
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  delay(5000);
  Serial.print("Thread start ");
  id1 = threads.addThread(my_priv_func1,1);
  if (threads.getState(id1) == Threads::RUNNING) Serial.println("OK");
}


void oneSensorCycle() { // Sensor ping cycle complete, do something with the results.
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    Serial.print(i);
    Serial.print("=");
    Serial.print(cm[i]);
    Serial.print("cm ");
  }
  Serial.println();
}

void loop() {

}
 
I'm afraid I don't have a sonar module handy, but the one issue that sticks out in the code is that "currentSensor" can change between being set in my_priv_func1() and triggering echoCheck(). You set currentSensor in my_priv_func1(), set the echoCheck() callback and then the code keeps running; the loop goes to the next sensor and if it's trigger time is up, it will change currentSensor. If all this happens before echoCheck() is called by the NewPing library, then echoCheck will be looking at the wrong currentSensor.

There could be other issues at play with the threading library
 
Any suggestions for making this more efficient would be appreciated.

Efficient in terms of total CPU time used? Or how frequently you can read the sensors?

This is just a suggestion I can't test. I think you can ping all sensors at the same time and then read the results as they come in. For example, you can call ping_trigger() for all the sensors at once and then have a section (or a "for" loop, as in your original example with the array of sensors) that calls "check_timer()" for each sensor. If it returns true, then you check the time and have your distance. When you collect data for all sensors (or a timeout occurs), then you break out of the loop.

EDIT: fix text to call ping_trigger() rather than ping()
 
Last edited:
@ftrias. Thanks for explaining the issue with the first test sketch. Think you are correct in your assessment. Always nice to know why something doesn't work one way but works another way. So I never ignore responses. they all get stored away for future use. Have to think about all sensor triggering at the same time, hmm, thinking about the echo - one sensor may read another sensors echo. Best way to do it is to give it a try I guess.
 
@ftrias I managed to create three threads: One for reading sonar sensors, one for IR sensors and a third for reading a BNO055. It all works. Since this is my first attempt at using threads was wondering if you could take a look at the way I implemented it and let me know if I need any changes for using threads. Once this is finished I will be incorporating it into my teensy autonomous rover to see how it works. One thing I did notice is the BNO055 reads seem to slow the sensor reads way down when using threading. Not sure why.

Oh, whats the best way to time the threads. Just curious.

Thanks again for your great library.


Anyway here it is:
Code:
#include <Arduino.h>
#include "Threads.h"
#include <NewPing.h>
#include <Streaming.h>
#include <vector>
#include <EEPROM.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>

/* Set the delay between fresh samples for BNO055*/
#define BNO055_SAMPLERATE_DELAY_MS (100)
Adafruit_BNO055 bno = Adafruit_BNO055(55);

float yar_heading, pitch, roll;

#define telem Serial

#define SONAR_NUM     4 // Number or sensors.
#define MAX_DISTANCE 200 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 45 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

NewPing sonarll(27, 26, MAX_DISTANCE); //lower left sensor
NewPing sonarlc(36, 35, MAX_DISTANCE); //front middle
NewPing sonarlr(30, 29, MAX_DISTANCE); //lower rigth sensro
NewPing sonarhd(34, 33, MAX_DISTANCE); // Each sensor's trigger pin, echo pin, and max distance to ping.

//IR Sensor Pins
const int leftIRsensor = A22;   //Front
const int rightIRsensor = A2;   //Rear
int frtIRdistance, rearIRdistance;
int max_IR_distance = 200;

int id1, id2, id3;

void my_priv_func1(){
  while(1){
    cm[0] = 0;  
    unsigned int uS = sonarll.ping();
    //unsigned int uS = sonarll.ping_median();
    cm[0] = uS / US_ROUNDTRIP_CM;
    threads.delay(PING_INTERVAL);  
  
    cm[1] = 0;  
    uS = sonarlc.ping();
    //uS = sonarlc.ping_median();
    cm[1] = uS / US_ROUNDTRIP_CM;
    threads.delay(PING_INTERVAL); 
  
    cm[2] = 0;  
    uS = sonarlr.ping();
    //uS = sonarlr.ping_median();
    cm[2] = uS / US_ROUNDTRIP_CM;
    threads.delay(PING_INTERVAL);

    cm[3] = 0;  
    uS = sonarhd.ping();
    //uS = sonarhd.ping_median();
    cm[3] = uS / US_ROUNDTRIP_CM;
    threads.delay(PING_INTERVAL);

    for (uint8_t i = 0; i < SONAR_NUM; i++) {
      if(cm[i] == 0) cm[i] = MAX_DISTANCE;
     }
  }
}


void my_priv_func2(){
  while(1){  
  int sum = 0;
  for (int i=0; i<3; i++) {
    int sensor_value = analogRead(leftIRsensor);  //read the sensor value
    if(sensor_value < 100){
      sensor_value = 100; 
    }
    sum = sum + sensor_value;
    threads.delay(5);
    }
    frtIRdistance = 27495 * pow(sum/3,-1.36); //convert readings to distance(cm)

  sum = 0;
  for (int i=0; i<3; i++) {
    int sensor_value = analogRead(rightIRsensor);  //read the sensor value
    if(sensor_value < 100){
      sensor_value = 100; 
    }
    sum = sum + sensor_value;
    threads.delay(5);
  }
  rearIRdistance = 25445 * pow(sum/3,-1.362); //convert readings to distance(cm)
  }
}


void my_priv_func3(){
  while(1){
    sensors_event_t event;
    bno.getEvent(&event);
    imu::Vector<3> euler = bno.getVector(Adafruit_BNO055::VECTOR_EULER);

    roll = (float)event.orientation.y;
    pitch = (float)event.orientation.z;
    yar_heading = (float)event.orientation.x;
  }
}


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Wire.begin(400000);
  
  BNO055_Init();

  delay(5000);
  Serial.print("Thread start ");
  id1 = threads.addThread(my_priv_func1,1);
  id2 = threads.addThread(my_priv_func2,1);
  id3 = threads.addThread(my_priv_func3,1);
  if (threads.getState(id1) == Threads::RUNNING) Serial.println("Sonar thread started");
  if (threads.getState(id2) == Threads::RUNNING) Serial.println("IR thread started");
  if (threads.getState(id3) == Threads::RUNNING) Serial.println("BNO055 thread started");
}


void oneSensorCycle() { // Sensor ping cycle complete, do something with the results.
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    Serial.print(i);
    Serial.print("=");
    Serial.print(cm[i]);
    Serial.print("cm ");
  }
  Serial.println();
  telem << "IR Distances: " << frtIRdistance << " -- " << rearIRdistance << endl;
  telem << -roll << "," << -pitch << "," << yar_heading << endl;

}

void loop() {
    oneSensorCycle();
}
 
@mjs513, I'll give it my best, but without hardware or software to test, it's hard to say.

One of the problems with threads is that it slices up run time into 100 millisecond intervals (this can be shortened). So things like this echo, which require very precise timing, probably won't work if you are caught between thread switches. For example, if you send out the "ping" and are waiting for the "ping" to return, expecting it to take 20 microseconds, but the thread decides to switch, your thread will have to wait 100 ms before the it will get CPU time again. So it will seem to your thread that over 100ms have elapsed. This could be fixed by rewriting the NewPing library to use interrupts, but that's some work. Or you could disable interrupts with NoInterrupts() while waiting for your pings to return.

Also, when you call "threads.delay()" you are handing over your thread's time to the next slice, and it's not very accurate. So in real time, you may loose up to 100ms for each one of those. One way to minimize this is to reduce the time slice time with "setTimeSlice()". I really should have done a better job setting a global time slice and making the delay function more accurate. But in my defense, this is just the first version, and threads are just not a good way to do time-sensitive tasks.

Hope that helps.
 
@ftrias First and foremost. For a first release of a library this is working absolutely great. I am learning as I go here so excuse me if I don't always make sense. The library is great and I hope more people use it and give you feedback on it. Second thanks for the info on the time slices. Things are beginning to make a little more sense.

Just for clarity the sonar and IR threads are working great now. The problem, which I have an idea on, is with the BNO055 IMU. Think it uses some sort of interrupt for when the data is ready. In that case I would have to change the time slice or not use it within the thread. If you want I will keep you posted on how it works out.

Thanks for all your help.
Mike

UPDATE: Changed the time slice on the BNO055 thread and working a lot better.
 
Last edited:
@mjs513

The default timeslice is WAY too large. Setting all thread timeslices to 1 as opposed to the default 100 results in about 1% overhead. Make sure to include thread 0 (which executes loop()): "threads.setTimeSlice(0, 1);".

Using a different timer with 100us ticks (the systick timer used by default has 1000us ticks), results in about 3% overhead with 1 tick timeslices.
 
@tni

Took your suggestion and saw a dramatic improvement. However, had to change the timeslice for the sonar sensors to get reasonable results to threads.setTimeSlice(1,25). Probably could be lower, value was just off the top of my head. Thanks for the suggestions and the info.
 
The 100 millisecond thread slice time comes from the default Linux slice time, before the scheduling was changed to a dynamic system called CPS. I looked briefly at doing a dynamic scheduling but it seems like overkill.

To make it easier, I can add a "threads.setDefaultTimeSlice(...)" that will change the time slice for all new threads create.

@tni, how did you implement the 100us slices? I looked at using IntervalTimer (or directly the PIT timer) but maybe there is a better way.
 
Last edited:
@tni, how did you implement the 100us ticks? I looked at using IntervalTimer (or directly the PIT timer) but maybe there is a better way.
I used IntervalTimer. I wouldn't use the PIT directly (to minimize conflicts with other code wanting to use the timers).

There is a ton of other timers that can trigger interrupts, but IntervalTimer / PIT is probably the least likely to cause conflicts.
 
@tni, would you mind sharing your code so other (including me) can try it?
The change is pretty trival. I renamed "systick_isr" in thread.cpp and removed the systick increment:
Code:
//void __attribute((naked)) systick_isr(void)
void __attribute((naked)) ctx_switch_timer_isr(void) {
    //systick_millis_count++;

In the sketch:
Code:
IntervalTimer ctx_switch_timer;
extern void ctx_switch_timer_isr();
void setup() {
    ctx_switch_timer.begin(ctx_switch_timer_isr, 100);
    ...
 
Last edited:
Back
Top