adafruit / adafruit/Adafruit_CircuitPython_BLE
DeviceInfoService leads to out of memory on ESP32 8mb/no PSRAM
- Dominant language
- Python
- Stars
- 138
- Forks
- 58
- PR merge metrics
- No merged PRs in 30d
Description
I've run into an out of memory error with the BLE library and while it is not a blocker for me (I can skip over it), it's weird so I thought I'd report it in case it could be of some use. I don't know how to debug circuitpython but if there is a way to increase the traceback module's "limit" param or turn on exception chaining in boot.py I can try it and report the results.
I've written a BLE parser class for the cycling power service and a simpletest that is a copy of ble_cycling_speed_and_cadence_simpletest.py but with my own service class substituted for CyclingSpeedAndCadenceService and a few prints of gc.mem_free() sprinkled about. It works fine on desktop linux. On an ESP32 8mb/no psram (product 5426) I get an out of memory error when doing `if DeviceInfoService in conn:`. I can only assume that there is something strange about my particular BLE device that causes something to go wrong after `_discover_remote` calls `self._bleio_connection.discover_remote_services`
I'm running the 9.2.8 bootloader with the circuitpython bundle cut on 8/7/2025
Here is the code output:
> Scanning...
> 78064
> services= comp>
> found a CyclingPowerService advertisement
> services= comp>
> found a CyclingPowerService advertisement
> Stopped scanning
> 69344
> Connected 1
> 69120
> Determining manufacturer
> Traceback (most recent call last):
> File "code.py", line 51, in
> File "adafruit_ble/__init__.py", line 88, in __contains__
> File "adafruit_ble/__init__.py", line 66, in _discover_remote
> MemoryError: Nimble out of memory
>
And here is the code:
```
# SPDX-FileCopyrightText: 2020 Dan Halbert for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
Read cycling speed and cadence data from a peripheral using the standard BLE
Cycling Speed and Cadence (CSC) Service.
"""
import time, gc
import adafruit_ble
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.standard.device_info import DeviceInfoService
from cycling_power_service import CyclingPowerService
# Initialize the BLE radio
ble = adafruit_ble.BLERadio()
while True:
print("Scanning...")
print(gc.mem_free())
# Save advertisements, indexed by address
advs = {}
for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=5):
print(adv)
if CyclingPowerService in adv.services:
print("found a CyclingPowerService advertisement")
# Save advertisement. Overwrite duplicates from same address (device).
advs[adv.address] = adv
ble.stop_scan()
print("Stopped scanning")
print(gc.mem_free())
if not advs:
# Nothing found. Go back and keep looking.
continue
# Connect to all available CSC sensors.
cyc_connections = []
for adv in advs.values():
cyc_connections.append(ble.connect(adv))
print("Connected", len(cyc_connections))
print(gc.mem_free())
# Print out info about each sensors.
for conn in cyc_connections:
if conn.connected:
print("Determining manufacturer")
if DeviceInfoService in conn:
dis = conn[DeviceInfoService]
try:
manufacturer = dis.manufacturer
except AttributeError:
manufacturer = "(Manufacturer Not specified)"
print("Device:", manufacturer)
else:
print("No device information")
print("Waiting for data... (could be 10-20 seconds or more)")
# Get CSC Service from each sensor.
cyc_services = []
if len(cyc_connections) > 1:
print("Multiple trainers detected, this implementation just grabs the first one")
for conn in cyc_connections:
cyc_services.append(conn[CyclingPowerService])
# Read data from each sensor once a second.
# Stop if we lose connection to all sensors.
while True:
still_connected = False
for conn, svc in zip(cyc_connections, cyc_services):
if conn.connected:
still_connected = True
print(svc.get_cycling_power_measurement)
print(gc.mem_free())
if not still_connected:
break
time.sleep(1)
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with adafruit_ble/__init__.py, especially __contains__ and _discover_remote, and reproduce the reported DeviceInfoService lookup using the supplied code on an ESP32 8mb/no-PSRAM device. Compare memory usage around discover_remote_services; done means the lookup no longer raises MemoryError in this environment or the limitation is documented with a verified explanation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 38/100