MPL3115A2 hangs intermittently



  • I’m using a WiPy 2 to gather data from a PySense board and a one-wire DS18X20 temperature sensor, send it to a data logging machine over UDP, deep sleep for a minute, and repeat. So far it’s run for up-to seven hours before it stops sending. This has happened twice. Both times I’ve been able to Telnet in and ctrl-C the program, and both times I’ve gotten the same traceback:

    File "main.py", line 45, in <module>
    File "/flash/lib/MPL3115A2.py", line 66, in __init__
    File "/flash/lib/MPL3115A2.py", line 77, in _read_status
    

    So it seems as if _read_status() might be stuck in an infinite loop. Has anyone else run into similar problems? Could the MPL3115A2 need an occasional reset of some kind? My thought is to add some kinds of explicit timeout to _read_status(), or use the watchdog timer. Suggestions welcome.

    Cheers,
    Tim


  • administrators

    hi @smbunn,
    The code was not modified.
    On a second thought, the altitude is just a simple function of the pressure, considering the same sea-level pressure. This is not ok, as the sea-level pressure varies in time, and from place to place.
    So, I would recommend, to use just the pressure, and if altitude is required, some arithmetics code has to be added, to get the right altitude. You could use some weather forecast service to obtain the accurate and real-time sea-level pressure.



  • Has this problem been fixed? MY code wants to call the MPL3115A2 twice, once in pressure mode and once in atitude mode so needs to be called repeatedly.

    for count in range (2000):
        print("Count=", count)
        vt = py.read_battery_voltage()
        print("Battery voltage: " +str(vt))
        dew = si.dew_point()
        print("Dew point: "+ str(dew) + " deg C")
        mp = MPL3115A2(py,mode=ALTITUDE) # Returns height in meters. Mode may also be set to PRESSURE, returning a value in Pascals
        print("MPL3115A2 temperature: " + str(mp.temperature()))
        temp1 = mp.temperature()
        alt1 = mp.altitude()+100.0
        print("Altitude: " + str(alt1))
        mpp = MPL3115A2(py,mode=PRESSURE) # Returns pressure in Pa. Mode may also be set to ALTITUDE, returning a value in meters
        press1 = mpp.pressure()
        print("Pressure: " + str(press1))
    

    so declaring it only once probably will not work?



  • @catalin Dear Catalin, the workaround indeed works fine. You should add it to the Pysense documentation to prevent other people falling in the same trap.

    However, I am still left with one concern. Could it be that the main while True: pass loop is consuming too much processing power? I have seen this on Linux systems using htop and it was also reported on StackExchange.

    Hence, my remaining questions are:

    • How can I check upon MCU usage as htop is not available?
    • Should I add a utime.sleep(0.2) to the main while True: loop?
    • When are we going to see uasyncio being support by Pycom? Under my incentive, a couple of good souls removed the last obstacles for uasyncio adoption.

  • administrators

    I've verified the assumption that garbage collector is the problem, by calling it every loop, gc.collect(). In about 3 minutes, it's still blocked.
    The problem seems to be the I2C module.
    Meanwhile, I guess, you can use the workaround.


  • administrators

    Hi guys,

    Sorry for the delayed answer.
    I was able to have the MPL3115A2 interrogation run for ~14 hours, by simply creating the object once, and periodically just calling pressure() method. Would this solve your problem?
    Probably, a better solution is to have this MPL3115A2 class made static (as in Java).
    With the previous version, the problem could have been the garbage collector, as for each new instance, new memory(heap) is allocated.

    Regarding I2C lines, they were designed for up to 400kHz baudrate.

    Code is here:

    import pycom
    import pysense
    import machine
    from MPL3115A2 import MPL3115A2, PRESSURE
    import utime
    import sys
    
    class Device:
    
        def __init__(self):
            self.iteration = 0
            self.p = MPL3115A2(board, mode=PRESSURE)    # create object just once
            self.sense(self)
            self.timer = machine.Timer.Alarm(self.sense, 3, periodic=True)
    
        def sense(self, device):
            # barometer mode, not raw, oversampling 128, minimum time 512 ms
            self.iteration += 1
            print('%f Pa | iteration %d' % (self.p.pressure(), self.iteration))
            pycom.rgbled(0x002200)
            utime.sleep(0.1)
            pycom.rgbled(0x000000)
    
    pycom.wifi_on_boot(False)
    pycom.heartbeat(False)
    board = pysense.Pysense()
    device = Device()
    
    try:
        while True:
            pass
    except KeyboardInterrupt:
        device.timer.cancel()
        sys.exit()
    


  • @catalin Lowering the I2C clock frequency can do wonders.
    A frequency as low as 50kHz would probably do fine and increase reliability.
    Also, keep the trace capacitance of SCL and SDA as low as possible.



  • @catalin Does the Pysense employ I2C for communicating with the MPL3115A2?
    If so, I am pretty sure we are dealing with an I2C problem.

    The following reading material hints into this direction:



  • @catalin I do not know if this is related, but it might be helpful. A similar problem with an MPL3115A2 has been reported a while back on a Raspberry Pi.

    i2c i2c-1: transfer setup timed out was the error given by the Raspberry Pi.


  • administrators

    Hi guys, I do reproduce the bug, I am trying to investigate.



  • @catalin I posted example code to reproduce the MPL3115A2 hang-up within minutes.





  • I am experiencing exactly the same problem with the following short piece of code. Within a minute or two (it varies) the thing hangs without any error message.

    Serial communication also breaks down and a power cycle is required to regain communication with the LoPy Pysense combo.

    import pycom
    import pysense
    import machine
    from MPL3115A2 import MPL3115A2, PRESSURE
    import utime
    import sys
    
    class Device:
    
        def __init__(self):
            self.iteration = 0
            self.sense(self)
            self.timer = machine.Timer.Alarm(self.sense, 3, periodic=True)
    
        def sense(self, device):
            # barometer mode, not raw, oversampling 128, minimum time 512 ms
            self.p = MPL3115A2(board, mode=PRESSURE).pressure()    # in Pa 
            self.iteration += 1
            print('%f Pa | iteration %d' % (self.p, self.iteration))
            pycom.rgbled(0x002200)
            utime.sleep(0.1)
            pycom.rgbled(0x000000)
    
    pycom.wifi_on_boot(False)
    pycom.heartbeat(False)
    board = pysense.Pysense()
    device = Device()
    
    try:
        while True:
            pass
    except KeyboardInterrupt:
        device.timer.cancel()
        sys.exit()


  • Following-up … this has continued to be an issue for me, with my WiPy 2 consistently hanging within 2 - 8 hours of operation (I’m waking-up from deep sleep once per minute to gather data and send it over HTTP). Since removing the MPL3115A2 code completely, it’s been running for 24 hours without a problem.

    Cheers,
    Tim



  • @catalin - thanks for looking into this! Here’s all of my sensor-related code (in case there’s some interaction). I’m re-running this code every time the machine boots, including after every deep sleep. FWIW, my impression was that, except for a few bits of information like machine.reset_cause(), returning from deep sleep is like booting from scratch … is that not the case?

    Thanks in advance,
    Tim

        sensors = pysense.Pysense()
        mpp = MPL3115A2.MPL3115A2(sensors, mode=MPL3115A2.PRESSURE) # This is line 45, where it hangs
        si = SI7006A20.SI7006A20(sensors)
        lt = LTR329ALS01.LTR329ALS01(sensors)
    
        ow = onewire.OneWire(machine.Pin("P10"))
        external_temperature = onewire.DS18X20(ow)
        external_temperature.start_conversion()
        short_wl, long_wl = lt.light()
        time.sleep(1.0) # The onewire thermometer requires ~750ms to take a reading
    
        record = {
            "battery": {"value": sensors.read_battery_voltage(), "units": "V"},
            "temperature/internal/1": {"value": mpp.temperature(), "units": "degC"},
            "pressure": {"value": mpp.pressure(), "units": "pascal"},
            "temperature/internal/2": {"value": si.temperature(), "units": "degC"},
            "humidity": si.humidity(),
            "light/ambient/short": short_wl,
            "light/ambient/long": long_wl,
            "temperature/water/1": {"value": external_temperature.read_temp_async(), "units": "degC"},
        }
    

  • administrators

    Hi @tshead, Could you post the portion of the python code where you call functions from MPL3115A2.py? Are you re-initiating (make a new instance) after each deep sleep?


 

Pycom on Twitter