Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wire lib hangs when scanning i2c in Wire.endTransmission() #194

Open
erniberni opened this issue Dec 27, 2016 · 7 comments
Open

Wire lib hangs when scanning i2c in Wire.endTransmission() #194

erniberni opened this issue Dec 27, 2016 · 7 comments

Comments

@erniberni
Copy link

Hi,
I tried the well known i2c scanner found on Arduino playground with a Feather M0.
Unfortunately the program hangs while testing
error = Wire.endTransmission();
In many libraries (for ex. the BME280 lib) this test is used to check if the sensor (based on it's i2c address) is present.
On Arduino and ESP the i2c scanner is working.

@spiderkeys
Copy link
Contributor

spiderkeys commented Dec 28, 2016

Hi @erniberni

Your program is getting hung in one of two places:
https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/SERCOM.cpp#L478
https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/SERCOM.cpp#L488

I've reviewed Arduino's I2CM implementation and the Wire library calls that are made to perform a scan, and there are a few ways that you can end up locked up.

  1. Starting with what is most likely the cause of your woes, the Feather M0 does not have built in pullup resistors on the I2C pins, so you need to manually add your own. If you don't, bad things are bound to happen that will leave the SAMD's I2C peripheral in a sad state. 4.7K resistors should be fine, but it all depends on your bus capacitance and the speed at which you're trying to clock the I2C. If you have already verified the electrical integrity of your buses, then read on.

  2. Double check to make sure you initialized the I2C peripheral before using it (Wire.begin()).

  3. When using a proper setup (I2C peripheral has been properly initialized and your physical buses are electrically to spec), the failure mechanism is that you have a slave playing bad actor on your bus as a result of how the scan is implemented (it could be holding down SDA or SCL forever). I don't think this is your failure case, as you report that it is working properly with the same code on a different board (and what I assume are the same sensors). However, this still could be the issue, because I am not sure how the Atmel EDBG chip responds to being scanned (being addressed with a write command and then immediately receiving a stop condition). If you have a logic probe, you can quickly determine if this is happening. If the EDBG chip decides to act up after being scanned, you could get stuck in one of those loops forever and you'll see either SDA or SCL held low as the SAMD and EDBG wait for things to happen that never will.

Unfortunately, there is no timeout concept in any of the Arduino board I2C drivers, so unless you have a 100% perfect setup, you can expect an application hangup in the event of certain classes of errors. Even if you have a watchdog and the chip resets, there is no guarantee that the error condition will disappear (the slave could still be holding down lines). In Arduino's defense, timeouts aren't part of the I2C spec, but then again, they don't even call it I2C, and it is not difficult to tease an I2C lockup out of most configurations. My philosophy is that I2C failures should not block your application, so timeouts should be employed to allow the user to decide what to do in the event of I2C conditions that led to a timeout. Some community members have added I2C timeouts for the various boards. For the SAMD21, you can check out RIOT OS's I2C driver for the SAMD21, however it also does not implement an I2CM driver that is fully robust, so I am working through a patch for them as well at the moment. It also does not conform to the Wire API, so it is not plug and play by any means.

All in all, the SAMD21 I2C peripheral is a complex beast that is almost too "smart" for its own good. I have yet to see anyone successfully implement a robust driver for it, and I believe this is mostly because the documentation for the peripheral is sometimes vague, conflicting, and leaves certain things out. I feel like I am getting pretty close with a combination of Arduino's driver, RIOT's driver, and my own custom driver that I wrote, so hopefully in the not too distant future, I can provide a standalone I2CM library that can be used to substitute wire or otherwise help to update the Arduino SAMD core and wire libraries.

@erniberni
Copy link
Author

@spiderkeys
Thank you for your detailed explanation.
As I tried to test if the sensor is present, a missing sensor breakout also means that also the pullups are missing (btw: you wrote "pulldown"). That leads to an undefined bad electrical situation on the SDA, SCL lines while testing. If I place pullups on the busses only, the test is working. Then I tried to read with digitalRead(SDA) if a pullup is present or not, but it seems that only the pinMode(SDA, INPUT) before wire.begin would clean the bad electrical situation on the bus. So maybe this would prevent a hanging in the code. I will do more tests.

@spiderkeys
Copy link
Contributor

spiderkeys commented Dec 28, 2016 via email

@sandeepmistry
Copy link
Contributor

@erniberni any updates on this?

@erniberni
Copy link
Author

No, I didn't find a solution.

@Gusev-Roman
Copy link

my Mega2560 hangs on endTransmission() when slave device is off, It seems unpowered device forms pull-down on i2c lines, but this is not good if firmware is toatlly hangs.

@Nielsbishere
Copy link

I'm running into the same issue, any fix for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants