Electronic – I2C bus fails after a few sensor reads

i2cparasitic-capacitanceraspberry pi

I'm building a small home automation hobby project for which I try to connect 4 temperature/humidity sensors (AM2320) to an I2C bus on the Raspberry Pi. When initially connecting the sensors they work fine, but after a few calls (10 to 50) with 4s interval, the reading fails with a "OSError: [Errno 6] No such device or address". After that, a call to i2cdetect on the bus results in a very slow respons where each probed address takes around 1s (equal to the problem described here). After this i2cdetect fails to detect the sensor.

Physically disconnecting and reconnecting the sensor resets the bus.

I think the root of the problem is in the capacitance of the connecting line between sensors and Pi, however I can't wrap my head around why. Hopefully someone can clarify what the problem is.

Details of the setup

I am using 4 identical sensors connected to a single Raspberry pi. Each sensor is connected to it via a stripped Cat5 cable (4 wires (2 twisted pairs) per sensor, shielding and outer isolation removed) of approximately 2m length.

As the sensors are rather simple and cheap, they do not have a configurable I2C address. Each has address 0x5c. To accommodate 4 of them I configured some GPIO pins to give me 3 additional I2C buses next to the standard bus number 1. This is done by adding the following lines to the config file. (As per this instructable.)

dtoverlay=i2c-gpio,bus=5,i2c_gpio_delay_us=1,i2c_gpio_sda=23,i2c_gpio_scl=24
dtoverlay=i2c-gpio,bus=4,i2c_gpio_delay_us=1,i2c_gpio_sda=17,i2c_gpio_scl=27
dtoverlay=i2c-gpio,bus=3,i2c_gpio_delay_us=1,i2c_gpio_sda=4,i2c_gpio_scl=18

All sensors are powered from the 3.3V rail on the Pi. All SDA and SCL lines have a pull-up resistor of 3.3 kOhm. Only for the 'standard' I2C bus number 1 I rely on the 1.8 kOhm resistors that are on the Pi itself.

I read the sensors by means of this Python script: https://github.com/Gozem/am2320/blob/master/am2320.py.

Things I have tried

I have tried the following things, by themselves and various permutations of these things. Non appears to have any noticeable positive or negative effect.

  • Different timing parameters for waiting in the Python script.
  • Different I2C speeds (from standard 100 kHz down to 50 kHz, 25 kHz, 10 kHz, 5 kHz and 0.5 kHz).
  • Different pull-up resistor values: 150 Ohm, 230 Ohm, 1.8 kOhm, 3.3 kOhm, 10 kOhm, and Inf (no pull-up). As well as the Raspberry Pi build in pull-ups.
  • Different number of sensors (connecting only 1, connecting 2, connecting all 4)
  • Different sensors on different buses (various permutations).
  • Untwisting the wires: For one sensor I untwisted the wires to attempt and reduce the capacitance between them.
  • Software resetting the bus (via this method) does not resolve the problem. Only a physical disconnect/reconnect works.
  • Rebooting the RaspBerry Pi sometimes resets the bus, but not always.

The only thing that did seem to solve the problem: Was when I moved one sensor closer to the Pi. With a twisted pair wire of ~0.2 m length it appears to work fine (tested for ~half an hour). This indicates to me that wire length, and thus likely wire capacitance or wire resistance is the issue. However when looking at the way to compute the maximum allowed values (as per this TI document), and looking at the characteristics of Cat 5 cable (wiki) I would think I should be well below the maximum 400 pF capacitance, and should have hit a suitable termination resistance somewhere given all the various resistor values and communication speeds I have tried.

(I unfortunately don't have an oscilloscope to measure the signals on the line directly.)

So to summarize the question: I'm at my wits end and hope that there is someone who can explain me what's going on.

Edits based on comments:

(2021-01-11 16:00)

I'm using a Raspberry Pi 3B+
I had the cables in twisted pairs as ((V;GND) and (SDA;SCL)).

I have now reduced the complexity of the setup. I only have 1 sensor connected directly to the Pi I2C bus 1 (GPIO 2 and 3), with only the Pi pull up resistors. The wires between are untwisted ~2m long. I've disabled all extra I2C buses. I've added a 4.7 nF and a 47 uF capacitor between the V+ and GND on the sensor side (As per suggestion from Justme to add some. I've chosen the cap values rather arbitrarily). This seems to work without issue(!) (Over 400 cycles, 4s interval. Stopped and started the script some times in between.).

From this point I extended to include one more sensor. I added an I2C bus number 3 on GPIO 17 and 27 with 3.2 kOhm pull-ups. Added a second sensor to this. The sensor also has 4.7 nF and 47 uF capacitors added at the sensor end. This is still connected via twisted wire pairs. The second sensor malfunctioned after ~200 cycles. At that point i2cdetect also only read addresses 'in slow mode'. Sensor on bus 1 happily continued on. I swapped the two sensors around. Same result after ~330 cycles: Sensor on bus 3 (untwisted pair) failed, sensor on bus 1 (twisted pair) continued on. So either it's the difference in pull-up resistors (next to investigate), or it's indeed that the extra I2C bus is unstable for some reason (as suggested in the comments by tflong01).

(2021-01-11 17:30)

Bus 3 is unstable with whichever sensor and whatever pull-up resistor. Sometimes it works for 100+ cycles, sometimes it fails on the first try. Any sensor on bus 1 however, is rock steady now that the caps are added to the sensor.

Best Answer

Only a partial answer:

  • A single AM2320 sensor connected to the standard RPi I2C bus 1 over a ~5m twisted pair Cat5 cable runs without issues (>5 days already) after bypass capacitors had been added between the V+ and GND pins on the sensor side. (4.7 nF and 47 uF).
  • Adding multiple sensors by adding more I2C busses via the GPIO pins did not work. I suspect this is an issue with the implementation of these busses on the RPi 3B+ and not with the sensors or the wiring. I personally accepted this and left it at this for now.

As not scope was available, it could not be checked if for example noise on the SDA and SCL lines was a problem.

Notes from the discussion in the chat that may help other people:

If noise on the SDA and SCL lines is the problem, it can help to:

  1. Avoid putting the SDA and SCL in a single twisted pair,
  2. add 100 ohm serie resistors in these lines to reduce dV/dt,
  3. replace the pull-up resistors with a constant current pull-up,
  4. or reducing the Pi's IO driver strength.

Do not add capacitors to any of the signal lines.

It is questioned if the I2C bus on the RPI 3B+ can actually be set to values other than the default: https://raspberrypi.stackexchange.com/questions/108896/what-is-rpis-i2c-maximum-speed/109005#109005