adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.06k stars 1.2k forks source link

Initializing with board.I2C() multiple times causing crash into safe mode #1603

Closed makermelissa closed 5 years ago

makermelissa commented 5 years ago

I was experimenting with changing from busio.I2C over to board.I2C() for another issue due to conflicts. It worked great in CP 3.1.2, but in both CP4 Beta 2 and the latest build off master, it causes the board to go into safe mode.

makermelissa commented 5 years ago

I forgot to add that this was on a Feather M4 Express.

makermelissa commented 5 years ago

The setup I had for reproduction purposes is I was using the test code Jerry had at: https://github.com/adafruit/Adafruit_CircuitPython_FeatherWing/issues/35 as code.py, the latest master branch at: https://github.com/makermelissa/Adafruit_CircuitPython_FeatherWing, and the latest uncompressed versions of the minimal supporting libraries.

I was just doing a quick test in CP4 before merging when I discovered this.

jerryneedell commented 5 years ago

when I try that test code with your feathewrwing library on a particle argon, I get

Press any key to enter the REPL. Use CTRL-D to reload.
Adafruit CircuitPython 4.0.0-alpha-1966-g3b58be9c7-dirty on 2019-02-24; Particle Argon with nRF52840
>>> import uv_temp_test
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "uv_temp_test.py", line 10, in <module>
  File "/lib/adafruit_sht31d.py", line 95, in __init__
  File "/lib/adafruit_bus_device/i2c_device.py", line 66, in __init__
AttributeError: 'dict' object has no attribute 'try_lock'
>>> 
>>> 

It works if I use my version of the alphanunum_featherewing.py

# The MIT License (MIT)
#
# Copyright (c) 2019 Melissa LeBlanc-Williams for Adafruit Industries LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_featherwing.alphanum_featherwing`
====================================================

Helper for using the `14-Segment AlphaNumeric FeatherWing <https://www.adafruit.com/product/3139>`_.

* Author(s): Melissa LeBlanc-Williams
"""

__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_FeatherWing.git"

import board
import adafruit_ht16k33.segments as segments
from adafruit_featherwing.led_segments import Segments

class AlphaNumFeatherWing(Segments):
    """Class representing an `Adafruit 14-segment AlphaNumeric FeatherWing
       <https://www.adafruit.com/product/3139>`_.

       Automatically uses the feather's I2C bus."""
    def __init__(self, address=0x70):
        super().__init__()
        self._segments = segments.Seg14x4(board.I2C(), address)
        self._segments.auto_write = False
jerryneedell commented 5 years ago

updated to current master CP -- same result


Press any key to enter the REPL. Use CTRL-D to reload.
Adafruit CircuitPython 4.0.0-beta.2-141-g2c9fbb5d4 on 2019-02-24; Particle Argon with nRF52840
>>> 
>>> import uv_temp_test
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "uv_temp_test.py", line 10, in <module>
  File "/lib/adafruit_sht31d.py", line 95, in __init__
  File "/lib/adafruit_bus_device/i2c_device.py", line 66, in __init__
AttributeError: 'function' object has no attribute 'try_lock'
>>> 

still works with my version

dhalbert commented 5 years ago

Is someone calling board.I2C instead of board.I2C() ?

jerryneedell commented 5 years ago

sorry - no @makermelissa has

import board
try:
    I2C_BUS = board.I2C()
except AttributeError:
    import busio
    I2C_BUS = busio.I2C(board.SCL, board.SDA)
jerryneedell commented 5 years ago

and the failure is on the sht31d device, not on the featherwing

jerryneedell commented 5 years ago

if I change the test code to this -- it works with the new featherwing code the changes are to import shared and then call shared.I2C_BUS for the additional sensors

While this works, I think it makes more sense to just eliminate shared.py.

import time
import board
import busio
import adafruit_sht31d
import adafruit_veml6070

from adafruit_featherwing import alphanum_featherwing,shared

display = alphanum_featherwing.AlphaNumFeatherWing()
sensor = adafruit_sht31d.SHT31D(shared.I2C_BUS)
uv = adafruit_veml6070.VEML6070(shared.I2C_BUS)

loopcount = 0
while True:
    uv_raw = uv.read
    risk_level = uv.get_index(uv_raw)
    print('Reading: {0} | Risk Level: {1}'.format(uv_raw, risk_level))
    display.print(uv_raw)
    time.sleep(2)
    temperature = sensor.temperature
    print("\nTemperature: %0.1f C" % temperature)
    display.print(temperature)
    print("Humidity: %0.1f %%" % sensor.relative_humidity)
    loopcount += 1
    time.sleep(2)
    # every 10 passes turn on the heater for 1 second
    if loopcount == 10:
        loopcount = 0
        sensor.heater = True
        print("Sensor Heater status =", sensor.heater)
        time.sleep(1)
        sensor.heater = False
        print("Sensor Heater status =", sensor.heater)

Both this is a discussion fo rthe library Is it acceptable for the SAMD51 to crash to safe mode in this case?

jerryneedell commented 5 years ago

Ran both version on a featherm4 express -- the original version reproduces the safe mode crash the modified version above work normally.

While this works, I think it makes more sense to just eliminate shared.py. But this is a discussion for the featherwing library Is it acceptable for the SAMD51 to crash to safe mode in the orginal case? Why does the nrf throw an error, but the SAMD51 crashes

jerryneedell commented 5 years ago

FYI -- the safe mode crash on the SAMD51 is

Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.

You are running in safe mode which means something unanticipated happened.
Looks like our core CircuitPython code crashed hard. Whoops!
Please file an issue at https://github.com/adafruit/circuitpython/issues
 with the contents of your CIRCUITPY drive and this message:
Crash into the HardFault_Handler.

Press any key to enter the REPL. Use CTRL-D to reload.
dhalbert commented 5 years ago

No, it shouldn't crash.

dhalbert commented 5 years ago

I'm trying to reproduce this in the simplest way possible, and not succeeding. Are you saying that your shared.py has:

import board
try:import board
try:
    I2C_BUS = board.I2C()
except AttributeError:
    import busio
    I2C_BUS = busio.I2C(board.SCL, board.SDA)

And then you get a crash when referencing board.I2C() in other code? The simple REPL version of this on a board with board.I2C() (I'm testing on a Metro M4) is

Adafruit CircuitPython 4.0.0-beta.2-141-g2c9fbb5d4 on 2019-02-26; Adafruit Metro M4 Express with samd51j19
>>> import board,busio
>>> i = board.I2C()
>>> j = board.I2C()
>>> i == j
True

which is what I'd expect. And if I try this, I get an expected error:

>>> import board,busio
>>> i = busio.I2C(board.SCL, board.SDA)
>>> j = board.I2C()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: SDA in use
makermelissa commented 5 years ago

I couldn't get it to happen in the REPL either and I wondered if it might have something to do with calling it in different files or through an import or something. Also, the entire contents of shared.py is basically what @jerryneedell posted above.

makermelissa commented 5 years ago

I also tried commenting out everything except this in shared and it still crashed.

import board
I2C_BUS = board.I2C()
dhalbert commented 5 years ago

This might have to do with class and global dictionaries or something like that. I'm not sure it's related to I2C at all, really. If you can give me the simplest test case you can muster that would be really helpful. Thanks.

makermelissa commented 5 years ago

Ok, I'll keep playing with it and see what I can come up with.

makermelissa commented 5 years ago

I kept trying to boil this down and even just removing a little of the SHT31D module made it behave as expected. It seems to be some kind of bad combination of things. I can go ahead and close this is you want or leave it open for later.

makermelissa commented 5 years ago

Nevermind, I got it to crash again, so I'll keep working on it.

dhalbert commented 5 years ago

I can go ahead and close this is you want or leave it open for later.\

No, please leave it open. A crash is a crash, and needs to be fixed.

makermelissa commented 5 years ago

Ok, thanks Danh. I'll leave it be.

tannewt commented 5 years ago

What is the smallest repro case we have for this? I can take a look tomorrow.

makermelissa commented 5 years ago

Other than the scenario I originally described, I haven't been able to get it to reproduce with anything smaller.

tannewt commented 5 years ago

I've reproduced this on a Feather M4. Will now try on Metro M4 where I can run a debugger.

tannewt commented 5 years ago

I've reproduced this on Metro M4 and confirmed it's long-lived related. (The bug disappears if I turn long-living off.) Still digging into why.