question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

`import board` with MCP2221 fails with `OSError: open failed`

See original GitHub issue

I spent a couple of nights trying to read data from a SCD30 connected to a Linux machine through a MCP2221 following this example, but I was getting a traceback during the import of the board module. To run the MCP2221 I followed this article that explains how to install all the necessary packages and also how to blacklist and replace the native hid_mcp2221 Linux driver (all the post-install checks were successful).

Eventually I narrowed it down to a minimal set of instructions to reproduce the failures consistently.

The first error I got was

>>> import board
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/board.py", line 288, in <module>
    raise NotImplementedError("Board not supported {}".format(board_id))
NotImplementedError: Board not supported GENERIC_LINUX_PC

I was able to fix this by restarting Python, setting the BLINKA_MCP2221 environment variable to 1, and trying import board again. However, I still got an error:

>>> import os; os.environ['BLINKA_MCP2221'] = '1'
>>> import board
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/board.py", line 197, in <module>
    from adafruit_blinka.board.microchip_mcp2221 import *
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/board/microchip_mcp2221.py", line 2, in <module>
    from adafruit_blinka.microcontroller.mcp2221 import pin
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/microcontroller/mcp2221/pin.py", line 2, in <module>
    from .mcp2221 import mcp2221
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/microcontroller/mcp2221/mcp2221.py", line 386, in <module>
    mcp2221 = MCP2221()
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/microcontroller/mcp2221/mcp2221.py", line 55, in __init__
    self._reset()
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/microcontroller/mcp2221/mcp2221.py", line 131, in _reset
    raise OSError("open failed")
OSError: open failed
>>> 

If I try again immediately after, I get a different error:

>>> import board
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/board.py", line 197, in <module>
    from adafruit_blinka.board.microchip_mcp2221 import *
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/board/microchip_mcp2221.py", line 2, in <module>
    from adafruit_blinka.microcontroller.mcp2221 import pin
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/microcontroller/mcp2221/pin.py", line 2, in <module>
    from .mcp2221 import mcp2221
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/microcontroller/mcp2221/mcp2221.py", line 386, in <module>
    mcp2221 = MCP2221()
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/microcontroller/mcp2221/mcp2221.py", line 53, in __init__
    self._hid.open(MCP2221.VID, MCP2221.PID)
  File "hid.pyx", line 113, in hid.device.open
OSError: open failed

In both cases, the error comes from a self._hid.open(MCP2221.VID, MCP2221.PID), and it happens whenever I try to open a device that is already open. By looking at the code, the __init__ of the MCP2221 class does: https://github.com/adafruit/Adafruit_Blinka/blob/1ac53c86297e869220ea41d8740f01852609dd65/src/adafruit_blinka/microcontroller/mcp2221/mcp2221.py#L51-L55 and the _reset method that it calls does (notice the other open at line 125): https://github.com/adafruit/Adafruit_Blinka/blob/1ac53c86297e869220ea41d8740f01852609dd65/src/adafruit_blinka/microcontroller/mcp2221/mcp2221.py#L119-L131

It seems that the first traceback is caused by the fact that the __init__ successfully opens the device (at line 53), but then calls self._reset() at line 55, that tries to open the – already opened – device again (at line 125), causing the traceback. The second traceback can be explained by the fact that after the first error, the __init__ doesn’t clean up after itself and doesn’t call self._hid.close(), so the device is still open when the self._hid.open(MCP2221.VID, MCP2221.PID) at line 53 is executed. However after waiting for a while, the import board fails again in the _reset() method (maybe the device is automatically closed – perhaps after being garbage collected – and the open at line 53 is successful again).

I verified that trying to open an already open device (like _reset does after the __init__, or after two consecutive import board) results in an error:

>>> import hid; h = hid.device(); h.open(0x04D8, 0x00DD);
>>> h.open(0x04D8, 0x00DD)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "hid.pyx", line 113, in hid.device.open
OSError: open failed

I also noticed that the hid.device.close() method sometimes returns immediately, other times it takes about ~16s. After a lot of trial and error, I found that trying to import board makes it slow, and a way to make it faster again is to first open the device, then close Python without closing the device, causing a segfault. After the segfault and after restarting Python, .close() will be faster again:

$ python3
Python 3.9.7 (default, Sep 10 2021, 14:59:43) 
[GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> # now .close() is fast
>>> import hid, time; h = hid.device(); h.open(0x04D8, 0x00DD); t = time.time(); h.close(); time.time() - t
0.0011868476867675781
>>> # try to import board, it will fail and make .close() slow
>>> import os; os.environ['BLINKA_MCP2221'] = '1'
>>> import board
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/board.py", line 197, in <module>
    ...
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/microcontroller/mcp2221/mcp2221.py", line 55, in __init__
    self._reset()
  File "/usr/local/lib/python3.9/dist-packages/adafruit_blinka/microcontroller/mcp2221/mcp2221.py", line 131, in _reset
    raise OSError("open failed")
OSError: open failed
>>> # now the device should still be open, so the next line will fail
>>> import hid, time; h = hid.device(); h.open(0x04D8, 0x00DD); t = time.time(); h.close(); time.time() - t
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "hid.pyx", line 113, in hid.device.open
OSError: open failed
>>> # after a while it should work, but it will be slow now
>>> h = hid.device(); h.open(0x04D8, 0x00DD); t = time.time(); h.close(); time.time() - t
16.50315546989441
>>> # now I can exit Python (with ctrl+d), but the .close() will still be slow
>>> 
$ python3
Python 3.9.7 (default, Sep 10 2021, 14:59:43) 
[GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> # .close() is still slow, even after reopening Python
>>> import hid, time; h = hid.device(); h.open(0x04D8, 0x00DD); t = time.time(); h.close(); time.time() - t
16.573604822158813
>>> # opening the device and quitting Python without closing it will cause a segfault
>>> h.open(0x04D8, 0x00DD)
>>> 
python3: io.c:2115: handle_events: Assertion `ctx->pollfds_cnt >= internal_nfds' failed.
Aborted (core dumped)
$ python3
Python 3.9.7 (default, Sep 10 2021, 14:59:43) 
[GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> # now it will be fast again
>>> import hid, time; h = hid.device(); h.open(0x04D8, 0x00DD); t = time.time(); h.close(); time.time() - t
0.0013420581817626953
>>>

Apparently importing board causes some issue with the MCP2221, that only gets reset after a crash. I think the module should ensure that the device is always closed correctly, both in case of error and in case of success after the program exits regularly. The former could be achieved by wrapping the call to self._reset() in the __init__ in a try/finally that closes the device before letting the exception propagate, the latter by defining a __del__ (unreliable) and/or by using atexit.

This should fix the slow .close() and some other issues, but probably not the import error. To fix the import error, I eventually used this solution:

>>> import os; os.environ['BLINKA_MCP2221'] = '1'
>>> import os; os.environ['BLINKA_MCP2221_RESET_DELAY'] = '20'
>>> import board
>>>
python3: io.c:2115: handle_events: Assertion `ctx->pollfds_cnt >= internal_nfds' failed.
Aborted (core dumped)

This is the only way I found to successfully import board, and after that I was able to run the example and read data from the SCD30 sensor (even though it still crashes upon exit).

There are still a few things that are not clear to me:

  1. Why is the call to self._reset() necessary in the __init__ and what does it do exactly?
  2. Why does _reset open the (already opened) device again, and why does it work after adding a delay?
  3. Can the delay be calculated dynamically, by making a few attempts at increasing time intervals?
  4. What exactly is altering the state of the MCP2221 and causing .close() to become slow?
  5. Why does the linked article requires the native Linux driver to be removed/blacklisted?

#243 and #244 seems related.


Tl;DR:

  • Trying to import board with BLINKA_MCP2221 == 1 (and importing the MCP2221 class) fails unless a 20s delay is added.
  • The MCP2221 class/module leave the MCP2221 in an altered state in case of error or even when closing the program after a successful import.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:14 (12 by maintainers)

github_iconTop GitHub Comments

1reaction
caternusoncommented, Mar 5, 2022
0reactions
caternusoncommented, Mar 8, 2022

Closing this issue. It’s getting off track.

@ezio-melotti I think guide info has generally been updated per all the previous discussion above.

@dalbabur Please open a new issue. This seems to be something specific to your setup. Just tested setting up a MCP2221 up on a Pi4 and it generally worked OK. Also, just to point out - the only thing a MCP2221 is adding that a Pi does not already have natively are the ADCs. Pi’s have I2C, SPI, UART, and tons of GPIO.

BASIC HID TEST

(blinka) pi@raspberrypi:~ $ python3
Python 3.9.2 (default, Mar 12 2021, 04:06:34) 
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import hid
>>> hid.enumerate()
[{'path': b'1-1.3:1.2', 'vendor_id': 1240, 'product_id': 221, 'serial_number': '', 'release_number': 256, 'manufacturer_string': 'Microchip Technology Inc.', 'product_string': 'MCP2221 USB-I2C/UART Combo', 'usage_page': 0, 'usage': 0, 'interface_number': 2}]
>>> device = hid.device()
>>> device.open(0x04D8, 0x00DD)
>>> 

BASIC BLINKA TEST

(blinka) pi@raspberrypi:~ $ python3
Python 3.9.2 (default, Mar 12 2021, 04:06:34) 
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.environ["BLINKA_MCP2221"]
'1'
>>> import board
>>> dir(board)
['G0', 'G1', 'G2', 'G3', 'I2C', 'SCL', 'SDA', '__blinka__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__repo__', '__spec__', '__version__', 'ap_board', 'board_id', 'detector', 'pin', 'sys']
>>>
Read more comments on GitHub >

github_iconTop Results From Across the Web

connections issues with mcp2221 #239 - GitHub
Type "help", "copyright", "credits" or "license" for more information. import board import digitalio button = digitalio.DigitalInOut(board.G0)
Read more >
CircuitPython Libraries on any Computer with MCP2221
Getting an error message about "board" not found or "board" has no attribute ... import spidev >>> spidev. ... Getting OSError: read error...
Read more >
MCP2221 Breakout Module User's Guide
This document describes how to use the MCP2221 Breakout Module as a development tool to emulate and debug firmware on a target board....
Read more >
Raspberry real time clock with Ubuntu - python
I'm trying to get the sb components USB RTC for Raspberry Pi working with my Raspberry running Ubuntu. It works when I use...
Read more >
Contributing - Open Issues - CircuitPython
You can also find us in the #circuitpython channel on the Adafruit Discord. Have an idea for a new driver or library? File...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found