adafruit / Adafruit_CircuitPython_BLE

Bluetooth Low Energy (BLE) library for CircuitPython
MIT License
127 stars 58 forks source link

Memory leak after repeated BLE reconnection and UARTService activation #192

Closed gkecskes78 closed 8 months ago

gkecskes78 commented 1 year ago

CircuitPython version

Adafruit CircuitPython 8.2.0 on 2023-07-05; PCA10059 nRF52840 Dongle with nRF52840

Code/REPL

import os, gc
import time
import board
import busio
import _bleio
import pwmio
import storage
import analogio
import microcontroller
from adafruit_ble import BLERadio
from digitalio import DigitalInOut, Direction
from adafruit_mcp230xx.mcp23017 import MCP23017
from adafruit_ble.services.nordic import UARTService
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
import adafruit_ads1x15.ads1015 as ADS

devices=[['092','fcc27e0b28b8']]

ble=BLERadio()
uart=UARTService()
advert=ProvideServicesAdvertisement(uart)
addr=advert.address
_bleio.adapter.enabled=True
adapt_address=str(_bleio.adapter.address).replace(':','')[9:-1]
for item in devices:
    if item[1]==adapt_address:
        _bleio.adapter.name=item[0]

print()
print("devices:"+str(devices))
print('BLE addr: {}'.format(_bleio.adapter.address))

while True:
    print("Scanning ...")
    for adv in ble.start_scan(ProvideServicesAdvertisement,timeout=1):
        print("adv: "+str(adv.address))
        add=str(adv.address).replace(":","")[9:-1]
        if add==devices[0][1]:
            print("Address found !")
            if UARTService not in adv.services:
                continue
            loop=1
            cnt=cnt1=None
            while True:
                try:
                    print("loop "+str(loop))
                    cnt=ble.connect(adv)
                    cnt1=cnt[UARTService]
                    print(" -> OK")
                    print("Free memory: %s"%str(gc.mem_free()))
                    print("Allocated memory: %s"%str(gc.mem_alloc()))
                    time.sleep(2)
                    cnt.disconnect()
                    time.sleep(2)
                    del cnt
                    del cnt1
                    gc.collect()
                except Exception as e:
                    print("Error: "+str(e))
                    time.sleep(2)
                loop+=1

Behavior

]0;๐ŸBLE:Off | main.py | 8.2.0\]0;๐ŸBLE:Off | main.py | 8.2.0\
devices:[['092', 'fcc27e0b28b8']]
BLE addr: <Address f2:7e:49:15:b9:09>
Scanning ...
adv: <Address fc:c2:7e:0b:28:b8>
Address found !
loop 1
]0;๐ŸBLE:Ok | main.py | 8.2.0\ -> OK
Free memory: 89312
Allocated memory: 48880
]0;๐Ÿ‘‚LE:Off | main.py | 8.2.0\loop 2
]0;๐Ÿ‘‚LE:Ok | main.py | 8.2.0\ -> OK
Free memory: 90576
Allocated memory: 47616
]0;๐ŸBLE:Off | main.py | 8.2.0\loop 3
]0;๐Ÿ‘‚LE:Ok | main.py | 8.2.0\ -> OK
Free memory: 90000
Allocated memory: 48192
]0;๐Ÿ‘‚LE:Off | main.py | 8.2.0\loop 4
]0;๐ŸBLE:Ok | main.py | 8.2.0\ -> OK
Free memory: 89424
Allocated memory: 48768
]0;๐ŸBLE:Off | main.py | 8.2.0\loop 5

##
loops 5 to 154
##

]0;๐Ÿ‘‚LE:Off | main.py | 8.2.0\loop 155
]0;๐ŸBLE:Ok | main.py | 8.2.0\ -> OK
Free memory: 2448
Allocated memory: 135744
]0;๐Ÿ‘‚LE:Off | main.py | 8.2.0\loop 156
]0;๐Ÿ‘‚LE:Ok | main.py | 8.2.0\ -> OK
Free memory: 1872
Allocated memory: 136320
]0;๐Ÿ‘‚LE:Off | main.py | 8.2.0\loop 157
]0;๐ŸBLE:Ok | main.py | 8.2.0\ -> OK
Free memory: 1296
Allocated memory: 136896
]0;๐ŸBLE:Off | main.py | 8.2.0\loop 158
]0;๐ŸBLE:Ok | main.py | 8.2.0\ -> OK
Free memory: 720
Allocated memory: 137472
]0;๐Ÿ‘‚LE:Off | main.py | 8.2.0\loop 159
]0;๐ŸBLE:Ok | main.py | 8.2.0\ -> OK
Free memory: 960
Allocated memory: 137232
]0;๐ŸBLE:Off | main.py | 8.2.0\loop 160
]0;๐ŸBLE:Ok | main.py | 8.2.0\Traceback (most recent call last):
  File "main.py", line 48, in <module>
  File "adafruit_ble/__init__.py", line 108, in __getitem__
  File "adafruit_ble/services/nordic.py", line 63, in __init__
  File "adafruit_ble/characteristics/__init__.py", line 222, in __get__
  File "adafruit_ble/characteristics/stream.py", line 81, in bind
MemoryError: memory allocation failed, allocating 64 bytes

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "main.py", line 59, in <module>
MemoryError: memory allocation failed, allocating 64 bytes
]0;๐ŸBLE:Ok | 59@main.py MemoryError | 8.2.0\
Code done running.

Press any key to enter the REPL. Use CTRL-D to reload.

Description

Hi guys,

I created this issue based on this forum entry. Basically I got in memory allocation trouble after repeated BLE reconnection and subsequent UARTService activation.

Additional information

No response

dhalbert commented 8 months ago

It took a while to track this down, but it appears that the reason for the leak is that CharacteristicBuffer.deinit() is not being called when the CharacteristicBuffer used for one direction of the UARTService is no longer in use. I have a fix for this in the adafruit_ble library.

It's possible we may be able to detect in _bleio when to call deinit(), but I am not at all sure. If we could, we might not need that exposed routine.

I'll discuss this with @tannewt, and maybe move the issue to the library.

dhalbert commented 8 months ago

The leak is due to _bleio.CharacteristicBuffer.deinit(). Not being called. This is best done in the library when a connection is closed.