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

Bug in smbus2 for long time operation vs. pigpio and tools i2cset(get) #101

Open
msawicki-poland opened this issue Dec 11, 2023 · 6 comments

Comments

@msawicki-poland
Copy link

Configuration:
Master i2c: Raspberry Pi Zero v2, Raspberry Pi OS
Slave i2c: two STM32s (addresses: 0x09 and 0x0c)
App for master: in infinity loop write to slave command (1 byte) with data (2 bytes + 1 byte CRC) and after it read the answer (2 bytes + 1 byte CRC).
I've prepared three versions of app:

  1. As bash script using i2cset and i2cget:
#!/bin/bash
while [ true ]
do
	./i2c.sh 0x09 0x00 0xff 0xff 
	./i2c.sh 0x09 0x10 0xff 0xff 
	r=$((1+$RANDOM%255))
	r=`printf '%x' $r`
	./i2c.sh 0x09 0x03 0xff 0x$r
	./i2c.sh 0x09 0x30 0xff 0xff 
	./i2c.sh 0x09 0x02 0x00 0x$r 
	./i2c.sh 0x09 0x20 0xff 0xff 
	./i2c.sh 0x09 0x50 0xff 0xff 
	./i2c.sh 0x09 0xff 0xff 0xff 
	./i2c.sh 0x0c 0x00 0xff 0xff 
	./i2c.sh 0x0c 0x04 0xff 0x$r 
	./i2c.sh 0x0c 0x40 0xff 0xff 
	./i2c.sh 0x0c 0x60 0xff 0xff 
	./i2c.sh 0x0c 0x70 0xff 0xff 
	./i2c.sh 0x0c 0x08 0xff 0x$r 
	./i2c.sh 0x0c 0x80 0xff 0xff 
	./i2c.sh 0x0c 0x09 0xff 0x00 
	./i2c.sh 0x0c 0x90 0xff 0xff 
	./i2c.sh 0x0c 0x0a 0xff 0x$r 
	./i2c.sh 0x0c 0xa0 0xff 0xff 
	./i2c.sh 0x0c 0xff 0xff 0xff 
done

and

#!/bin/bash
bus=3
stm=$1
rozkaz=$2
dane1=$3
dane2=$4
crc=`python calcCRC.py $rozkaz $dane1 $dane2`
i2ctransfer -y $bus w4@$stm $rozkaz $dane1 $dane2 $crc
i2ctransfer -y $bus r3@$stm
  1. As python (v3.9.2) script using pigpio (v1.78) library:
#!/usr/bin/python
import pigpio
import time
from crc import Calculator, Crc8
import random
pi=pigpio.pi()
crc=Calculator(Crc8.CCITT)
bus=3
downSTM=0x09
upSTM=0x0c
def command(address,command,data1,data2):
    crcSum=crc.checksum(bytes([command,data1,data2]))
    data=bytes([command,data1,data2,crcSum])
    h=pi.i2c_open(bus,address,0)
    pi.i2c_write_device(h,data)
    time.sleep(0.01)
    (count,res)=pi.i2c_read_device(h,3)
    time.sleep(0.01)
    pi.i2c_close(h)
while True:
    command(downSTM, 0x00, 0xff, 0xff) 
    command(downSTM, 0x10, 0xff, 0xff)
    command(downSTM, 0x03, 0xff, random.randrange(256))
    command(downSTM, 0x30, 0xff, 0xff) 
    command(downSTM, 0x02, random.randrange(4), random.randrange(233))
    command(downSTM, 0x20, 0xff, 0xff)
    command(downSTM, 0x50, 0xff, 0xff)
    command(downSTM, 0xff, 0xff, 0xff) 
    command(upSTM, 0x00, 0xff, 0xff)
    command(upSTM, 0x04, 0xff, random.randrange(101)) 
    command(upSTM, 0x40, 0xff, 0xff)
    command(upSTM, 0x60, 0xff, 0xff)
    command(upSTM, 0x70, 0xff, 0xff) 
    command(upSTM, 0x08, 0xff, random.randrange(101))
    command(upSTM, 0x80, 0xff, 0xff) 
    command(upSTM, 0x09, 0xff, random.randrange(2))
    command(upSTM, 0x90, 0xff, 0xff) 	
    command(upSTM, 0x0a, 0xff, random.randrange(101))
    command(upSTM, 0xa0, 0xff, 0xff)
    command(upSTM, 0xff, 0xff, 0xff) 
  1. As python (v3.9.2) script using smbus2 (v0.4.3) library:
#!/usr/bin/python
from smbus2 import SMBus
import time
from crc import Calculator, Crc8
import random
bus = SMBus(3)
crc=Calculator(Crc8.CCITT)
downSTM=0x09
upSTM=0x0c
def command(address,command,data1,data2):
    crcSum=crc.checksum(bytes([command,data1,data2]))
    data=bytes([data1,data2,crcSum])
    bus.write_i2c_block_data(address,command,data,False)
    time.sleep(0.01)
    res=bus.read_i2c_block_data(address,command,3,False)
    time.sleep(0.01)
while True:
    command(downSTM, 0x00, 0xff, 0xff)
    command(downSTM, 0x10, 0xff, 0xff)
    command(downSTM, 0x03, 0xff, random.randrange(256))
    command(downSTM, 0x30, 0xff, 0xff)
    command(downSTM, 0x02, random.randrange(4), random.randrange(233))
    command(downSTM, 0x20, 0xff, 0xff)
    command(downSTM, 0x50, 0xff, 0xff)
    command(downSTM, 0xff, 0xff, 0xff)
    command(upSTM, 0x00, 0xff, 0xff)
    command(upSTM, 0x04, 0xff, random.randrange(101))
    command(upSTM, 0x40, 0xff, 0xff)
    command(upSTM, 0x60, 0xff, 0xff)
    command(upSTM, 0x70, 0xff, 0xff)
    command(upSTM, 0x08, 0xff, random.randrange(101))
    command(upSTM, 0x80, 0xff, 0xff)
    command(upSTM, 0x09, 0xff, random.randrange(2))
    command(upSTM, 0x90, 0xff, 0xff)
    command(upSTM, 0x0a, 0xff, random.randrange(101))
    command(upSTM, 0xa0, 0xff, 0xff)
    command(upSTM, 0xff, 0xff, 0xff)

Apps 1 and 2 works correctly for eg. 48h. App 3 works correctly only for two-three hours and after that app threw error:

Traceback (most recent call last):
  File "/home/pi/./app.py", line 44, in <module>
    command(upSTM, 0x0a, 0xff, random.randrange(101)) 	
  File "/home/pi/./app.py", line 20, in command
    res=bus.read_i2c_block_data(address,command,3,False)
  File "/usr/local/lib/python3.9/dist-packages/smbus2/smbus2.py", line 617, in read_i2c_block_data
    ioctl(self.fd, I2C_SMBUS, msg)
OSError: [Errno 6] No such device or address

For each app I've repeated experiment three times. Is there any bug in smbus2?

@kplindegaard
Copy link
Owner

kplindegaard commented Dec 12, 2023

Hey and thanks for your interest in this lib.

It seems your first two scripts open and close the bus for every command while the one using smbus2 keeps the bus open indefinitely until OSError exception is thrown - most likely because the OS closes the bus behind the scenes when a timeout limit is reached. In that regard it's not a fair comparison. And thus no bug. ;)

However, if you write your 3rd app to do the same, constantly open and close the bus, I would assume it could run forever.

#!/usr/bin/python
from smbus2 import SMBus
import time
from crc import Calculator, Crc8
import random
# bus = SMBus(3)  # This automatically opens the bus and keeps it open. Not recommended.
crc=Calculator(Crc8.CCITT)
downSTM=0x09
upSTM=0x0c

def command(address,command,data1,data2):
    crcSum=crc.checksum(bytes([command,data1,data2]))
    data=bytes([data1,data2,crcSum])

    # Commented out your original code that keeps the bus open indefinitely
    # bus.write_i2c_block_data(address,command,data,False)
    # time.sleep(0.01)
    # res=bus.read_i2c_block_data(address,command,3,False)
    # time.sleep(0.01)

    # Rewrite: Open and close for each command. For example like this:
    with SMBus(3) as bus:
        bus.write_i2c_block_data(address,command,data,False)
        time.sleep(0.01)
        res=bus.read_i2c_block_data(address,command,3,False)
        time.sleep(0.01)

while True:
    # .... As before

Not tested, but I hope you get the idea.

@msawicki-poland
Copy link
Author

I've prepared 3rd app as you said (exactly the same code as yours) and unfortunetly after three hours I got the same error.

@kplindegaard
Copy link
Owner

kplindegaard commented Dec 13, 2023

Meaning it's the read_i2c_block_data that always fails? You see, that puzzles me a bit... Because it means that the sequence for that particular command goes like this

  1. Open the bus - success
  2. Write - success
  3. Read => Boom and the OS throws the exception.

It's hard to say why without more information. I mean, could be be that the slave device that gives in and it just accidentally happens with in this app? After all, all smbus2 is just a wrapper on top of the ioctl OS command and the OS exception all of a sudden claims OSError: [Errno 6] No such device or address. Just some off the cuff questions/requests:

  • Do you know exactly how many commands you have executed when this happens?
  • If you inject a try: ... except and immediately do an ls -l /dev command using subprocess.POpen in the except handler, what does that tell you? Is bus three still there? Or can you successfully reopen the bus in the exception handler or will that also throw and indicate the OS really has lost it? bus2 = SMBus(3).
  • And what if you increase the sleep-interval?

Thanks for your time.

@msawicki-poland
Copy link
Author

  1. Always exception is thrown for reading from slave device (read_i2c_block_data).
  2. How many commands? It's difficult to evaluate but aprox. a half of million.
  3. After exception I must restart device beacuse SCL (clock) is zero. I registered it using oscilloscope and allways after sent the last bit in address (direction bit) something wrong with ack signal on the i2c bus.

@msawicki-poland
Copy link
Author

Reply for "And what if you increase the sleep-interval?". I changed delay from 0.01s to 0.1 in two lines incommandfunction. It works. I've tested it for 24h, but it's important to remember that if I increase delay the number of commands per hour and per whole experiment decreased. So I don't know how it works fo eg. 10 days.

@kplindegaard
Copy link
Owner

Hi @msawicki-poland and thanks for your patience as well as your comments.
I'm afraid I don't understand what happens with the SCL in your case. That it fails so consistently lead me to believe that you were potentially able to issue a lot more commands using smbus2 than the other options and hence the hardware was stressed a lot more and eventually failed as a result of that. But then again - it's hard to tell.

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

2 participants