2

This example scans for any I2C slave address current on the I2C bus.
(I am using a RPi 3B+ with raspbian buster from september release)

Script trial_smbus.py:

from smbus2 import SMBus

channel = 1

i2c = SMBus(channel)

for i2c_addr in range(127):
    try:
        read_data = i2c.read_byte_data(i2c_addr, 0)
    except Exception as e:
        if e.errno == 121: 
            pass # no ACK
        else:
            print (e)
    else:
        print ("0x{:02X} => ACK!".format (i2c_addr))

It works nicely after a fresh boot of the RPi!

This script (not in the same script) simply 'plays' with the same IO pins:

Script trial_gpio.py:

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(3, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP)

I run this script, and indeed the GPIO are set as input with pullup resistors.

Now, I run again script trial_smbus.py, and I got:

[Errno 5] Input/output error

That means 2 things: - that little trial_gpio.py script set something that SMBus2 does not like. - SMBus2 does not init everything correctly.

Now I reboot and run again script trial_smbus.py==> works:

Run again trial_gpio.py.

Run again trial_smbus.py, does not work.

Run the wiringpi tool:

gpio mode 8 alt0
gpio mode 9 alt0

Run again trial_smbus.py, does work.

The easy solution would be to call those 2 command lines with a system call from within python. however wiringpi is deprecated: http://wiringpi.com/wiringpi-deprecated/

The issue I have is that I want a robust implementation with SMBus package. As you know some people might run several other programs prior they run my program, so I want the init to be done robustly.

Is there any alternative to set the pins in the correct mode?

  • 1
    Hi @karelv, Ah, let me see. (1) WiringPi once worked but now deprecated, so I am saying good bye without even once saying Hello. (2) SMBus1 works most of the time, but not always, eg no bus stretching, block read is buggy, but can get around by lowering I2C speed (from 100kHz to 10kHz!), (3) I once thought SMBus2 is new so would be better than SMBus1. I was very wrong. I tried it but gave up almost immediately. My conclusion is that I can only reluctantly bear SMBus1 for a couple of more years. – tlfong01 Nov 20 at 1:32
  • It is difficult to see what your question is. You cannot have 2 programs setting GPIO unless you release between uses. – Milliways Nov 20 at 2:00
  • @tlfong01 thanks for your take on SMBus2 vs SMBus1, I have the same view as you had, although for me it is not an option to lower the clock to 10kHz, as it would be way to slow; I would rather like to work at 400kHz, if you know how to do that, feel free to share! – karelv Nov 20 at 21:35
  • @Milliways, I updated the question. I agree, the 2 scripts I made to show the issue; for isolation from all other clutter I build around it... My point is not to run them in parallel, but if somebody run a program that set some GPIO, and later they run my program it does not work. I don't like that, and I am looking for a solution. – karelv Nov 20 at 21:38
  • Hi @karelv, Rpi3B+ I2C is mission impossible: (1) raspberrypi.org/forums/… (2) raspberrypi.org/forums/…. But if you don't need bus stretching, I heard the rumour aht you can set I2C bus higher than 100kHz. But I need to let my scope to display the real 400kHz or higher wave form to my stupid human eyes. Perhaps I can try to do the possible mission over this weekend. – tlfong01 Nov 21 at 1:08
2

For GPIO 2/3 (pins 3/5) to operate as the I2C bus they must be in a GPIO mode called ALT0.

They are set to this mode when the Linux I2C/SMBus driver is loaded.

The RPi.GPIO module is changing the mode of GPIO 2/3 to INPUT. This stops the proper operation of the I2C bus.

You need to again set the mode to ALT0 for GPIO 2/3 for the bus to operate again.

As far as I am aware the RPi.GPIO module provides no way of setting a GPIO to this mode.

To set GPIO 2 and 3 to mode ALT0 do one of the following.

If the wiringPi gpio utility is installed you can use the command.

gpio -g mode 2 alt0
gpio -g mode 3 alt0

If the pigpio pigs utility is installed you can use the command.

sudo pigpiod # start the daemon if not already running
pigs m 2 0 m 3 0

If the raspi-gpio utility is installed you can use the command.

raspi-gpio set 2 a0
raspi-gpio set 3 a0
  • Up to gpio, I'm aligned with you, then you start with new stuff. – karelv Nov 20 at 21:45
  • 1
    pigs is installed but I got error message: socket connect failed. – karelv Nov 20 at 21:46
  • 1
    raspi-gpio is also installed, no error message, and it works! – karelv Nov 20 at 21:46
  • 1
    raspi-gpio seems part of the package 'raspi-gpio' (dpkg -S raspi-gpio), and was preinstalled with raspbian buster. – karelv Nov 20 at 21:48
  • 1
    I rather stick with raspi-gpio, it works of-the-shelf! Thanks for your help! – karelv Nov 20 at 22:01
2

The mode of each pin is either in, out or any of the other functions (I2C in this case). Pins 3 and 5 are in ALT0 mode by default after reboot if the I2C driver is active. Type gpio readall on the command line to see which pins are in which mode.

To do the same as

gpio mode 8 alt0
gpio mode 9 alt0

in code, you need to do something like

GPIO.setup(3, GPIO.ALT0)
GPIO.setup(5, GPIO.ALT0);

(Not sure about the exact naming at the moment, though).

  • 1
    I like your pure python approach, so I would love it works! I've read that RPi.GPIO is working on I2C (5 years ago), but they will NEVER support ALT0 mode as such. I might be wrong of course, so please can you verify the exact naming convention? Thanks. – karelv Nov 20 at 21:57
  • 1
    @karelv: Yea, will do. sourceforge.net/p/raspberry-gpio-python/code/ci/default/tree/… suggests that it should work, but I'm unsure that this is the right repo, as it doesn't really contain any python code. – PMF Nov 21 at 7:52
  • 1
    It is in fact looking bad. That library seems to be very popular, but it is very poorly documented. I can't even find the declaration of all available commands. sourceforge.net/p/raspberry-gpio-python/wiki/… shows that it's possible to read the pin usage, but not how to write it. – PMF Nov 21 at 21:59
0

To Answer the additional question you asked in Comments:-

RPi.GPIO has a cleanup command which can be used at the end of the program to restore startup state and should resolve the problem of releasing GPIO pins.

I don't actually use RPi.GPIO; gpiozero does this automatically.

  • 1
    Thanks, but.. you know, any program can crash accidentally... so I rather work on my init, the trial_gpio,py is just do demonstrate what a wrong starting condition for the trial_smbus.py. – karelv Nov 21 at 6:49
  • @karelv If you would rather rely on your kludge, rather fixing the underlying cause that is your choice. Well written programs rarely crash. My Pi run for months at a time without error, and only get rebooted to update software. Smbus ie quite properly refusing to take over hardware in use. – Milliways Nov 21 at 7:34
  • 1
    Thanks for the tip, in case I work with the 'trial_gpio.py' as my target application I will take care. My (current) target program is the trial_smbus.py, this is the thing I want to make robust. trial_gpio.py is my 'test-case' to let my program not work. I do this to minimize the work to be done afterwards to support users that do... well, I-do-not-know-what... – karelv Nov 22 at 4:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.