Issue with I2C on Photon

I hadn’t seen that, @peekay123. Thank you for sharing. Hmmm.

1 Like

@modulo, for your convenience, here is the link.

Hey all! A new release candidate is available that brings greater reliability to the I2C interface. Particle Firmware Updates Thread

1 Like

I tried the new release candidate and it works much better. I actually got the photon discovering and controlling Modulos for the first time. Yay! Even better, it seems quite solid in the face of bus errors. Hot plugging works now too!

However, there is still an issue. After a slave address has been nacked, there is a 100ms delay before the program continues. This causes really terrible performance since it’s stuck waiting for a timeout most of the time. In a simple example I tried, bus utilization was only about 2.5%. A slave address NACK is a normal condition, not a bus error, so I think it should be possible to handle it without a timeout.

I believe the fix should be similar to what I did in the change above: while waiting for MASTER_RECEIVER_MODE_SELECTED, check to see if the slave address ack failed. If it did, bail immediately rather than waiting for a timeout. That said, when I tried my change in the new latest branch I couldn’t get it to work. Any thoughts on why that might be? Is I2C_EVENT_SLAVE_ACK_FAILURE not actually the right way to detect a nack? Maybe generating a stop isn’t the correct way to handle it?

Thanks again for the progress on this!

1 Like

cc: @satishgn

I suspect there may several events firing in succession and the I2C_EVENT_SLAVE_ACK_FAILURE is just missed. The I2C_CheckEvent() just returns the last event, not any event that has recently happened. I think a better way to definitely catch it is to check the flags associated with this error. There is an I2C_FLAG_AF: Acknowledge failure flag but now that I think of it we are handling errors in an interrupt now, clearing them as soon as they are set. Perhaps we can set our own volatile flag for AR if it is seen, and check for that in the polled loop waiting for MASTER_RECEIVER_MODE_SELECTED... then immediately timeout, generate STOP and SW reset for good measure :smile: @satishgn what do you think?

3 Likes

Hey @BDub and @satishgn. I was wondering if you had a chance to look into setting a flag instead of timing out? It sounds liked a reasonable solution but I haven’t had a chance to try it yet.

I have not, but just brought this up internally and will file a Github issue so we don’t lose track of it. Thanks for the reminder!

@modulo please give this PR a test :smile:

Original example code:

void setup() {
    Wire.begin();
}

void loop() {
    Wire.beginTransmission(8);
    Wire.write('M');
    Wire.endTransmission();
}

New timeout with no slave actually present, bus continuing to retry in under 1ms with photon connected to the cloud.

In manual mode, things are much faster… once we get the issues sorted with task switching you might be able to see similar times with system threading enabled. Currently system threading has some issues where it’s corrupting i2c though, so be on the lookout for an update about that in a future firmware release. Anyhoo, here’s the i2c retying after just 70us in manual mode:

1 Like

Just added some code to ensure we wait for the STOP bit to actually send. SW Reset was occurring too quickly and preventing it from being generated. Thanks for testing and pointing this out @modulo :wink:

1 Like

It’s working! Hurray! Thanks so much for all the work on this. It’s really great!

2 Likes

I think this or a similar issue may have been reintroduced in 0.4.7. When the slave device is not connected, my call to Wire.endTransmission() is hanging on my photon - I think it never returns.

Here is some sample code that shows the problem, based on @modulo’s sample code:

void setup() {
    Wire.begin();
}

void loop() {
    // set LED to red for a second
    RGB.control(true);
    RGB.color(255, 0, 0);
    delay(1000);

    Wire.beginTransmission(8);
    Wire.write('M');
    Wire.endTransmission();

    // return LED to default cyan for a second
    RGB.control(false);
    delay(1000);
}

When I run this code against 0.4.6 or 0.4.5, it works as expected: the LED cycles between cyan and red. But when I run it against 0.4.7, the code hangs with the LED set to red, and I have to put the photon into safe mode to load new firmware. This is only a problem when no slave device is connected.

Thanks in advance!

1 Like

I tested the Modulo library with 0.4.7. It works, which indicates that it is not completely locking up on NACKs (at least in my case), but it is less immune to bus errors. On 0.4.6 I can’t get the photon to hang when plugging and unplugging devices. On 0.4.7 it does hang sometimes.

Ah, yes, I should have mentioned the problem is intermittent. When building against 0.4.7, I do occasionally see a couple cycles of cyan and red before it gets stuck at red. I think Wire.endTransmission() is hanging maybe 80% of the time for me.

I have the same i2c problem with firmware 0.4.7. Unplugging/replugging SDA with 0.4.6 recovers ok, but hangs with 0.4.7 – requires the reset button.

In my application, photon is the master and just writing, not expecting a response. Code:

void loop() {
  Wire.beginTransmission(17);
  Wire.write("r");
  retc=Wire.endTransmission();
  if (retc!=0) {
     Wire.reset();
     delay(1000);
  }
  delay(1000);
}

0.4.9 has the fix an will be released tomorrow (if all goes as planned)

If you have a local toolchain you can already try it out.

1 Like

Just tested against 0.4.9 and I don’t see the problem anymore. Thanks!

1 Like