Issue with I2C on Photon

I’m working on getting my project, Modulo working with the Photon and have run into an issue with I2C. It seems like after an I2C write fails, the bus locks up and won’t transmit again. Both SDA and SCL are held low rather than returning to idle (high) as they should. I created this trivial test case:

#include "spark_wiring.h"
#include "spark_wiring_i2c.h"


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

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

Here’s what I see on the logic analyzer:

then after 9.4ms:

I would expect that after the NAK a stop condition would occur, then the transmission would immediately begin again.

Any idea what might be going on? Maybe there’s a bug in how I2C NAKs are handled? Maybe I’m doing something dumb?

Thanks in advance,
Erin

3 Likes

Hi @modulo,

I pinged the firmware team about your post, and hopefully they’ll followup as soon as they get a chance. :slight_smile:

Thanks,
David

Sweet! Thanks, Dave.

1 Like

I have also bumped this up for the firmware team Erin, though most of them are asleep now.

I am extremely appreciative of your minimal, complete, verifiable example! We’ll take a look and get back to you as soon as is reasonable. And of course, if you happen to find out more, keep posting here. Thank you!

:spark: :heart: @modulo

1 Like

Awexome info! @modulo Love the logic analyzer usage :sunny:

Just a quick guess here, do you not have an actual I2C Slave hooked up that would respond to address 8? I ask because if you did the slave should ACK instead of NAK. The I2C Master is waiting for a response… perhaps indefinitely. One thing that may come out of this if that’s the case, is being able to set an appropriate timeout for cases where devices just don’t respond for whatever reason.

Glad you liked the example. I’ve spent years fixing bugs in a multi-million line codebase, so I always appreciate a good bug report. I’m glad I can pay it forward. :smile:

1 Like

That’s right, @BDub: There are no I2C slave devices attached in this example. This should be reproducible with nothing more than pullup resistors on SDA and SCL.

The master should not wait for a response since it already received a NAK condition. Since I2C timing is entirely driven by the master, the only the time the master should ever wait on the slave is when the slave “stretches the clock” by pulling SCL low.

Since this repeated "try again" scenario could also block forever, it seems that it would be a good idea to have a timeout value and/or max number of tries value that can be set by the user, but default to some reasonable values.

Per the IIC Specification, there are two ways to handle this, due to several reasons:

When SDA remains HIGH during this ninth clock pulse, this is defined as the Not
Acknowledge signal. The master can then generate either a STOP condition to abort the
transfer, or a repeated START condition to start a new transfer. There are five conditions
that lead to the generation of a NACK:

  1. No receiver is present on the bus with the transmitted address so there is no device to
    respond with an acknowledge.
  2. The receiver is unable to receive or transmit because it is performing some real-time
    function and is not ready to start communication with the master.
  3. During the transfer, the receiver gets data or commands that it does not understand.
  4. During the transfer, the receiver cannot receive any more data bytes.
  5. A master-receiver must signal the end of the transfer to the slave transmitter.

Let me know if that sounds appropriate @modulo and I'll submit a firmware issue with the appropriate info :smile:

Have you run this on a Core. If so, what were the results there?

@Jack,

@modulo is an experienced person and that’s her kickstarter so there’s not point in testing on the core though it is working on the core.

We might want to leave this thread for the official Particle folks to handle :wink:

2 Likes

Sorry, but inquiring minds...
Does that mean the processor stops running, or that the I2C bus only locks up?

Sorry, I worded that poorly. I just meant that the loop function in this particular example program would run again. I don't think that the implementation of the Wire library should try again. The issue is that even though the example program immediately tries to transmit again, the bus state never changes.

Yes, that sound correct to me. I believe that whether it issues a "stop" or "repeated start" depends on the optional argument to Wire.endTransmission().

Great! I’ve added a firmware issue:

Minimum fix: Issue STOP and return appropriate error code indicating slave NAK’d.

UBER fix: Minimum fix + API added for number of retries and/or timeout. Retries could always be set (or defaulted) to 0 if you wanted to manage this in user code.

I tried blinking the LED in the loop function and that appears to continue working, so no crash or infinite loop. The bus state just never changes.

Also good question on the Core behavior. I tried it and I get address+NAK over and over again as expected. Curiously though it holds SDA high for 100ms before the next iteration. Though that doesn't seem quite right, it hasn't caused any problems for me.

1 Like

Thank you, @BDub!

Probably another lame question, but:
If no slave is connected to the I2c , then there is really no technical problem?
But, if you had a second slave that was attached, does this mean the second slave will not get anymore commands ?
Could it be, that the photon is so smart, that once it determines that the slave is not available, it will not try to contact it again? Didn’t really think so, but?

Did you have a 100ms delay in your main loop() ?

Isn't the SDA held high by a pullup resistor, and the Core can only hold it low?
I believe the idle state of the SDA bus is high.
If that is the case, then the Core could be releasing the bus, to allow a slave to answer, or just going to idle mode?

I may be all confused about this tho.

Nope, and even if I just do it twice in the same loop there's still a delay.

Yes you're right. I meant to say that it's holding SCL low for 100ms. Sorry.

No, it's normal to receive a NAK under certain circumstances. It shouldn't cause future communication on the bus to cease.

1 Like

Sorry, but I don’t understand, have you tested this with an actual slave on I2c, and it was prevented from operating by addressing another non existing slave?

@Jack,

just a reminder that i mentioned this:

We might really want to keep this thread clean for the particle team to assist with the issue.

2 Likes