Richard-Gemmell / teensy4_i2c

An I2C library for the Teensy 4. Provides slave and master mode.
MIT License
92 stars 19 forks source link

analogWrite interferes with slave/listener receiving data #15

Closed niek-dewit closed 3 years ago

niek-dewit commented 3 years ago

Hi @Richard-Gemmell I've been trying to use your library to exchange data between 2 teensy 4.0 micro-controllers. The master controller provides the listening controller with sound information, which the listening controller than turns into audio signals with the help of the build in analogWrite functionality.

I ran into an issue that the listener seemed to stop listening/receiving as soon as analogWrite was called.

I made a really simple setup to reproduce this issue. On my own setup, the listener stopped listening instantaneously after calling analogWrite, but in this simplified example it takes a couple seconds before the issue appears.

My controllers are connected to each other on pins 18 & 19.

I am not sure if this is an actual issue in the library, or if this is an issue in my example code?

Master code:


#include <Arduino.h>
#include <i2c_device.h>

I2CMaster& master = Master;

const uint8_t listener_address = 127;
uint8_t buff[513] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
unsigned int speaking_length = 10;

void setup() {
    master.begin(400 * 1000U);
    Serial.begin(9600);
    Serial.println("Started speaking");
}

void loop() {
    master.write_async(listener_address, buff, speaking_length, true);
    Serial.println("I spoke: ");
    for(unsigned int i = 0; i < speaking_length; i ++) {
        Serial.print(buff[i]); Serial.print(",");
    }
    Serial.println();
    delay(100);
}

Slave code:

#include <Arduino.h>
#include <i2c_driver.h>
#include "imx_rt1060/imx_rt1060_i2c_driver.h"

const uint8_t listener_address = 127;

I2CSlave& listener = Slave;
const size_t listening_buffer_size = 513;
uint8_t listening_buffer[listening_buffer_size] = {};
volatile size_t listener_bytes_received = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("Started listening");
  listener.after_receive(after_receive);
  listener.set_receive_buffer(listening_buffer, listening_buffer_size);
  listener.listen(listener_address);
  //analogWrite(0, 1); //uncomment this line, and the slave will eventually stop listening
}

void loop() {
  Serial.println("Still alive!");
  delay(100);
}

void after_receive(size_t length, uint16_t address) {
  Serial.println("I received: ");
  for(unsigned int i = 0; i < length; i ++) {
    Serial.print(listening_buffer[i]); Serial.print(",");
  }
  Serial.println();
}

Master output:

I spoke: 
0,1,2,3,4,5,6,7,8,9,
I spoke: 
0,1,2,3,4,5,6,7,8,9,
I spoke: 
0,1,2,3,4,5,6,7,8,9,
I spoke: 
0,1,2,3,4,5,6,7,8,9,
...

Slave output (analogWrite line commented out):

Still alive!
I received: 
0,1,2,3,4,5,6,7,8,9,
Still alive!
I received: 
0,1,2,3,4,5,6,7,8,9,
Still alive!
I received: 
0,1,2,3,4,5,6,7,8,9,
Still alive!
I received: 
0,1,2,3,4,5,6,7,8,9,
...

Slave output (analogWrite line enabled):

0,1,2,3,4,5,6,7,8,9,
Still alive!
I received: 
0,1,2,3,4,5,6,7,8,9,
Still alive!
I received: 
0,1,2,3,4,5,6,7,8,9,
... after 10 or so seconds..:
Still alive!
I received: 
0,1,2,3,4,5,6,7,8,9,
Still alive!
Still alive!
Still alive!
Still alive!
Still alive!
Still alive!
Still alive!
niek-dewit commented 3 years ago

cool! I tried the new code and now I am definitely seeing error12 reported. I thought maybe at this point I would be able to put in a hacky fix by connecting the 2 controllers with a third signal wire. I call this the "stuck bus signal wire". Basically just really simple, master on OUTPUT, slave on INPUT. When error12 is detected on the master, it puts the wire HIGH, and when slave reads HIGH on the wire, it will execute listener.listen(listener_address);.

This actually kind of works! The controllers are able to recover very often. (but it is not perfect yet) This is how the logs look like now, kind of collecting errors here and there, but still continuing: (slave left, master right) image

Then after a bit I am seeing this error: image

And then this happens: image

Here is another run: image

And another: image

Here is my code now: master:

#include <Arduino.h>
#include <i2c_device.h>

// Blink the LED to make sure the Teensy hasn't hung
volatile bool led_high = false;
void blink_led();

I2CMaster& master = Master;

const uint8_t listener_address = 127;
uint8_t buff[513] = {0xAA, 0xFF, 0x55, 0x00};
unsigned int speaking_length = 4;

unsigned long noError = 0;
unsigned long error0 = 0;
unsigned long error1 = 0;
unsigned long error2 = 0;
unsigned long error3 = 0;
unsigned long error4 = 0;
unsigned long error5 = 0;
unsigned long error6 = 0;
unsigned long error7 = 0;
unsigned long error8 = 0;
unsigned long error9 = 0;
unsigned long error10 = 0;
unsigned long error11 = 0;
unsigned long error12 = 0;

uint8_t stuck_bus_pin = 12;

elapsedMillis timeMillis = 0;
void setup() {
    // Turn the LED on
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, true);

    pinMode(stuck_bus_pin, OUTPUT);
    digitalWrite(stuck_bus_pin, false);

    master.begin(100 * 1000U);
    Serial.begin(9600);
    delay(2000);
}

void loop() {
    bool finished = master.finished();
    if (finished)
    {
        if (master.has_error()) {
            switch((int)master.error()) {
              case 0: {
                error0++;
                break;
              }
              case 1: {
                error1++;
                break;
              }
              case 2: {
                error2++;
                break;
              }
              case 3: {
                error3++;
                break;
              }
              case 4: {
                error4++;
                break;
              }
              case 5: {
                error5++;
                break;
              }
              case 6: {
                error6++;
                break;
              }
              case 7: {
                error7++;
                break;
              }
              case 8: {
                error8++;
                break;
              }
              case 9: {
                error9++;
                break;
              }
              case 10: {
                error10++;
                break;
              }
              case 11: {
                error11++;
                break;
              }
              case 12: {
                error12++;
                digitalWriteFast(stuck_bus_pin, true);
                break;
              }
            }
        } else {
            noError++;
            digitalWriteFast(stuck_bus_pin, false);
        }
        Serial.print("noError: "); Serial.println(noError);
        Serial.print("error0: "); Serial.println(error0);
        Serial.print("error1: "); Serial.println(error1);
        Serial.print("error2: "); Serial.println(error2);
        Serial.print("error3: "); Serial.println(error3);
        Serial.print("error4: "); Serial.println(error4);
        Serial.print("error5: "); Serial.println(error5);
        Serial.print("error6: "); Serial.println(error6);
        Serial.print("error7: "); Serial.println(error7);
        Serial.print("error8: "); Serial.println(error8);
        Serial.print("error9: "); Serial.println(error9);
        Serial.print("error10: "); Serial.println(error10);
        Serial.print("error11: "); Serial.println(error11);
        Serial.print("error12: "); Serial.println(error12);

        // Send the next message
        master.write_async(listener_address, buff, speaking_length, true);

    } else {
        //
    }

    if(timeMillis > 500) {
        blink_led();
        timeMillis = 0;
    }
    delay(1);
}

void blink_led() {
    led_high = !led_high;
    digitalWrite(LED_BUILTIN, led_high);
}

slave:

#include <Arduino.h>
#include <i2c_driver.h>
#include "imx_rt1060/imx_rt1060_i2c_driver.h"

// Blink the LED to make sure the Teensy hasn't hung
volatile bool led_high = false;
void blink_led();

const uint8_t listener_address = 127;

I2CSlave& listener = Slave;
const size_t listening_buffer_size = 513;
uint8_t listening_buffer[listening_buffer_size] = {};
uint8_t listening_buffer_2[listening_buffer_size] = {};
volatile size_t listener_bytes_received = 0;
void after_receive(size_t, uint16_t);

elapsedMillis timeMillis = 0;

unsigned long correct = 0;
unsigned long incorrect = 0;

unsigned long noError = 0;
unsigned long error0 = 0;
unsigned long error1 = 0;
unsigned long error2 = 0;
unsigned long error3 = 0;
unsigned long error4 = 0;
unsigned long error5 = 0;
unsigned long error6 = 0;
unsigned long error7 = 0;
unsigned long error8 = 0;
unsigned long error9 = 0;
unsigned long error10 = 0;
unsigned long error11 = 0;
unsigned long error12 = 0;

uint8_t stuck_bus_pin = 13;

void setup() {
    // Turn the LED on
   // pinMode(LED_BUILTIN, OUTPUT);
   // digitalWrite(LED_BUILTIN, true);

    pinMode(stuck_bus_pin, INPUT);

    Serial.begin(9600);
    Serial.println("Started listening");
    listener.after_receive(after_receive);
    listener.set_receive_buffer(listening_buffer, listening_buffer_size);
    listener.listen(listener_address);
    analogWrite(0, 1);
    delay(2000);

}

void loop() {
    if (listener_bytes_received) {
        if(listening_buffer[0] == 0xAA && listening_buffer[1] == 0xFF && listening_buffer[2] == 0x55 && listening_buffer[3] == 0x00 ) {
          correct++;
        } else {
          incorrect++;
        }
        listener_bytes_received = 0;

        if (listener.has_error()) {

            switch((int)listener.error()) {
                case 0: {
                    error0++;
                    break;
                }
                case 1: {
                    error1++;
                    break;
                }
                case 2: {
                    error2++;
                    break;
                }
                case 3: {
                    error3++;
                    listener.listen(listener_address);
                    break;
                }
                case 4: {
                    error4++;
                    break;
                }
                case 5: {
                    error5++;
                    break;
                }
                case 6: {
                    error6++;
                    break;
                }
                case 7: {
                    error7++;
                    break;
                }
                case 8: {
                    error8++;
                    break;
                }
                case 9: {
                    error9++;
                    break;
                }
                case 10: {
                    error10++;
                    break;
                }
                case 11: {
                    error11++;
                    break;
                }
                case 12: {
                    error12++;
                    break;
                }
            }
        } else {
            noError++;
        }
        Serial.print("noError: "); Serial.println(noError);
        Serial.print("error0: "); Serial.println(error0);
        Serial.print("error1: "); Serial.println(error1);
        Serial.print("error2: "); Serial.println(error2);
        Serial.print("error3: "); Serial.println(error3);
        Serial.print("error4: "); Serial.println(error4);
        Serial.print("error5: "); Serial.println(error5);
        Serial.print("error6: "); Serial.println(error6);
        Serial.print("error7: "); Serial.println(error7);
        Serial.print("error8: "); Serial.println(error8);
        Serial.print("error9: "); Serial.println(error9);
        Serial.print("error10: "); Serial.println(error10);
        Serial.print("error11: "); Serial.println(error11);
        Serial.print("error12: "); Serial.println(error12);
        Serial.print("Correct: "); Serial.println(correct);
        Serial.print("Incorrect: "); Serial.println(incorrect);
    }

    if(digitalReadFast(stuck_bus_pin)) {
      listener.listen(listener_address);
    }

    if(timeMillis > 500) {
        //blink_led();
        timeMillis = 0;
    }
    delay(1);

}

void after_receive(size_t length, uint16_t address) {
    if (!listener_bytes_received) {
        memcpy(listening_buffer_2, listening_buffer, length);
        listener_bytes_received = length;
    }
}

void blink_led() {
    led_high = !led_high;
    digitalWrite(LED_BUILTIN, led_high);
}

I actually also tested it on my breadboard layout, and while it is a LOT more robust now with the stuck_bus_pin solution, I can still manage to make it lock up in the same way as it does on my custom pcb. At least the errors look the same, I cannot be 100 percent sure if the cause of the error is the same this way too. So this is now what my breadboard layout looks like: WhatsApp Image 2021-04-29 at 13 35 38

Notice the new yellow wire, which is my stuck_bus_signal wire :P Also notice the red/green wires on the bottom, connected to the i2c lines. This is how I can reproduce the issue on my breadboard: image

Just take some small bundles of utp wire and make contact with the i2c line(s). Just wiggle it around for a bit, and I am able to get very comparable errors and logs: image

I noticed that annoying the SCL line introduces more errors, and larger bundles of wires will make the everything throw errors more quickly.

I know it might be weird to fix an issue that occurs when you have to screw with the wires so much. But if you manage to make THIS stable, then I'm sure this i2c solution will work in any situation, making the entire library a lot more robust.

Richard-Gemmell commented 3 years ago

Cool! It feels like we're making progress.

I think you found another bug with the master getting confused. I've changed the driver to try to fix both the previous bug and this one. Please give it a go.

When we've fixed all the master issues on the breadboard it'll be good to make sure it works on the PCB. I wouldn't be at all surprised if it behaves a little differently.

Using a wire to reset the slave seems like a good plan. I suggest tweaking the code so that the slave triggers a reset when it detects a rising edge rather than HIGH. I think you can do it with attachInterrupt but I haven't actually tried it. I'd get the ISR to set a flag when the edge is detected. loop() should then do the actual reset.

I'm glad we're making the driver more robust. I know there are other people who've had these sorts of problems but I've never been able to help them much before.

niek-dewit commented 3 years ago

I think we are definitely making great progress. And this new update already made things a lot more stable again. I noticed it is a LOT more difficult to make the system fail now in the breadboard layout.

breadboard logs with intentionally trying to induce issues: image image image

Also when trying it out on the custom pcb, I noticed that it took a much longer time before it failed. We're now touching the 20000's of successful transfers instead of 100's. pcb logs without touching anything: image image image

I think we are seeing 2 different issues, the first one with the arbitration lost logs. And the other one that logs bus already in use

I also changed my code to use the attachInterrupt intead :)

Does this give you enough information to make any next step in this? Or can I help you with providing some more info?

Richard-Gemmell commented 3 years ago

Hi Niek,

That looks a lot better. I think I've got enough information to keep working on this for a while. My immediate goal is to see if I can ensure that the driver never gets stuck.

Is the current version good enough for you to use for the next 2 or 3 weeks? If it is, I'll concentrate on my own test setup. I'm thinking of creating some sort of I2C torture test involving injecting random bits etc. I also want to create an automated test suite to make sure I'm not breaking anything else with these changes.

Do you know the capacitance of the UTP cable you have? The rating per meter + the length is all I need to know.

cheers, Richard

niek-dewit commented 3 years ago

yes the current version is good enough for the next couple weeks. Please just work on whatever you think is best now. For me this is just a really time consuming exploded side-project ;) And there are still plenty of other things I can work on while keeping this part on hold for a few weeks :)

I don't know the capacitance of the wire. But it is wire I repurposed from a bunch of old ethernet cabling. In the cable, there were 4 pairs of twisted wires I believe, I am using a piece of wire about 2,5m long. I don't have a multimeter which can measure capacitance, so your guess would be as good as mine :P

niek-dewit commented 3 years ago

Hi Richard! Have you had the chance to make any progress on this issue? Anything I could test or measure? This weekend I will have some time again :)

Richard-Gemmell commented 3 years ago

Hi Niek,

I don't have anything for you yet. I have a rather long and complicated plan that I'm working on. The goals are:-

  1. create a library to watch the bus and report what's happening at the electrical level
  2. use this library to write an fully automated test suite for Teensy4_I2C
  3. use the library to trigger faults like the ones you've seen
  4. fix the bugs without breaking anything

I'm currently in stage 1. The first useful bit for you will be a tool that you can use to monitor the bus and trigger soft resets whenever it detects a stuck bus. You should be able to use it to work out if it's the master or one of the slaves that's stuck. I'm hoping that this will be enough to ensure that your project never locks up.

cheers, Richard

niek-dewit commented 3 years ago

sounds great! Any repo I will be able to keep an eye on ?

Richard-Gemmell commented 3 years ago

https://github.com/Richard-Gemmell/i2c-underneath I'll make it public when there's something useful in it.

Richard-Gemmell commented 3 years ago

I've published a tool in i2c-underneath that will tell you if the bus is stuck. There are a couple of examples linked from the project homepage. They both monitor the bus and tell you if it's stuck and if so whether it's SDA or SCL that's stuck. The other example contains code to reset SDA as well.

I hope this will help a bit.

niek-dewit commented 3 years ago

@Richard-Gemmell awesome! Can you send me some basic instructions on how to use the i2c-underneath code? I tried cloning the project and directly running the monitor_master.ino, but it gives compilation errors that it cannot find the included files of the library

/home/niek/Arduino/libraries/i2c-underneath/examples/bus_monitor/monitor_master/monitor_master.ino:25:44: fatal error: common/hal/arduino/arduino_pin.h: No such file or directory
 #include "common/hal/arduino/arduino_pin.h"

I also tried copying the necessary files directly into a project file. image Then I changed a bunch of the imports in your code to use relative paths, because it threw errors about not finding certain files.

Now this is my sketch file:

#include <Arduino.h>
#include "./common/hal/arduino/arduino_pin.h"
#include "./common/hal/teensy/teensy_timestamp.h"
#include "./bus_monitor/bus_monitor.h"

unsigned char sda_monitor_pin = 17;
unsigned char scl_monitor_pin = 16;

common::hal::ArduinoPin sda(sda_monitor_pin);
common::hal::ArduinoPin scl(scl_monitor_pin);

void setup() {

}

void loop() {

}

Now I am getting stuck on another compilation error:

/tmp/arduino_build_89031/sketch/i2c-issue-master.ino.cpp.o: In function `__static_initialization_and_destruction_0':
/home/niek/Arduino/i2c-issue-master/i2c-issue-master.ino:18: undefined reference to `common::hal::ArduinoPin::ArduinoPin(unsigned char)'
/home/niek/Arduino/i2c-issue-master/i2c-issue-master.ino:19: undefined reference to `common::hal::ArduinoPin::ArduinoPin(unsigned char)'
/home/niek/Arduino/i2c-issue-master/i2c-issue-master.ino:19: undefined reference to `common::hal::ArduinoPin::~ArduinoPin()'
collect2: error: ld returned 1 exit status

After a bit of googling this seems to be a VERY basic cpp issue. But because of my inexperience with cpp , I am simply unable to get this working :/

Richard-Gemmell commented 3 years ago

I didn't have time to test it with Arduino or to make it an Arduino library. I'll give it a go now and get back to you.

Richard-Gemmell commented 3 years ago

I've fixed the problems you mentioned. The examples now work in Arduino.

BusMonitor uses 2 pins to spy on an I2C bus. In the monitor_master.ino example these are sda_monitor_pin and scl_monitor_pin. It watches the bus lines and detects a "stuck" bus. It also tells you if the bus is busy or idle but you probably don't care about that. :)

When you detect a stuck bus you can take action to clear the fault. If the clock line (SCL) is stuck there's not much you can do except reset all the devices. If the the data line (SDA) is stuck then you can usually clear the fault automatically.

The stuck_bus example just shows how to use BusMonitor on it's own. You might use this if you have a device which is neither the master nor the slave but a 3rd device which just watches.

The monitor_master example shows how you can add a BusMonitor to an existing I2C master device. In this case, it's based on the raw_find_slaves example from Teensy4_I2C.

It's a bit awkward to use at the moment. I'll probably improve it a bit to make it easier to merge into an existing project. I also appreciate that it doesn't do very much but it's early days. The teensy4_i2c doesn't automatically clear a stuck bus at the moment unlike the default driver.

Let me know if you need a better explanation.

niek-dewit commented 3 years ago

Awesome!! I combined your example code with my Teensy master/slave setup. And it works perfectly now. I am really unable to get it stuck now, without it automatically recovering. This is amazing! image Left master Teensy, right slave Teensy. As you can see, in my case data can sometimes be stuck low. And it is automatically recovering from this. I don't know how I would be able to get clock stuck low. But I don't think this is a cause for any problems in my project.

For any future reference, this is now the code I am using which is very stable. (I realize it might be a bit messy, but it works, and I will implement this in my larger project source soon :) ) Master:

#include <Arduino.h>
#include <i2c_device.h>
#include "imx_rt1060/imx_rt1060_i2c_driver.h"
#include "bus_monitor.h"
#include "common/hal/arduino/arduino_pin.h"
#include "common/hal/teensy/teensy_timestamp.h"

volatile bool led_high = false;
void blink_led();

I2CMaster& master = Master;

// Configure pins to monitor the bus
unsigned char sda_monitor_pin = 17;
unsigned char scl_monitor_pin = 16;

common::hal::ArduinoPin sda(sda_monitor_pin);
common::hal::ArduinoPin scl(scl_monitor_pin);

void sda_on_edge_isr() {
    sda.raise_on_edge();
}
void scl_on_edge_isr() {
    scl.raise_on_edge();
}

common::hal::TeensyTimestamp timestamp;
bus_monitor::BusState previous_state = bus_monitor::BusState::unknown;

bus_monitor::BusMonitor monitor(sda, scl, timestamp);

const uint8_t listener_address = 127;
uint8_t buff[512] = {0xAA, 0xFF, 0x55, 0x00};
unsigned int speaking_length = 4;

unsigned long noError = 0;
unsigned long error0 = 0;
unsigned long error1 = 0;
unsigned long error2 = 0;
unsigned long error3 = 0;
unsigned long error4 = 0;
unsigned long error5 = 0;
unsigned long error6 = 0;
unsigned long error7 = 0;
unsigned long error8 = 0;
unsigned long error9 = 0;
unsigned long error10 = 0;
unsigned long error11 = 0;
unsigned long error12 = 0;
unsigned long clockStuckLow = 0;//I2C clock is stuck LOW
unsigned long dataStuckLow = 0;//I2C data line is stuck LOW
unsigned long failedAutoFix = 0;//Failed to clear I2C bus automatically

uint8_t stuck_bus_pin = 12;
elapsedMillis timeMillis = 0;

void setup() {

    pinMode(LED_BUILTIN, OUTPUT);

    pinMode(stuck_bus_pin, OUTPUT);
    digitalWrite(stuck_bus_pin, false);

    master.begin(100 * 1000U);

    pinMode(sda_monitor_pin, INPUT_PULLUP);
    pinMode(scl_monitor_pin, INPUT_PULLUP);
    sda.set_on_edge_isr(sda_on_edge_isr);
    scl.set_on_edge_isr(scl_on_edge_isr);

    monitor.begin();

    Serial.begin(9600);
    delay(2000);
}

void clear_bus() {
    pinMode(scl_monitor_pin, OUTPUT_OPENDRAIN);
    delayMicroseconds(10);
    const int reset_cycles = 9;
    for(int i=0; i<reset_cycles; i++) {
        scl.write_pin(false);
        delayMicroseconds(5);
        scl.write_pin(true);
        delayMicroseconds(5);
    }
    pinMode(scl_monitor_pin, INPUT_PULLUP);
    delayMicroseconds(10);
}

void resetMasterAndSlave() {
    digitalWriteFast(stuck_bus_pin, true);
    master.begin(100 * 1000U);
}

void monitor_loop() {
    bus_monitor::BusState bus_state = monitor.get_state();
    if (bus_state != previous_state) {
        if (bus_state == bus_monitor::BusState::stuck) {
            if (!scl.read_line()) {
                Serial.println("I2C clock is stuck LOW. Reset all devices to clear the fault.");
                resetMasterAndSlave();
                clockStuckLow++;
            }
            else if (!sda.read_line()) {
                Serial.println("I2C data line is stuck LOW. Clearing fault.");
                clear_bus();
                resetMasterAndSlave();
                dataStuckLow++;
                if (monitor.get_state() == bus_monitor::BusState::stuck) {
                    Serial.println("Failed to clear I2C bus automatically. Reset set all devices to clear the fault.");
                    resetMasterAndSlave();
                    failedAutoFix++;
                } else {
                    Serial.println("I2C bus is free again.");
                }
            }
        }
        digitalWrite(LED_BUILTIN, monitor.get_state() == bus_monitor::BusState::stuck);
    }
    previous_state = bus_state;
}

void loop() {
    monitor_loop();

    if (master.has_error()) {
        switch((int)master.error()) {
          case 0: {
            error0++;
            break;
          }
          case 1: {
            error1++;
            break;
          }
          case 2: {
            error2++;
            break;
          }
          case 3: {
            error3++;
            break;
          }
          case 4: {
            error4++;
            break;
          }
          case 5: {
            error5++;
            break;
          }
          case 6: {
            error6++;
            break;
          }
          case 7: {
            error7++;
            break;
          }
          case 8: {
            error8++;
            break;
          }
          case 9: {
            error9++;
            break;
          }
          case 10: {
            error10++;
            break;
          }
          case 11: {
            error11++;
            break;
          }
          case 12: {
            error12++;
            //digitalWriteFast(stuck_bus_pin, true);
            resetMasterAndSlave();
            break;
          }
        }
    } else {
        noError++;
        digitalWriteFast(stuck_bus_pin, false);
    }

    master.write_async(listener_address, buff, speaking_length, true);
    finish();
    if(timeMillis > 500) {
        blink_led();
        timeMillis = 0;
   }

    Serial.print("noError: "); Serial.println(noError);
    Serial.print("error0: "); Serial.println(error0);
    Serial.print("error1: "); Serial.println(error1);
    Serial.print("error2: "); Serial.println(error2);
    Serial.print("error3: "); Serial.println(error3);
    Serial.print("error4: "); Serial.println(error4);
    Serial.print("error5: "); Serial.println(error5);
    Serial.print("error6: "); Serial.println(error6);
    Serial.print("error7: "); Serial.println(error7);
    Serial.print("error8: "); Serial.println(error8);
    Serial.print("error9: "); Serial.println(error9);
    Serial.print("error10: "); Serial.println(error10);
    Serial.print("error11: "); Serial.println(error11);
    Serial.print("error12: "); Serial.println(error12);
    Serial.print("clockStuckLow: "); Serial.println(clockStuckLow);
    Serial.print("dataStuckLow: "); Serial.println(dataStuckLow);
    Serial.print("failedAutoFix: "); Serial.println(failedAutoFix);

    delay(1);
}
void finish() {
    elapsedMillis timeout;
    while (timeout < 200) {
        if (master.finished()){
            return;
        }
    }
    Serial.println("Master: ERROR timed out waiting for transfer to finish.");

}
void blink_led() {
    led_high = !led_high;
    digitalWrite(LED_BUILTIN, led_high);
}

Slave:

#include <Arduino.h>
#include <i2c_driver.h>
#include "imx_rt1060/imx_rt1060_i2c_driver.h"

volatile bool led_high = false;
void blink_led();

const uint8_t listener_address = 127;

I2CSlave& listener = Slave;
const size_t listening_buffer_size = 512;
uint8_t listening_buffer[listening_buffer_size] = {};
uint8_t listening_buffer_2[listening_buffer_size] = {};
volatile size_t listener_bytes_received = 0;
void after_receive(size_t, uint16_t);

unsigned long correct = 0;
unsigned long incorrect = 0;

unsigned long noError = 0;
unsigned long error0 = 0;
unsigned long error1 = 0;
unsigned long error2 = 0;
unsigned long error3 = 0;
unsigned long error4 = 0;
unsigned long error5 = 0;
unsigned long error6 = 0;
unsigned long error7 = 0;
unsigned long error8 = 0;
unsigned long error9 = 0;
unsigned long error10 = 0;
unsigned long error11 = 0;
unsigned long error12 = 0;
unsigned long stuck = 0;
boolean isStuck = false;

uint8_t stuck_bus_pin = 12;
elapsedMillis timeMillis = 0;

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

    attachInterrupt(digitalPinToInterrupt(stuck_bus_pin), bus_stuck, RISING);

    Serial.begin(9600);
    Serial.println("Started listening");
    listener.after_receive(after_receive);
    listener.set_receive_buffer(listening_buffer, listening_buffer_size);
    listener.listen(listener_address);
    delay(2000);

}

void bus_stuck() {
  isStuck = true;
}

void loop() {
    if (listener_bytes_received) {
        if(listening_buffer[0] == 0xAA && listening_buffer[1] == 0xFF && listening_buffer[2] == 0x55 && listening_buffer[3] == 0x00 ) {
          correct++;
        } else {
          incorrect++;
        }
        listener_bytes_received = 0;

        if (listener.has_error()) {

            switch((int)listener.error()) {
                case 0: {
                    error0++;
                    break;
                }
                case 1: {
                    error1++;
                    break;
                }
                case 2: {
                    error2++;
                    break;
                }
                case 3: {
                    error3++;
                    listener.listen(listener_address);
                    break;
                }
                case 4: {
                    error4++;
                    break;
                }
                case 5: {
                    error5++;
                    break;
                }
                case 6: {
                    error6++;
                    break;
                }
                case 7: {
                    error7++;
                    break;
                }
                case 8: {
                    error8++;
                    break;
                }
                case 9: {
                    error9++;
                    break;
                }
                case 10: {
                    error10++;
                    break;
                }
                case 11: {
                    error11++;
                    break;
                }
                case 12: {
                    error12++;
                    break;
                }
            }
        } else {
            noError++;
        }

    }
    if(isStuck) {
        stuck++;
        listener.listen(listener_address);
        isStuck = false;
    }
    if(timeMillis > 500) {
        blink_led();
        timeMillis = 0;
    }

    Serial.print("noError: "); Serial.println(noError);
    Serial.print("error0: "); Serial.println(error0);
    Serial.print("error1: "); Serial.println(error1);
    Serial.print("error2: "); Serial.println(error2);
    Serial.print("error3: "); Serial.println(error3);
    Serial.print("error4: "); Serial.println(error4);
    Serial.print("error5: "); Serial.println(error5);
    Serial.print("error6: "); Serial.println(error6);
    Serial.print("error7: "); Serial.println(error7);
    Serial.print("error8: "); Serial.println(error8);
    Serial.print("error9: "); Serial.println(error9);
    Serial.print("error10: "); Serial.println(error10);
    Serial.print("error11: "); Serial.println(error11);
    Serial.print("error12: "); Serial.println(error12);
    Serial.print("Correct: "); Serial.println(correct);
    Serial.print("Incorrect: "); Serial.println(incorrect);
    Serial.print("Stuck: "); Serial.println(stuck);

    delay(1);

}

void after_receive(size_t length, uint16_t address) {
    if (!listener_bytes_received) {
        memcpy(listening_buffer_2, listening_buffer, length);
        listener_bytes_received = length;
    }
}

void blink_led() {
    led_high = !led_high;
    digitalWrite(LED_BUILTIN, led_high);
}

Thanks a lot for all your help @Richard-Gemmell this will make my project so so much more stable. I will let you know if I encounter any other issues, but I dont suspect any. I will keep an eye on this project and the i2c_underneath project, in case there are going to be any developments with making "cleaner" to implement in a project. But for now this is a totally perfect solution for me. :)

Richard-Gemmell commented 3 years ago

Hi, Thanks for trying it out. I'm delighted to hear that it's working for you.

I'm planning to modify the driver to apply this fix automatically. It'll probably be several months before I release it though.

cheers, Richard