pyftdi icon indicating copy to clipboard operation
pyftdi copied to clipboard

Unable to open two pyftdi spi instances even though they are both listed using ftdi_urls.py

Open schoeler opened this issue 4 years ago • 5 comments

Hi, I am encountering a strange issue.

I have two discrete (separate) FT232H devices, here is the output of ftdi_urls.py -d -v:

Available interfaces:
  ftdi://ftdi:232h:1/1   ()
  ftdi://ftdi:232h:2/1   ()

If I have both plugged in, I can only open the one enumerated on ftdi://ftdi:232h:1/1.
Opening on ftdi://ftdi:232h:2/1 will reported "OSError: No such device".

I can plug each one in one at a time so they both exist on ftdi://ftdi:232h:1/1 at different times and I am able to open the connection. The problem is even though after I plug the 2nd one in, and it comes back on ftdi_urls.py I am unable to open on the ftdi://ftdi:232h:2/1.

Unfortunately neither of them have a serial number, and they have identical VID and PID. I wonder if this is somehow the part of the issue.

Is there some way I can give them a serial number? I know I can specify the serial number in the ftdi url string if they had one.

schoeler avatar Nov 15 '20 14:11 schoeler

I use the FT Prog tool to give both of them serial numbers, and ftdi_urls.py now returns those instead:

Available interfaces:
  ftdi://ftdi:232h:FT4YSHD2/1   (USB <-> Serial Converter)
  ftdi://ftdi:232h:FT5B0PCB/1   (USB <-> Serial Converter)

If I use those returned urls both open successfully. Not sure why not having a serial number would return a bad URL before.

schoeler avatar Nov 15 '20 15:11 schoeler

For reference, part used is from a https://www.adafruit.com/product/2264 breakout board. Not sure why they didn't have serial numbers.

schoeler avatar Nov 15 '20 15:11 schoeler

similar to https://github.com/adafruit/Adafruit_Blinka/issues/260

the OS simply needs a way to ID one device from the other, otherwise the OS sees both devices as the same device. Not a problem with any of the libraries as far as I can tell.

mcprat avatar Nov 15 '20 23:11 mcprat

Opening a USB port from an index was a very bad idea from the early beginning of PyFtdi, over 10 years ago. Simply because this indexing is not reproducible from one session to another.

There is an on-going (but stalled due to lack of spare time) effort to change the syntax for this deprecated index-based addressing mode.

When several FTDI devices are connected to the host, you can open a USB / PyFtdi device with either:

  1. Its serial number, which is the safer way and only 100% reproducible way to select a device, or
  2. Its bus/address identifier, with the alternate syntax ftdi://vendor:product:bus:address/port, i.e. 3 : in the host part of the URL.

The second mode is ok as long as you always plug the device on the very same USB port / hub topology (which is a mess under Windows, as usual :-))

Most FTDI devices (but the newest one) required an external EEPROM to store/retrieve their configuration, among which the serial number is encoded. Therefore, it is possible that some cheap boards cannot be programmed to use a serial number, if the EEPROM is missing.

eblot avatar Nov 16 '20 09:11 eblot

AnnaGiasson created an init for us to use this library with. In it, she found this technique:

from io import StringIO from contextlib import redirect_stdout

in the init:

    if isinstance(ftdi, (I2cController, Use_Ftdi)):  # use existing i2c

        if isinstance(ftdi, I2cController):
            self.i2c_master = ftdi
        else:
            self.i2c_master = ftdi.i2c_master

        # this assumes its the first dongle found
        idn = self.i2c_master.ftdi.list_devices()[0][0]

        self.ftdi_url = 'ftdi://{}:{}:{}/{}'.format(idn.vid, idn.pid,
                                                    idn.sn, idn.address)

    else:  # make a new connection
        self.i2c_master = I2cController()

        if ftdi is None:  # url was not passed
            # gets first found
            try:
                self.ftdi_url = get_available_ftdi_urls()[0][0]
            except IndexError:
                raise OSError('No FTDI device connections were found, '
                              'unable to connect')
        else:
            self.ftdi_url = ftdi

"""We will probably publish this repo soon. Just have to sanitize a few more internal names off the code. Basically what occurs, is that the dongles / FTDI devices are in the system with the libusb already setup for each. Now when our code runs, we do something like this"""

try: i2cURL = get_available_ftdi_urls()[0][0] # ...urls()[dongle position in the list, 0 if only one][dongle url is # element 0] except IndexError: print('No FTDI Devices Found, plugged in? Check USB connections...') print('If it is plugged in, did you change the driver for THIS dongle?')

""" make an instance of the i2c dongle and hunt for addresses""" try: i2c = Use_Ftdi(ftdi=i2cURL, **ftdi_options) except FtdiError: input('is there an i2c device connected or powered? press any key...') """ now we pass i2c as the instance of the dongle to whatever functions we need to use it. your system would find two URLs.. so you want to assign them.

Good luck. p.s. triple quotes etc used to make this more readable without the font and bold coming and going. ""'

TedKus avatar Dec 14 '20 14:12 TedKus