Skip to content

ESP32-S3 dual-role (client + server) fails with rc=519 on all NimBLE-Arduino versions 2.3.x #1016

@Berg0162

Description

@Berg0162

ESP32-S3 dual-role (client + server) fails with rc=519 on all NimBLE-Arduino versions 2.3.x

Description

When using an ESP32-S3 as both BLE central (client) and BLE peripheral (server), advertising (or connection) fails with:
E NimBLEAdvertising: Error enabling advertising; rc=519, Memory Capacity Exceeded

  • This occurs only on NimBLE-Arduino versions 2.3.x.

  • The exact same sketch runs correctly on NimBLE-Arduino version 2.2.3 without changes.

This looks like a regression in how NimBLE configures controller resources (advertising sets, connection pools, or GATT service init) on the ESP32-S3.


Minimal Reproduction Code

#include <NimBLEDevice.h>

static bool clientConnected = false;
const NimBLEAdvertisedDevice* foundDevice = nullptr;

// ========== Central (client) callbacks ==========
class clientCallbacks : public NimBLEClientCallbacks {
    void onConnect(NimBLEClient* pClient) {
        Serial.println(">> Client connected");
        clientConnected = true;
    }
    void onDisconnect(NimBLEClient* pClient) {
        Serial.println(">> Client disconnected");
        clientConnected = false;
    }
};

class clientScanCallbacks : public NimBLEScanCallbacks {
    void onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
        Serial.printf(">> Device found: %s\n", advertisedDevice->toString().c_str());
        foundDevice = advertisedDevice;
        NimBLEDevice::getScan()->stop();
    }
};

// ========== Peripheral (server) setup ==========
void setupServer() {
    // Generic Access (0x1800) is implicit, we add DIS and CPS
    NimBLEServer* pServer = NimBLEDevice::createServer();
    // Device Information Service
    NimBLEService* dis = pServer->createService(NimBLEUUID((uint16_t)0x180A));
    NimBLECharacteristic* dis_ManufacturerName = dis->createCharacteristic(NimBLEUUID((uint16_t)0x2A29), \
                                                                                NIMBLE_PROPERTY::READ);
    dis_ManufacturerName->setValue("Vendor");
    dis->start();

    // Cycling Power Service (basic)
    NimBLEService* cps = pServer->createService(NimBLEUUID((uint16_t)0x1818));
    NimBLECharacteristic* cps_Measurement = cps->createCharacteristic(NimBLEUUID((uint16_t)0x2A63), \
                                                        NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
    cps_Measurement->setValue("W");
    cps->start();

    Serial.println(">> Server is setup and started!"); 
    pServer->start();

    // Advertising Data setup
    NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
    if(pAdvertising->addServiceUUID((uint16_t)0x1818) )
        Serial.println(">> Setting Service [cps] in Advertised data");
    if(pAdvertising->setName("Repro"))
        Serial.println(">> Setting DeviceName in Advertised data");
}

// ========== Main flow ==========
void setup() {
    Serial.begin(115200);
    delay(200);

    Serial.printf("Free heap: %d\n", ESP.getFreeHeap());
    Serial.printf("Free PSRAM: %d\n", ESP.getFreePsram());

    NimBLEDevice::init("Repro");

    setupServer();

    // Client scan setup and start scanning
    NimBLEScan* pScan = NimBLEDevice::getScan();
    pScan->setScanCallbacks(new clientScanCallbacks());
    pScan->setActiveScan(true);
    pScan->start(0, false, false); // NO Restart
    Serial.println(">> Scanning started");
}

void loop() {
    if (foundDevice && !clientConnected) {
        Serial.println(">> Attempting connection...");
        NimBLEClient* client = NimBLEDevice::createClient();
        client->setClientCallbacks(new clientCallbacks(), false);
        if (!client->connect(foundDevice)) {
            Serial.println(">> Connection failed");
        } else {
            Serial.println(">> Connection success");
        }
        foundDevice = nullptr;

        if(NimBLEDevice::startAdvertising()) Serial.println(">> Advertising started!");
        else Serial.println(">> Advertising start FAILED!");

    }

    delay(1000);
}

Steps to Reproduce

  1. Flash above sketch on an ESP32-S3 board (tested with LilyGo T-Display S3 and Seeed XIAO S3).

  2. Have any connectable BLE device nearby (phone or another ESP32 peripheral).

  3. With NimBLE-Arduino version 2.2.3:

    • Device scans, connects, then starts advertising successfully.
  4. With NimBLE-Arduino version later than 2.2.3 (from 2.3.0 to present 2.3.4 tested!):

    • Device scans, connects OK.

    • When advertising starts (or on subsequent connection attempt), fails with:

E NimBLEAdvertising: Error enabling advertising; rc=519, Memory Capacity Exceeded

Expected Behavior

ESP32-S3 should be able to:

  • scan & connect as central, then

  • advertise as peripheral,
    with at least a small set of services (GA, DIS, CPS).
    This worked fine on NimBLE-Arduino 2.2.3.

Actual Behavior

On NimBLE-Arduino versions 2.3.x:

  • Scan + connect still works.

  • Advertising (or connection in reversed order) fails with rc=519.

Environment

  • Board(s): ESP32-S3 (e.g. LilyGo T-Display S3, Seeed XIAO ESP32S3)

  • Arduino-esp32: tested version 3.2.0 until present 3.3.0 (no difference)

  • NimBLE-Arduino:

    • ✅ 2.2.3 → works

    • ❌ versions 2.3.x → fail with rc=519

  • PSRAM: enabled (8 MB available)

  • Heap: ~260 KB free before advertising

Regression Notes

  • The issue only appears starting with NimBLE-Arduino versions 2.3.x

  • Likely related to how versions later than 2.2.3 configure controller resources (advertising sets, connection pools, or GATT service init) for ESP32-S3.

  • Advertising payload size does not affect outcome (tested trimmed down).

  • Scanning and advertising are not active simultaneously.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions