Due to high demand expect some shipping delays at this time, orders may not ship for 3-4 business days. On MLK Day no orders will be shipped.
0

How to daisy chain I2C with MiniGPS
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

How to daisy chain I2C with MiniGPS

by sgalway on Sun Nov 14, 2021 10:07 pm

I am having issues with the STEMMA MiniGPS. I have three STEMMA I2C sensors, MiniGPS, BNO055 (9DOF), SHT31D (Temp/Humid) attached to a PyPortal Titano. All are being connected with STEMMA QT cables.

When used separately they all work as expected. When the BNO055 and the SHT31D are used together everything works as expected. When the MiniGPS and the SHT31D are used together everything works as expected.

When the three are used together I can't get past the following error

Code: Select all | TOGGLE FULL SIZE
ValueError: No I2C device at address: 0x44


0x44 is the address of the SHT31D, but why does it suddenly disappear?

Then when take the SHT31D out, and only have the BNO055 and the MiniGPS everything appears to work for a while (5mins). However eventually it will die and I get this error:

Code: Select all | TOGGLE FULL SIZE
OSError: [Errno 5] Input/Output error


The next time I update any code the error will change to:

Code: Select all | TOGGLE FULL SIZE
RuntimeError: No pull up found on SDA or SCL; check your wiring


The ONLY wiring is using the STEMMA connectors.

I don't have a lot of experience with I2C, but I was under the impression there would be no issue daisy chaining the sensors. The STEMMA concept really sells it as "plug-n-play". What am I missing?

sgalway
 
Posts: 10
Joined: Fri Nov 12, 2021 12:42 pm

Re: How to daisy chain I2C with MiniGPS

by gammaburst on Mon Nov 15, 2021 1:50 am

If you're running the I2C bus at 400 kHz, try reducing it to 200 kHz to see if that helps.

Erratic I2C with BNO055 or BNO085 is a familiar problem:
- Try this simple finger test: viewtopic.php?f=60&t=182881#p889280
- If you get similar results, try this workaround: viewtopic.php?f=60&t=182881&start=15#p889629

Or maybe something else.

gammaburst
 
Posts: 651
Joined: Thu Dec 31, 2015 12:06 pm

Re: How to daisy chain I2C with MiniGPS

by sgalway on Tue Nov 16, 2021 11:02 pm

Thanks for the tips. Unfortunately I am no closer. I could not get the BNO055 to fail the "touch test". But as recommended in the FAQ I installed the pull up resistors anyway. As soon as I hook up the BNO055 and the miniGPS together there is so much trash on the I2C line. Using a i2c.scan() I get the following when the temp sensor and GPS are hooked up:

['0x10', '0x77']

When the temp sensor and the IMU are hooked up

['0x28', '0x77']

When the IMU and GPS are hooked up

['0x10', '0x28']

When the temp sensor, IMU, and GPS are hooked up

['0x8', '0x9', '0xa', '0xb', '0xc', '0xd', '0xe', '0xf', '0x10', '0x11', '0x12', '0x13', '0x14', '0x15', '0x16', '0x17', '0x18', '0x19', '0x1a', '0x1b', '0x1c', '0x1d', '0x1e', '0x1f', '0x20', '0x21', '0x22', '0x23', '0x24', '0x25', '0x26', '0x27', '0x28', '0x29', '0x2a', '0x2b', '0x2c', '0x2d', '0x2e', '0x2f', '0x30', '0x31', '0x32', '0x33', '0x34', '0x35', '0x36', '0x37', '0x38', '0x39', '0x3a', '0x3b', '0x3c', '0x3d', '0x3e', '0x3f']

I'm going to make a wild ass guess that isn't correct. ;)

sgalway
 
Posts: 10
Joined: Fri Nov 12, 2021 12:42 pm

Re: How to daisy chain I2C with MiniGPS

by gammaburst on Thu Nov 18, 2021 2:41 pm

That last test does look unhappy.

I think I have all the hardware to try your project, although I'm unfamiliar with PyPortal Titano. Can you share or point to the test code you're using?

gammaburst
 
Posts: 651
Joined: Thu Dec 31, 2015 12:06 pm

Re: How to daisy chain I2C with MiniGPS

by sgalway on Thu Nov 18, 2021 2:58 pm

this code will trigger the erroneous I2C addresses above. To see further errors, comment out the i2c.scan() doWhile, and comment in gps.update().

# Write your code here :-)

Code: Select all | TOGGLE FULL SIZE
import board
import busio
import adafruit_bmp3xx as tempBoard
import adafruit_gps as gpsBoard
import adafruit_bno055 as dofSensorBoard
from adafruit_pyportal import PyPortal
from digitalio import DigitalInOut


# Set up i2c bus
i2c = busio.I2C(board.SCL, board.SDA, frequency = 100000)

while not i2c.try_lock():
    pass
print([hex(x) for x in i2c.scan()])

# ---- TEMP SENSOR INTIALIZE ---

bmp = tempBoard.BMP3XX_I2C(i2c)

# ---- DOF SENSOR INTIALIZE ---

dofSensor = dofSensorBoard.BNO055_I2C(i2c)

# ---- GPS SENSOR INTIALIZE ---
gps = gpsBoard.GPS_GtopI2C(i2c, debug = False)

# Turn on the basic GGA and RMC info
# gps.send_command(b'PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0')
# gps.send_command(b"PMTK220,1000") # update refresh rate to every 1000ms 1sec


while True:

    #gps.update()

    print(count)
   
    count = count + 1

sgalway
 
Posts: 10
Joined: Fri Nov 12, 2021 12:42 pm

Re: How to daisy chain I2C with MiniGPS

by sgalway on Thu Nov 18, 2021 4:15 pm

PROGRESS! My father always told me to RTFM. After doing so I notice a note on the Pyportal FAQ that the sensor voltage can be reduced to 3.3v by jumper to help with communication problems. After making this change the setup is far more stable, however I am still getting random failures which sound like the "touch test" issue. Its stable enough for my purposes right now tough.

Your help is much appreciated.

sgalway
 
Posts: 10
Joined: Fri Nov 12, 2021 12:42 pm

Re: How to daisy chain I2C with MiniGPS

by gammaburst on Thu Nov 18, 2021 8:34 pm

Hmmm! I2C voltage *shouldn't* make much difference but apparently it does. I can probably figure it out once I have this stuff working here.

Time passes ...

I installed CircuitPython 7 into Titano, updated all the libraries in its "libs" directory with newer versions from Adafruit's circuitpython bundle, ran your code, installed additional libraries from Adafruit's bundle as needed by your project, and <drum roll> it scanned the chain and reported: ['0x10', '0x28', '0x44']

I'm running the I2C at default 5V.
My I2C chain is 28cm long: BNO055--SHT31D--MiniGPS--Titano.

Hey! Who put that different-size I2C connector on the Titano? My little Qwiic cables don't fit the Titano. I had to MAKE a connector!

Wait a minute ... you mentioned SHT31D but your code uses BMP3XX ... I don't have a BMP3XX.

Trying more experiments ...

gammaburst
 
Posts: 651
Joined: Thu Dec 31, 2015 12:06 pm

Re: How to daisy chain I2C with MiniGPS

by sgalway on Thu Nov 18, 2021 11:40 pm

Of course it works on your bench…

The change of temp sensors was a trouble shooting step. It had no effect for me.

The change to 3.3v seems to have licked the problem. It fails every 30-40 mins, but that’s acceptable for now. Looking into the BerryGPS/IMU combo…

sgalway
 
Posts: 10
Joined: Fri Nov 12, 2021 12:42 pm

Re: How to daisy chain I2C with MiniGPS

by gammaburst on Fri Nov 19, 2021 5:54 am

After enabling more I2C communication, I'm now seeing similar malfunctions as you see.
I'll dig deeper into the erratic crashing problem when I find more spare time.

The stubborn error message, "RuntimeError No pull up found on SDA or SCL check your wiring", should instead say "We apologize that our developer didn't read the 'Bus Clear' paragraph in the I2C spec".

I2C-bus spec rev 6: https://www.pololu.com/file/0J435/UM10204.pdf

I whipped up a workaround that clears the I2C slaves before initializing the I2C controller.
Insert it into your code before the busio.I2C() statement.
I wanted to set SCL to open-drain but I don't know how.
I'm a python newbie, perhaps you can write a better version:
Code: Select all | TOGGLE FULL SIZE
# grab I2C bus and wiggle SCL to clear slave devices
import digitalio
import time
sda = digitalio.DigitalInOut(board.SDA)
sda.direction = digitalio.Direction.INPUT
scl = digitalio.DigitalInOut(board.SCL)
scl.direction = digitalio.Direction.OUTPUT
for n in range(1,20):
  scl.value = 0
  time.sleep(0.0001)
  scl.value = 1
  time.sleep(0.0001)
scl.direction = digitalio.Direction.INPUT
scl.deinit()
sda.deinit()
Now, whenever the I2C crashes, I can press the Titano's RESET button and it restarts and runs without the "no pullup found" problem.

Comments/corrections welcome!

gammaburst
 
Posts: 651
Joined: Thu Dec 31, 2015 12:06 pm

Re: How to daisy chain I2C with MiniGPS

by gammaburst on Sat Nov 20, 2021 5:24 am

Adafruit Support - I think we need your help please!

I can easily reproduce sgalway's occasional I2C failures on the Titano. They occur when we connect any three I2C sensors to the Titano, and then run CircuitPython's busio scan() command repeatedly (code below). CircuitPython's output messages show occasional I2C misdetections, and my oscilloscope shows occasional bogus I2C transactions. See scope waveforms below showing three GOOD I2C scans and three equivalent BAD I2C scans. The bogus I2C transactions seem truncated and sometimes have the wrong I2C address. This smells like I2C controller firmware running amok, but that's only a guess.

I tried sgalway's I2C chain of sensors and saw the problem. Then I tried some different sensors (see photo below) and saw the same problem:
Adafruit 4444, Titano
Adafruit 3886, MPU6050, I2C address 0x68
Adafruit 2857, SHT31D, I2C address 0x44
Adafruit 4627, H3LIS331, I2C address 0x18


I'm running CircuitPython 7.0.0 on the Titano, and I replaced all the libraries in its "lib" directory with new ones from Adafruit's CircuitPython library bundle. I don't know which versions sgalway is using.

Here's the code I'm running (based upon sgalway's code). It spins in a loop doing an I2C scan every 0.5 seconds:
Code: Select all | TOGGLE FULL SIZE
import board
import busio
import adafruit_sht31d as tempBoard
import adafruit_gps as gpsBoard
import adafruit_bno055 as dofSensorBoard
from adafruit_pyportal import PyPortal
from digitalio import DigitalInOut

# grab I2C bus and wiggle SCL to clear slave devices
import digitalio
import time
sda = digitalio.DigitalInOut(board.SDA)
sda.direction = digitalio.Direction.INPUT
scl = digitalio.DigitalInOut(board.SCL)
scl.direction = digitalio.Direction.OUTPUT  # want open drain scl.drive_mode = digitalio.DriveMode.OPEN_DRAIN
for n in range(1,20):
  scl.value = 0
  time.sleep(0.0001)
  scl.value = 1
  time.sleep(0.0001)
scl.direction = digitalio.Direction.INPUT
scl.deinit()
sda.deinit()

# Set up i2c bus
i2c = busio.I2C(board.SCL, board.SDA, frequency = 100000)

while not i2c.try_lock():
  pass
n = 0
while 1:
  print([hex(x) for x in i2c.scan()], n)
  n = n+1
  time.sleep(.5)
Attachments
IMG_2581a.jpg
Titano MPU6050 SHT31D H3LIS331
IMG_2581a.jpg (329.47 KiB) Viewed 535 times
Titano three good-bad examples.png
Oscilloscope - three good I2C scans, three bad I2C scans
Titano three good-bad examples.png (338.53 KiB) Viewed 535 times

gammaburst
 
Posts: 651
Joined: Thu Dec 31, 2015 12:06 pm

Re: How to daisy chain I2C with MiniGPS

by gammaburst on Sat Nov 20, 2021 4:40 pm

A little more info ...

My scope captures (above) show all the failure modes that I've seen.

Many of the bad I2C transaction occur three transactions after an interesting event:
- First scope capture shows corruption of the I2C scan's third transaction.
- Second scope capture shows corruption of the third transaction after detecting device 0x18.

Third scope capture shows corruption during the detection of device 0x44. The Titano didn't complete the transaction, so device 0x44 stopped while pulling down SDA. That normally causes a bogus "no pull up found on SDA or SCL" error during future Titano restarts, but my code clears the I2C bus before enabling Titano's I2C controller. (My code needs to configure SCL as OPEN_DRAIN rather than OUTPUT, could someone please show me the correct syntax?)

I noticed some developer comments about busio locks and semaphores, and then "we've been puzzled by multiple i2c problems in CircuitPython."

Maybe try switching from busio to bitbangio ... Yup, that fixes it.

gammaburst
 
Posts: 651
Joined: Thu Dec 31, 2015 12:06 pm

Re: How to daisy chain I2C with MiniGPS

by gammaburst on Sun Nov 21, 2021 10:34 am

More trouble.
I see the BNO055 (address 0x28) trying to stretch the I2C clock, but Titano keeps right on going, corrupting the next I2C transaction. Looks like bitbangio doesn't support I2C clock stretching, or do I need to enable it somehow? I tried adding the "timeout" parameter to the bitbangio initialization, but it had no effect.

I saw busio do proper clock stretching, but busio has that other erratic problem.

Here's the code I'm running:
Code: Select all | TOGGLE FULL SIZE
import board
import bitbangio
from adafruit_pyportal import PyPortal
from digitalio import DigitalInOut
import time

i2c = bitbangio.I2C(board.SCL, board.SDA, frequency=100000, timeout=10000)

while not i2c.try_lock():
  pass
n = 0
while 1:
  print([hex(x) for x in i2c.scan()], n)
  n = n+1
  time.sleep(.5)

gammaburst
 
Posts: 651
Joined: Thu Dec 31, 2015 12:06 pm

Re: How to daisy chain I2C with MiniGPS

by gammaburst on Wed Nov 24, 2021 2:51 pm

Guess I scared everyone away.

gammaburst
 
Posts: 651
Joined: Thu Dec 31, 2015 12:06 pm

Re: How to daisy chain I2C with MiniGPS

by sgalway on Wed Nov 24, 2021 3:10 pm

Still here and watching.

sgalway
 
Posts: 10
Joined: Fri Nov 12, 2021 12:42 pm

Re: How to daisy chain I2C with MiniGPS

by danhalbert on Sun Nov 28, 2021 10:16 am

The BNO055's I2C implementation is not well- behaved, and other I2C sensors can also be flaky. Your jumper change 3.3V, and using the longer timeout parameter with bitbangio.I2C, are some things we have found to fix I2C issues. I think you may have tried this pullup change as well? https://github.com/adafruit/Adafruit_Ci ... x/issues/7

Have you tried the chaining of these multiple sensors on non SAMD51 boards? It would be good to see if this is due to a particular implementation.

Another note about flaky I2C: https://www.cypress.com/file/364871/download

danhalbert
 
Posts: 2947
Joined: Tue Aug 08, 2017 12:37 pm

Please be positive and constructive with your questions and comments.