Skip to content

DeviceInfoService leads to out of memory on ESP32 8mb/no PSRAM #216

@hberg32

Description

@hberg32

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
<ProvideServicesAdvertisement flags= services=<BoundServiceList: UUID(0x1818), UUID(0x1826)> comp>
found a CyclingPowerService advertisement
<ProvideServicesAdvertisement flags= services=<BoundServiceList: UUID(0x1818), UUID(0x1826)> 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions