Teensy 3.x multithreading library first release

This is a great discussion and probably should be added to the Teensy wiki. Was doing some research on my own and found a simple tutorial on Mutex lock and unlock. I am including just for any beginners like me following this discussion. Mutex Tutorial and Example as well as Stackflow question which has a nice explanation as well. Here is another about sharing resources. Getting interested in this because am wondering if one thread can use info from another thread.
 
Given this code, I'm attempting to share the bus benchtesting an I2C device, but there is an issue, either with this library, or possibly i2c_t3? or perhaps im not doing it right?

if you decrease the timeslice, and/or start removing the delays, it seems the i2c bus locks up, comes back after resetting the arduino
not very ideal to have delays for multithreading hehe, there are no delays in my non-teensythreading code originally..

The chip is an MCP23017, very common to find anywhere if anyone wants to try out
address lines are all GND (0x20) and GPIO 6 and 15 are GND as well.


Code:
#include <Arduino.h>
#include "TeensyThreads.h"
#include <i2c_t3.h>

int count = 0;
const int LED = 13;

Threads::Mutex lock;
Threads::Mutex unlock;

void setup() {
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);
  Wire.setDefaultTimeout(200000); // 200ms
  pinMode(LED, OUTPUT);
  auto blinky = threads.addThread(blinkthread);
  //  threads.setMicroTimer(10);
  threads.setTimeSlice(blinky, 100);
}




void loop() {

  Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
  Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 2);
  byte x = Wire.read();
  byte y = Wire.read();
  if ( x == 191 && y == 127 ) {
    Serial.print("Wire Register Read Thread1: ");
    Serial.print(x);
    Serial.print(" : ");
    Serial.print(y);
    Serial.println(" GOOD!");
  }
  else {
    Serial.print("Wire Register Read Thread1: ");
    Serial.print(x);
    Serial.print(" : ");
    Serial.print(y);
    Serial.println(" FAIL!");
  }
  delay(20);
}

void blinkthread() {
  while (1) {
    //  Threads::Scope locker(lock);
    Threads::Mutex lock;
    Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
    Wire.beginTransmission(32); Wire.write(0x13); Wire.endTransmission(); Wire.requestFrom(32, 1);
    byte x = Wire.read();
    Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 1);
    byte y = Wire.read();
    if ( x == 127 && y == 191 ) {
      Serial.print("Wire Register Read Thread2: ");
      Serial.print(x);
      Serial.print(" : ");
      Serial.print(y);
      Serial.println(" GOOD!");
    }
    else {
      Serial.print("Wire Register Read Thread2: ");
      Serial.print(x);
      Serial.print(" : ");
      Serial.print(y);
      Serial.println(" FAIL!");
    }
    delay(125);
    Threads::Mutex unlock;
  }
}
 
or perhaps im not doing it right?
Yes, your locking is completely wrong - you aren't actually locking anything. BTW, it's not just I2C that's not thread-safe and needs locking, USB Serial is not thread safe either. In general, you should expect that most libraries are not thread-safe - even elementary stuff like malloc()/free() is not thread-safe.

The following should work (untested):
Code:
#include <Arduino.h>
#include "TeensyThreads.h"
#include <i2c_t3.h>

int count = 0;
const int LED = 13;

Threads::Mutex i2c_lock;
Threads::Mutex serial_lock;

void setup() {
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);
  Wire.setDefaultTimeout(200000); // 200ms
  pinMode(LED, OUTPUT);
  auto blinky = threads.addThread(blinkthread);
  //  threads.setMicroTimer(10);
  threads.setTimeSlice(blinky, 100);
}


void loop() {
  {
    Threads::Scope locker(i2c_lock);
    Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
    Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 2);
    byte x = Wire.read();
    byte y = Wire.read();
  }
  {
    Threads::Scope locker(serial_lock);
    if ( x == 191 && y == 127 ) {
      Serial.print("Wire Register Read Thread1: ");
      Serial.print(x);
      Serial.print(" : ");
      Serial.print(y);
      Serial.println(" GOOD!");
    }
    else {
      Serial.print("Wire Register Read Thread1: ");
      Serial.print(x);
      Serial.print(" : ");
      Serial.print(y);
      Serial.println(" FAIL!");
    }
  }
  delay(20);
}

void blinkthread() {
  while (1) {
    {
      Threads::Scope locker(i2c_lock);
      Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
      Wire.beginTransmission(32); Wire.write(0x13); Wire.endTransmission(); Wire.requestFrom(32, 1);
      byte x = Wire.read();
      Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 1);
      byte y = Wire.read();
    }
    {
      Threads::Scope locker(serial_lock);
      if ( x == 127 && y == 191 ) {
        Serial.print("Wire Register Read Thread2: ");
        Serial.print(x);
        Serial.print(" : ");
        Serial.print(y);
        Serial.println(" GOOD!");
      }
      else {
        Serial.print("Wire Register Read Thread2: ");
        Serial.print(x);
        Serial.print(" : ");
        Serial.print(y);
        Serial.println(" FAIL!");
      }
    }
    delay(125);
  }
}
 
the x and y bytes were out of the scopes so i fixed that:

I've done this now with default timeslice of 1ms

Code:
#include <Arduino.h>
#include "TeensyThreads.h"
#include <i2c_t3.h>

int count = 0;
const int LED = 13;

Threads::Mutex i2c_lock;
Threads::Mutex serial_lock;

void setup() {
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);
  Wire.setDefaultTimeout(200000); // 200ms
  pinMode(LED, OUTPUT);
  auto blinky = threads.addThread(blinkthread);
  // threads.setMicroTimer(10);
  threads.setTimeSlice(blinky, 1);
}


void loop() {
  byte x, y;
  {
    Threads::Scope locker(i2c_lock);
    Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
    Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 2);
    x = Wire.read();
    y = Wire.read();
  }
  {
    Threads::Scope locker(serial_lock);
    if ( x == 191 && y == 127 ) {
      Serial.print("Wire Register Read Thread1: ");
      Serial.print(x);
      Serial.print(" : ");
      Serial.print(y);
      Serial.println(" GOOD!");
    }
    else {
      Serial.print("Wire Register Read Thread1: ");
      Serial.print(x);
      Serial.print(" : ");
      Serial.print(y);
      Serial.println(" FAIL!");
    }
  }
  delay(1);
}

void blinkthread() {
  while (1) {
    byte x, y;


    {
      Threads::Scope locker(i2c_lock);
      Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
      Wire.beginTransmission(32); Wire.write(0x13); Wire.endTransmission(); Wire.requestFrom(32, 1);
      x = Wire.read();
      Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 1);
      y = Wire.read();
    }


    {
      Threads::Scope locker(serial_lock);
      if ( x == 127 && y == 191 ) {
        Serial.print("Wire Register Read Thread2: ");
        Serial.print(x);
        Serial.print(" : ");
        Serial.print(y);
        Serial.println(" GOOD!");
      }
      else {
        Serial.print("Wire Register Read Thread2: ");
        Serial.print(x);
        Serial.print(" : ");
        Serial.print(y);
        Serial.println(" FAIL!");
      }
    }

  }
}


For the "delay(1)" in the loop, I get this result:

Code:
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!

without the delay(1), it doesnt seem to be releasing access to the thread

Code:
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!

at least this time the bus is not locking up nor are the read/writes colliding, which is progress :)

Tony
 
You haven't set the time slice for the 'loop()' thread, which is thread 0: "threads.setTimeSlice(0, 1);".
 
same result:

Code:
  threads.setTimeSlice(0, 1);
  threads.setTimeSlice(blinky, 1);

Code:
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!

you can see a blip of Thread1 come in and go, mostly its Thread2.
But for:

Code:
  threads.setTimeSlice(0, 2);
  threads.setTimeSlice(blinky, 1);

theres more exchanges:
Code:
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!


and commenting out the delay(1) in the loop results in:

Code:
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!

forever
 
@tni. Looking at the code example that you provided and the explanation it appears that you are saying that anytime I am using serial or I2C I should be using locking before the calls to wire or serial. This would, at least to me, if I am using library calls that use wire I should wrap it in threads:Scope locker(i2c_lock); or would one time at the beginning of the loop suffic? Sorry, if this seems like a simple question but I have not time to dig into this yet. Working about three projections at one time (kind of round robining them). For instance if I am using lib calls to the BNO055 that is in a thread but it is inialized in setup.
 
Since the Teensy is a beginner platform and seeing that the locking mechanism is a bit hard to follow, I thought maybe there could be a simpler way to wrap existing libraries in order to lock/unlock for every call. But before I make any changes to the library I wanted to propose it here and get feedback. There are many ways to skin this cat and maybe the library should provide one.

The concept is to wrap a class with a "locking" class as shown below. 'Grab' would wrap the parent class, overriding the "->" operator to create a temporary class that then locks and unlocks at creation and destruction:

Code:
template <class C> class GrabTemp {
private:
  Threads::Mutex *lkp;
public:
  C *me;
  GrabTemp(C *obj, Threads::Mutex *lk) { me = obj; lkp=lk; lkp->lock(); }
  ~GrabTemp() { lkp->unlock(); }
};

template <typename T> class Grab {
private:
  Threads::Mutex lk;
  T *me;
public:
  Grab(T &t) {
    me = &t;
  }
  GrabTemp<T> grab() {
    return GrabTemp<T>(me, &lk);
  }
  operator T() {
    return *(grab().me);
  }
  T *operator->() {
    return grab().me;
  }
};

#define ThreadWrap(obj, name) Grab<typeof(obj)> name(obj);

Usage would be as follow:

Code:
ThreadWrap(Serial, SerialX);

int setup()
{
    SerialX->print(1);
    SerialX->print(2);
}

void loop() {
}

The one unavoidable side effect is that you can't use the '.' operator, as in 'Serial.print()' because it can't be overloaded.
 
@ftrias. Can't say if that method is the best way but it looks relatively easy to implement. As @tni pointed out there is almost nothing in the teensy/Arduino world that is thread safe so that appears to be a limiting factor for threading.

Now for the question with libraries like the BNO library. Since that is library class that has calls to wire to get data if I would have to call ThreadWrap(Wire, WireX); and that would take care of the library using the wire library. The one drawback is I would then have to modify the BNO library to change Wire.endtransmission(), for example to Wire -> endtransmission(). I would then need to have two libraries established, one thread safe and the other non-thread safe to use with other mcu's. Not a big deal but just trying to understand the implication.

I think this is what you saying here.

Respectfully
Mike

PS. Forgot to ask. If I modify the library (they all use classes) how I add threadwrap to other libraries with are classes with a .h and .cpp file?
 
Last edited:
@ftrias. Can't say if that method is the best way but it looks relatively easy to implement. As @tni pointed out there is almost nothing in the teensy/Arduino world that is thread safe so that appears to be a limiting factor for threading.

Now for the question with libraries like the BNO library. Since that is library class that has calls to wire to get data if I would have to call ThreadWrap(Wire, WireX); and that would take care of the library using the wire library. The one drawback is I would then have to modify the BNO library to change Wire.endtransmission(), for example to Wire -> endtransmission(). I would then need to have two libraries established, one thread safe and the other non-thread safe to use with other mcu's. Not a big deal but just trying to understand the implication.

I think this is what you saying here.

Respectfully
Mike

That is right. The library code would have to change from Wire.write() to WireX->write(). That seems like a lot of work. But once you change to WireX->write(), you can easily switch back to the thread-unsafe calls by eliminating "#include <TeensyThreads.h>" and "ThreadWrap(...)" and adding "#define WireX (&Wire)".

But perhaps you avoid this by wrapping the BNO library? I am unfamiliar with it, so I can't really say.
 
Automagic locking wrapper

@ftrias:

Your locking wrapper doesn't solve the problem. For I2C, you want an atomic group of calls (e.g. everything from beginTransmission() to endTransmission()). With your wrapper (which locks / unlocks for each method call), another thread can come in the middle of an I2C transaction.

For USB Serial, you probably want an atomic group of several print / write calls, so that you get complete output lines (with content from a single thread) and not jumbled lines with content from multiple threads.

Locking everything on a per method level is rarely good enough.
 
Now for the question with libraries like the BNO library. Since that is library class that has calls to wire to get data if I would have to call ThreadWrap(Wire, WireX); and that would take care of the library using the wire library. The one drawback is I would then have to modify the BNO library to change Wire.endtransmission(), for example to Wire -> endtransmission(). I would then need to have two libraries established, one thread safe and the other non-thread safe to use with other mcu's. Not a big deal but just trying to understand the implication.
That doesn't work. You must keep your lock for the entire SPI transaction, everything between beginTransaction/endTransaction. The wrapper locks/unlocks each method call, so another thread can come in and screw up your SPI transaction.

What may work for the SPI case is modifying beginTransaction to acquire a SPI lock/mutex and endTransaction to release it.
 
@tonton81:
Your timing appears to trigger very unfair thread scheduling. If a thread calls 'lock()', while another thread already hold the lock, it gives up its current time slice and calls Threads::yield(). When the thread gets scheduled again, the lock might be again be taken and so on.

You can ensure fair scheduling by calling yield:
Code:
#include <Arduino.h>
#include "TeensyThreads.h"

Threads::Mutex i2c_lock;
Threads::Mutex serial_lock;

void setup() {
  auto blinky = threads.addThread(blinkthread);
  threads.setTimeSlice(blinky, 1);
  threads.setTimeSlice(0, 1);
}

volatile uint32_t thread_cnt_b = 0;
volatile uint32_t thread_cnt_l = 0;

void loop() {
    {
        Threads::Scope locker(i2c_lock);
        thread_cnt_l++;
        delayMicroseconds(100);
    }
    {
        Threads::Scope locker(serial_lock);
        Serial.print("Thread  (loop): ");
        Serial.print(thread_cnt_l);
        Serial.print("  ");
        Serial.println(thread_cnt_b);
    }
    threads.yield();
}

void blinkthread() {
    while (1) {
        {
            Threads::Scope locker(i2c_lock);
            thread_cnt_b++;
            delayMicroseconds(100);
        }
        {
            Threads::Scope locker(serial_lock);
            Serial.print("Thread (blink): ");
            Serial.print(thread_cnt_l);
            Serial.print("  ");
            Serial.println(thread_cnt_b);
        }
        threads.yield();
    }
}
 
@ftrias:

Your locking wrapper doesn't solve the problem. For I2C, you want an atomic group of calls (e.g. everything from beginTransmission() to endTransmission()). With your wrapper (which locks / unlocks for each method call), another thread can come in the middle of an I2C transaction.

For USB Serial, you probably want an atomic group of several print / write calls, so that you get complete output lines (with content from a single thread) and not jumbled lines with content from multiple threads.

Locking everything on a per method level is rarely good enough.

I agree that ideally, you want to place the locks by hand, but perhaps this is too hard for beginner coders. For Serial, if the output is interspersed, that's not ideal, but it's probably acceptable.

For Wire/SPI, etc. we could write simple classes that like you mention later:

What may work for the SPI case is modifying beginTransaction to acquire a SPI lock/mutex and endTransaction to release it.

I was also thinking that for this kind of thing, we need a critical section or something like it that will prevent a context switch because there may be timeouts and other problems if there is a long delay between calls. Maybe Wire doesn't have this problem, but I can imagine other libraries might.

Something like the code below:

Code:
class WireX : public Wire {
int mstate;
public:
int beginTransaction(...) { mstate = Threads::stop(); Wire::beginTransaction(...); }
int endTransaction(...) { Wire::endTransaction(...); Threads::start(mstate); }
};
 
@ftrias. The BNO055 is a 9DOF Orientation Sesor (IMU) with sensor fusion built in. It is like a MPU-6050 IMU, if you are familiar with that sensor.

I agree that ideally, you want to place the locks by hand, but perhaps this is too hard for beginner coders. For Serial, if the output is interspersed, that's not ideal, but it's probably acceptable.

All I can say is that I agree with you here, since I am a beginner. I use serial.debug statements all over my sketches. To be honest unless I am using threading for a critical application, if I had to worry about that approach I would probably opt for a different one than threads (from beginners stand point). I think the something like the approach that you mentioned at the end of your last message would be advisable.

One of the other things I am looking at with threads is to use it to collect GPS data besides, iMU, sonar, IR or sensor data. GPS can use either I2C or SerialX. I am also using 3dR type radio on SerialY to transmit telemetry. Now, if one had to hand coding locks/unlocks it might get more complicated than its worth which would support your suggestion
we need a critical section or something like it that will prevent a context switch because there may be timeouts and other problems if there is a long delay between calls.

I am also assuming you would need something for Serial as well.
 
isnt it possible just to make the mutex locks "blocking"? think while(1);... stop threads for a "blocking mutex" just to give a higher priority to another thread, then the other threads continue when mutex is released, not good?
 
isnt it possible just to make the mutex locks "blocking"? think while(1);... stop threads for a "blocking mutex" just to give a higher priority to another thread, then the other threads continue when mutex is released, not good?

I suppose this is one reason why most real OSes implement priority scheduling for threads, as opposed to just doing running them in a fixed order. Are you suggesting something like the following? When a mutex lock is blocked because it is locked by another thread, the thread waiting for the lock is suspended and put on a queue. When the lock is released, the suspended thread goes to the front of the execution line is executed on the next context switch.
 
yeah, some people can work with that, although its.blocking, we can give priority to the threads that matter

for variable work or processing its okay, but shared spi/i2c if we have no chiice then all we can do is prioritize while blocking seems like a good alternative
 
Last edited:
yeah, some people can work with that, although its.blocking, we can give priority to the threads that matter

for variable work or processing its okay, but shared spi/i2c if we have no chiice then all we can do is prioritize while blocking seems like a good alternative

I am not really sure what your are proposing. Can you be more specific?
 
A quick question on blocking. What effect would this have on threads that are dependent on events occurring such as for an IMU that uses I2C. The event is data ready. For sonar sensors timer interrupts like in the newping eample. Right now I am using both and neither seems to be having an issue Serial since Serial is only in the main thread.
 
are the i2c devices on the same bus? if yes this is what im trying to deal with in the mutex if possible
if your using each i2c bus in a different thread, yes it will work fine

a workaround must exist to read/write a shared bus as demonstrated in my last example where each thread is writing and reading over the same i2c bus, the only issue, but one or the other thread will skip the code access because the bus is locked already, so the thread misses an event this way, this is why i suggested for this type of scenario, if options are not available, that maybe we can block (stop ALL threads including loop) while the mutex is active until released, this can possibly be a new mutex with a custom lock that does this block for some people who would like to use it to prioritize a thread as if it were ISR based.

the reason is not everyone has multiple devices on the same i2c/spi bus, and, for cases like mine, im having data flowing from 2 threads to one LCD in my project, you can see where there would be issues when 2 threads try to output over the same uart using the same class

if you run 2 lcds like i do you would visually see a difference between both running code in same loop and running it in a different thread, the speed of the transfers and animations are noticable, but collision might occur, as in my case one of the functions of the lcd triggered an spi gpio, which was constantly polled read/write in loop, and the gpios were going crazy, so im willing to help out testing stuff if necessary
 
Last edited:
by the way i commented out the timeslices right now since i had the same sketch open and setMicroTimer(1) to 1.

I'm getting this output:

Code:
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread2: 127 : 191 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!

it looks like it's working sequencially, WITHOUT delays, settimeslice needs delays to allow the other thread to catch and lock the mutex i guess, but at 1usec on microtimer i guess the switching happens so fast that the other thread gets enough time to lock onto it (i guess)

at 10uSec microtimer the output is:

Code:
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!
Wire Register Read Thread1: 191 : 127 GOOD!

here you can see, without the delays, it gives no chance for the other thread to process anything,
if a queue system is possible, thatll be the way to go for data integrity, but, this way, for sure some transactions will be missed or ignored altogether
 
Last edited:
@tonton81, I've updated the TeensyThreads library so that when a mutex is locked, the next thread to request the lock will be suspended until the lock is released. When it is released, the waiting thread will be executed on the next context switch. It's not a queue, since it will only work on one thread. If two threads try to lock a mutex that is already locked, then only the first one will be put in this wait state. If this turns out to be helpful, then we can expand it to a queue.

https://github.com/ftrias/TeensyThreads

Revision 0.3: April 2017
1. Rename Threads::Lock to Threads::Suspend
2. Add setSliceMicros() and setSliceMillis()
3. "lock" will suspend blocking thread until "unlock" and then give thread priority
4. Add ThreadWrap macro and supporting classes

The new ThreadWrap macro is for wrapping simple classes like Serial with locks. It works as follows:

Code:
ThreadWrap(Serial, SerialX);
#define Serial ThreadClone(SerialX)

int thread_func()
{
    Serial.println("begin");
}
 
Back
Top