micropython / micropython-lib

Core Python libraries ported to MicroPython
Other
2.3k stars 980 forks source link

Handle MemoryError in aioble_central.py scanner event #865

Open brianreinhold opened 1 month ago

brianreinhold commented 1 month ago

I am using the scanner as follows in my application code

      # The 'scanner' is of type 'scan' defined in aioble
      async with scan(duration_ms=10000,  # The length of the scan cycle in milliseconds. 
                      interval_us=30000,  # The time between each scan listen start
                      window_us=15000,    # The length of time during the scan interval one is actually listening
                                          # If interval_us = window_us the scan is non stop (so called fully duty cycle)
                                          # Background scanning usually sets the window_us to be less than interval_us.
                      active=True) as scanner:    # active = True means do scan requests to get scan responses
                                                  # For PHD we always want to do active scans.

However, every once and a while in the scan event handler a MemoryError is thrown while trying to load a result into the queue shown below:

    if event == _IRQ_SCAN_RESULT:
        addr_type, addr, adv_type, rssi, adv_data = data    # data here is a tuple[int, bytes, int, int, bytes]
        if not _active_scanner:
            return
        try:
            if queue_scan:
                _active_scanner._queue.append((addr_type, bytes(addr), adv_type, rssi, bytes(adv_data)))
                _active_scanner._event.set()
        except MemoryError:
            queue_scan = False
            logger.error('LNI: AIOBLE: Memory allocation error queuing an advertisement/scan response.') # type: ignore
            import gc
            gc.collect()

I have added a handler for the exception and would like to be able to recover from the error but am not sure what else I need to do. With the code shown above, the scanner restarts after 10 seconds but ScanResult objects are no longer created. Not sure why. Do I need to set the event? Do I need to add a filter that blocks repeat advertisements in a given scan cycle? Maybe it is simply not possible to recover from such an error?

Any help is greatly appreciated, especially from the code author.

brianreinhold commented 1 month ago

The concept turns out to work, I was just accessing the variable queue_scan as a global variable in the other module. The proper way to access it in the other module if I want to change it is to reference it as a module variable

import aioble_central

then where I want to reset it to True: aioble_central.queue_scan = True,

This worked. On the MemoryError setting queue_scan to False blocked any further attempt to queue ScanResponses until the queue_scan is reset to True in the module handling the scan cycle (which in this case lasts for 10 seconds)