h2zero / NimBLE-Arduino

A fork of the NimBLE library structured for compilation with Arduino, for use with ESP32, nRF5x.
https://h2zero.github.io/NimBLE-Arduino/
Apache License 2.0
672 stars 138 forks source link

increasing heap consumption over time #694

Closed Tschmidt9490 closed 1 week ago

Tschmidt9490 commented 1 week ago

My heap is slowly getting eaten up over time. This is ultimately causing a reboot of the device over time. I get the following issues.

Backtrace: 0x40083845:0x3fff1d70 0x400940b1:0x3fff1d90 0x40099cd1:0x3fff1db0 0x4017a3fb:0x3fff1e30 0x4017a442:0x3fff1e50 0x4017b1b1:0x3fff1e70 0x4017a568:0x3fff1e90 0x4018abe1:0x3fff1eb0 0x4018f41e:0x3fff1ef0 0x4018f83c:0x3fff1f60 0x40194f26:0x3fff1f80 0x40194d0a:0x3fff1fc0 0x40195144:0x3fff1fe0 0x4019376d:0x3fff2000 0x40090a46:0x3fff2020 0x40188f2a:0x3fff2040

0 0x40083845:0x3fff1d70 in panic_abort at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_system/panic.c:408

1 0x400940b1:0x3fff1d90 in esp_system_abort at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_system/esp_system.c:137

2 0x40099cd1:0x3fff1db0 in abort at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/newlib/abort.c:46

3 0x4017a3fb:0x3fff1e30 in pbus_rx_dco_cal_1step at /home/cff/gittree/chip7.1_phy/chip_7.1/board_code/app_test/pp/phy/phy_chip_v7_cal.c:729

4 0x4017a442:0x3fff1e50 in pbus_rx_dco_cal_1step at /home/cff/gittree/chip7.1_phy/chip_7.1/board_code/app_test/pp/phy/phy_chip_v7_cal.c:738

5 0x4017b1b1:0x3fff1e70 in ram_rfcal_pwrctrl at /home/cff/gittree/chip7.1_phy/chip_7.1/board_code/app_test/pp/phy/phy_chip_v7_cal.c:1685

6 0x4017a568:0x3fff1e90 in pbus_rx_dco_cal_1step at /home/cff/gittree/chip7.1_phy/chip_7.1/board_code/app_test/pp/phy/phy_chip_v7_cal.c:769 (discriminator 1)

7 0x4018abe1:0x3fff1eb0 in NimBLEDevice::setSecurityAuth(unsigned char) at .pio/libdeps/adafruit_feather_esp32_v2/NimBLE-Arduino/src/NimBLEDevice.cpp:1021

8 0x4018f41e:0x3fff1ef0 in ble_att_svr_fill_type_value at .pio/libdeps/adafruit_feather_esp32_v2/NimBLE-Arduino/src/nimble/nimble/host/src/ble_att_svr.c:1159

9 0x4018f83c:0x3fff1f60 in ble_att_svr_prev_handle at .pio/libdeps/adafruit_feather_esp32_v2/NimBLE-Arduino/src/nimble/nimble/host/src/ble_att_svr.c:143

10 0x40194f26:0x3fff1f80 in ble_gatts_count_cfg at .pio/libdeps/adafruit_feather_esp32_v2/NimBLE-Arduino/src/nimble/nimble/host/src/ble_gatts.c:2161

11 0x40194d0a:0x3fff1fc0 in ble_gatts_chr_updated at .pio/libdeps/adafruit_feather_esp32_v2/NimBLE-Arduino/src/nimble/nimble/host/src/ble_gatts.c:1612

12 0x40195144:0x3fff1fe0 in ble_hs_timer_sched at .pio/libdeps/adafruit_feather_esp32_v2/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs.c:482

13 0x4019376d:0x3fff2000 in ble_gattc_disc_chr_uuid_rx_adata at .pio/libdeps/adafruit_feather_esp32_v2/NimBLE-Arduino/src/nimble/nimble/host/src/ble_gattc.c:2487

14 0x40090a46:0x3fff2020 in ble_npl_event_run at .pio/libdeps/adafruit_feather_esp32_v2/NimBLE-Arduino/src/nimble/porting/npl/freertos/include/nimble/nimble_npl_os.h:526

  (inlined by) nimble_port_run at .pio/libdeps/adafruit_feather_esp32_v2/NimBLE-Arduino/src/nimble/porting/nimble/src/nimble_port.c:319

15 0x40188f2a:0x3fff2040 in std::enable_if<std::and_<std::_not<std::is_tuple_like<unsigned char> >, std::is_move_constructible<unsigned char>, std::is_move_assignable<unsigned char> >::value, void>::type std::swap<unsigned char>(unsigned char&, unsigned char&) at c:\users\scoth.platformio\packages\toolchain-xtensa-esp32\xtensa-esp32-elf\include\c++\8.4.0\bits/move.h:194

  (inlined by) std::_Vector_base<unsigned char, std::allocator<unsigned char> >::_Vector_impl::_M_swap_data(std::_Vector_base<unsigned char, std::allocator<unsigned char> >::_Vector_impl&) at c:\users\scoth\.platformio\packages\toolchain-xtensa-esp32\xtensa-esp32-elf\include\c++\8.4.0\bits/stl_vector.h:112
  (inlined by) std::vector<unsigned char, std::allocator<unsigned char> >::swap(std::vector<unsigned char, std::allocator<unsigned char> >&) at c:\users\scoth\.platformio\packages\toolchain-xtensa-esp32\xtensa-esp32-elf\include\c++\8.4.0\bits/stl_vector.h:1373
  (inlined by) NimBLEAdvertising::setManufacturerData(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) at .pio/libdeps/adafruit_feather_esp32_v2/NimBLE-Arduino/src/NimBLEAdvertising.cpp:159

Is there a way to track this down?

I believe it might be related to this function in my code

void BLEAppInterface_startAdvertising() { if (!pAdvertising) { pAdvertising = NimBLEDevice::getAdvertising(); } else { pAdvertising->stop(); // Ensure previous advertising is stopped }

if (!isUUIDAdded) {
    try {
        pAdvertising->addServiceUUID(pFireplaceControlService->getUUID());
        isUUIDAdded = true;
    } catch (const std::bad_alloc& e) {
        Serial.println("Failed to add Service UUID to Advertising: Out of Memory");
        return;
    } catch (...) {
        Serial.println("Failed to add Service UUID to Advertising: Unknown Error");
        return;
    }
}

uint8_t currentTick = _getCurrentTick();
uint8_t currentStatus = _getCurrentStatus();
uint8_t currentTankLevel = _getCurrentTankLevel();
uint8_t currentFuelConsumption = _getCurrentFuelConsumption();

// Check if there are changes in the values (excluding currentTick)
if (currentStatus == previousStatus && 
    currentTankLevel == previousTankLevel && 
    currentFuelConsumption == previousFuelConsumption) {
    Serial.println("No changes in advertising data, skipping advertising.");
    return;
}

uint8_t Adv_DATA[] = {
    0x7f, 0x01, // build ManufacturerData
    0x7e, 0x88, 0xfd,
    currentTick,
    0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab,
    currentStatus, currentTankLevel, currentFuelConsumption, 0x00, 0x00, _getCurrentTankSize()
};

std::string manufacturerData(reinterpret_cast<char*>(Adv_DATA), sizeof(Adv_DATA));
pAdvertising->setManufacturerData(manufacturerData); // Only one argument now
pAdvertising->setScanResponse(true);
pAdvertising->start();

Serial.println("Advertising Started");

// Update previous values
previousStatus = currentStatus;
previousTankLevel = currentTankLevel;
previousFuelConsumption = currentFuelConsumption;

}

h2zero commented 1 week ago

I'm assuming you found the issue? Was it something in the library?

Tschmidt9490 commented 1 week ago

I made a mistake with some connection for my BLE HID remote. It seems to be fixed and had nothing to do with the App implementation side of the code. The heap is now under control.

I do have a reconnection issue though. I am almost giving up on getting this remote to reconnect after a reboot of the esp32...

h2zero commented 1 week ago

Thanks. What is happening with reconnecting? Some changes were made lately to help reconnecting with bonded devices.

Tschmidt9490 commented 1 week ago

It does not seem to do it at all actually. Once i restart i need to have a button pressed on the remote. If not no device is shown which seems okay. I guess the remote is going in to sleep mode if no button is pressed for X time.

I use a slightly modified version of the "NimBLE_client.ino" example to accomodate the HID spec.

include "remote.h"

include

include

static uint32_t scanTime = 0; // Scan time in seconds

int bleServers = 0; bool doConnect = false; NimBLEAdvertisedDevice *advertisedDevice = nullptr; bool commandProcessed = false; char hexStr[5] = {0}; extern unsigned long lastUserActivityTime;

static NimBLEUUID batteryServiceUUID("180F"); static NimBLEUUID batteryLevelCharUUID("2A19");

extern bool remoteDebug;

void setHexStr(const char* value) { strncpy(hexStr, value, sizeof(hexStr) - 1); hexStr[sizeof(hexStr) - 1] = '\0'; // Ensure null termination commandProcessed = false; // Reset the processed flag when a new value is set }

const char* getHexStr() { return hexStr; }

/ None of these are required as they will be handled by the library with defaults. Remove as you see fit for your needs / void ClientCallbacks::onConnect(NimBLEClient pClient) { Serial.println("Connected"); / After connection we should change the parameters if we don't need fast response times.

void AdvertisedDeviceCallbacks::onResult(NimBLEAdvertisedDevice *ad) { if (ad->isAdvertisingService(NimBLEUUID(HID_SERVICE))) { NimBLEDevice::getScan()->stop(); } advertisedDevice = ad; doConnect = true; }

void handleBatteryLevelNotification(uint8_t* data, size_t length) { if (length > 0) { int batteryLevel = data[0]; // Assuming the battery level is the first byte int barValue;

    // Convert battery percentage to a value between 1 and 4 based on intervals
    if (batteryLevel <= 25) {
        barValue = 1;
    } else if (batteryLevel <= 50) {
        barValue = 2;
    } else if (batteryLevel <= 75) {
        barValue = 3;
    } else {
        barValue = 4;
    }

    if (remoteDebug) {
        Serial.print("Battery Level: ");
        Serial.print(batteryLevel);
        Serial.println("%");
        Serial.print("Bar Value: ");
        Serial.println(barValue);
    }

    lv_bar_set_value(ui_Bar1, barValue, LV_ANIM_ON);
}

}

void handleHIDNotification(uint8_t* data, size_t length) { if ((length == 8) || (length == 3)) { // Determine the target index based on the data length size_t targetIndex = (length == 8) ? 2 : 0;

    if (remoteDebug) {
        Serial.println("Data Received:");
        Serial.printf("Length: %zu, Targeting byte at index: %zu\n", length, targetIndex);

        char hexStr[3]; // Buffer for 2 hex digits + null terminator
        snprintf(hexStr, sizeof(hexStr), "%02X", data[targetIndex]); // Convert the targeted byte to hex string
        Serial.printf("Byte at Index %zu in Hex: %s\n", targetIndex, hexStr);

        setHexStr(hexStr);
    }
} else {
    if (remoteDebug) {
        Serial.println("Received data length does not meet the required criteria.");
    }
}

}

void buttonPressCallback(NimBLERemoteCharacteristic characteristic, uint8_t data, size_t length, bool is_notify) { if (!is_notify) return; // If the callback was not triggered by a notification, exit lastUserActivityTime = millis(); // update last user activity // Get the UUID of the characteristic that triggered the notification NimBLEUUID charUUID = characteristic->getUUID();

// Print the characteristic UUID
if (remoteDebug) {
    Serial.print("Notification received from characteristic: ");
    Serial.println(charUUID.toString().c_str());
}

// Check if this is a notification from the Battery Level characteristic
if (charUUID.equals(NimBLEUUID((uint16_t)0x2A19))) {
    handleBatteryLevelNotification(data, length);
} else {
    handleHIDNotification(data, length);
}

}

void scanEndedCB(NimBLEScanResults results) { Serial.println("Scan Ended"); }

static ClientCallbacks clientCB;

bool connectToServer() { lastUserActivityTime = millis(); lv_label_set_text(ui_Label10, "Connecting remote"); lv_obj_set_style_text_font(ui_Label10, &lv_font_montserrat_28, LV_PART_MAIN | LV_STATE_DEFAULT); int startTime = millis(); NimBLEClient *pClient = nullptr;

if (NimBLEDevice::getClientListSize()) {
    pClient = NimBLEDevice::getClientByPeerAddress(advertisedDevice->getAddress());
    if (pClient) {
        if (!pClient->connect(advertisedDevice, false)) {
            if (remoteDebug) {Serial.println("reconnect failed");}
            return false;
        }
        if (remoteDebug) {Serial.println("reconnected client");}
    } else { pClient = NimBLEDevice::getDisconnectedClient(); }
}

/** No client to reuse? Create a new one. */
if(!pClient) {
    if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
        Serial.println("Max clients reached - no more connections available");
        return false;
    }

    pClient = NimBLEDevice::createClient();

    Serial.println("New client created");

    pClient->setClientCallbacks(&clientCB, false);
    /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
     *  These settings are safe for 3 clients to connect reliably, can go faster if you have less
     *  connections. Timeout should be a multiple of the interval, minimum is 100ms.
     *  Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout
     */
    pClient->setConnectionParams(12,12,0,51);
    /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
    pClient->setConnectTimeout(20);

    if (!pClient->connect(advertisedDevice)) {
        /** Created a client but failed to connect, don't need to keep it as it has no data */
        NimBLEDevice::deleteClient(pClient);
        Serial.println("Failed to connect, deleted client");
        return false;
    }
}

if (!pClient->isConnected()) {
    if (!pClient->connect(advertisedDevice)) {
        if (remoteDebug) {
            Serial.println("failed to connect");
        }
        return false;
    }
}

// Use the discovered services and characteristics
auto services = pClient->getServices(true);
static NimBLEUUID hidServiceUUID(HID_SERVICE);
for (auto service : *services) {
    auto serviceUUID = service->getUUID();
    if (serviceUUID.equals(hidServiceUUID)) {
        if (remoteDebug) {
            Serial.println("-> Found HID service");
        }

        auto characteristics = service->getCharacteristics(true);
        for (auto characteristic : *characteristics) {
            if (remoteDebug) {
                Serial.print("---> Found characteristic: ");
                Serial.println(characteristic->getUUID().toString().c_str());
            }

            if (characteristic->canRead()) {
                if (remoteDebug) {
                    Serial.println("-----> Can read...");
                }
                std::vector<uint8_t> value = characteristic->readValue();
                if (remoteDebug) {
                    Serial.print("Value: ");
                    for (auto byte : value) {
                        Serial.print(byte, HEX);
                        Serial.print(" ");
                    }
                    Serial.println();
                }
            }

            if (characteristic->canNotify() || characteristic->canIndicate()) {
                if (remoteDebug) {
                    Serial.println("-----> Can notify/indicate. Subscribing...");
                }
                if (!characteristic->subscribe(true, buttonPressCallback, true)) {
                    if (remoteDebug) {
                        Serial.println("subscribe notification failed");
                    }
                    pClient->disconnect();
                    return false;
                }
                if (remoteDebug) {
                    Serial.println("Subscribed successfully");
                }
            }
        }
    } 
    // Check for Battery Service
    else if (serviceUUID.equals(batteryServiceUUID)) {
        if (remoteDebug) {
            Serial.println("-> Found Battery Service");
        }
        auto characteristics = service->getCharacteristics(true);
        for (auto characteristic : *characteristics) {
            if (characteristic->getUUID().equals(batteryLevelCharUUID)) {
                if (remoteDebug) {
                    Serial.println("---> Found Battery Level Characteristic");
                }

                // Read battery level if characteristic is readable
                if (characteristic->canRead()) {
                    if (remoteDebug) {
                        Serial.println("-----> Reading Battery Level...");
                    }
                    std::vector<uint8_t> value = characteristic->readValue();
                    if (remoteDebug) {
                        Serial.print("Battery Level: ");
                        Serial.println((int)value[0]); // Assuming battery level is the first byte
                    }
                }

                // Subscribe to battery level notifications
                if (characteristic->canNotify()) {
                    if (remoteDebug) {
                        Serial.println("-----> Subscribing to Battery Level Notifications...");
                    }
                    characteristic->subscribe(true, buttonPressCallback, true);
                }
            }
        }
    }
}

char timeString[4];
float seconds = (millis() - startTime) / 1000.0;
snprintf(timeString, sizeof(timeString), "%3.2f", seconds);
if (remoteDebug) {
    Serial.print("completed connection after ");
    Serial.println(timeString);
}
return true;

}

void initRemoteClient(bool initDevice) { if (initDevice) { NimBLEDevice::init(""); NimBLEDevice::setPower(CLIENT_POWER); NimBLEDevice::setSecurityAuth(true, true, true); }

NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT); //use numeric comparison

/** create new scan */
NimBLEScan* pScan = NimBLEDevice::getScan();

/** create a callback that gets called when advertisers are found */
pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
pScan->setInterval(45);
pScan->setWindow(15);
pScan->setActiveScan(true);
pScan->start(scanTime, scanEndedCB);

}

void remoteTick() { if (doConnect) { doConnect = false; if (!connectToServer()) { if (remoteDebug) { Serial.println("failed to connect, starting scan"); } //NimBLEDevice::getScan()->start(scanTime,scanEndedCB); } } }

Tschmidt9490 commented 1 week ago

Now i have got it to reconnect. I had a variable declared at bool and not static bool which somehow made the difference. Yay.

Now i am keen to find a faster reconnect. It seems to need to rediscover all characteristics and resubscribe after a reboot. Is this needed? Should it save the device information for the next reconnect?

h2zero commented 1 week ago

Great!

Yes, when reconnecting you should set the deleteAttributes parameter to false so you won't need to discover them again.

Tschmidt9490 commented 1 week ago

the one you are referring to is this one right?

    if (!pClient->connect(advertisedDevice,false)) {
        /** Created a client but failed to connect, don't need to keep it as it has no data */
        NimBLEDevice::deleteClient(pClient);
        Serial.println("Failed to connect, deleted client");
        resumeOtherTasks();
        return false;
    }
Tschmidt9490 commented 1 week ago

here is the whole function just for reference

bool connectToServer() { suspendOtherTasks(); lastUserActivityTime = millis(); lv_label_set_text(ui_Label10, "Connecting remote"); lv_obj_set_style_text_font(ui_Label10, &lv_font_montserrat_28, LV_PART_MAIN | LV_STATE_DEFAULT); int startTime = millis(); NimBLEClient *pClient = nullptr;

if (NimBLEDevice::getClientListSize()) {
    pClient = NimBLEDevice::getClientByPeerAddress(advertisedDevice->getAddress());
    if (pClient) {
        if (!pClient->connect(advertisedDevice, false)) {
            if (remoteDebug) {Serial.println("reconnect failed");}
            resumeOtherTasks();
            return false;
        }
        if (remoteDebug) {Serial.println("reconnected client");}
    } else { pClient = NimBLEDevice::getDisconnectedClient(); }
}

/** No client to reuse? Create a new one. */
if(!pClient) {
    if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
        Serial.println("Max clients reached - no more connections available");
        resumeOtherTasks();
        return false;
    }

    pClient = NimBLEDevice::createClient();

    Serial.println("New client created");

    pClient->setClientCallbacks(&clientCB, false);
    /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
     *  These settings are safe for 3 clients to connect reliably, can go faster if you have less
     *  connections. Timeout should be a multiple of the interval, minimum is 100ms.
     *  Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout
     */
    pClient->setConnectionParams(6,12,0,100);
    /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
    pClient->setConnectTimeout(10);

    if (!pClient->connect(advertisedDevice,false)) {
        /** Created a client but failed to connect, don't need to keep it as it has no data */
        NimBLEDevice::deleteClient(pClient);
        Serial.println("Failed to connect, deleted client");
        resumeOtherTasks();
        return false;
    }
}

if (!pClient->isConnected()) {
    if (!pClient->connect(advertisedDevice)) {
        if (remoteDebug) {
            Serial.println("failed to connect");
        }
        resumeOtherTasks();
        return false;
    }
}

// Use the discovered services and characteristics
auto services = pClient->getServices(true);
static NimBLEUUID hidServiceUUID(HID_SERVICE);
for (auto service : *services) {
    suspendOtherTasks();
    auto serviceUUID = service->getUUID();
    if (serviceUUID.equals(hidServiceUUID)) {
        if (remoteDebug) {
            Serial.println("-> Found HID service");
        }

        auto characteristics = service->getCharacteristics(true);
        for (auto characteristic : *characteristics) {
            suspendOtherTasks();
            if (remoteDebug) {
                Serial.print("---> Found characteristic: ");
                Serial.println(characteristic->getUUID().toString().c_str());
            }

            if (characteristic->canRead()) {
                if (remoteDebug) {
                    Serial.println("-----> Can read...");
                }
                std::vector<uint8_t> value = characteristic->readValue();
                if (remoteDebug) {
                    Serial.print("Value: ");
                    for (auto byte : value) {
                        Serial.print(byte, HEX);
                        Serial.print(" ");
                    }
                    Serial.println();
                }
            }

            if (characteristic->canNotify() || characteristic->canIndicate()) {
                if (remoteDebug) {
                    Serial.println("-----> Can notify/indicate. Subscribing...");
                }
                if (!characteristic->subscribe(true, buttonPressCallback, true)) {
                    if (remoteDebug) {
                        Serial.println("subscribe notification failed");
                    }
                    pClient->disconnect();
                    resumeOtherTasks();
                    return false;
                }
                if (remoteDebug) {
                    Serial.println("Subscribed successfully");
                }
            }
            resumeOtherTasks();
        }
    } 
    // Check for Battery Service
    else if (serviceUUID.equals(batteryServiceUUID)) {
        if (remoteDebug) {
            Serial.println("-> Found Battery Service");
        }
        auto characteristics = service->getCharacteristics(true);
        for (auto characteristic : *characteristics) {
            suspendOtherTasks();
            if (characteristic->getUUID().equals(batteryLevelCharUUID)) {
                if (remoteDebug) {
                    Serial.println("---> Found Battery Level Characteristic");
                }

                // Read battery level if characteristic is readable
                if (characteristic->canRead()) {
                    if (remoteDebug) {
                        Serial.println("-----> Reading Battery Level...");
                    }
                    std::vector<uint8_t> value = characteristic->readValue();
                    if (remoteDebug) {
                        Serial.print("Battery Level: ");
                        Serial.println((int)value[0]); // Assuming battery level is the first byte
                    }
                }

                // Subscribe to battery level notifications
                if (characteristic->canNotify()) {
                    if (remoteDebug) {
                        Serial.println("-----> Subscribing to Battery Level Notifications...");
                    }
                    characteristic->subscribe(true, buttonPressCallback, true);
                }
            }
            resumeOtherTasks();
        }
    }
    resumeOtherTasks();
}

char timeString[4];
float seconds = (millis() - startTime) / 1000.0;
snprintf(timeString, sizeof(timeString), "%3.2f", seconds);
if (remoteDebug) {
    Serial.print("completed connection after ");
    Serial.println(timeString);
}
resumeOtherTasks();
return true;

}

Tschmidt9490 commented 1 week ago

i can also see that this

NimBLEDevice::getClientListSize()

Returns a 0 so it does not seem to be stored

Tschmidt9490 commented 6 days ago

i can see that after a successful connection is made with the connect to server that a bond if formed and the ClientListSize is increased by 1. The data is not persistent between reboots though

Tschmidt9490 commented 6 days ago

By pulling the battery of the remote and inserting again and not rebooting i can reconnect. But the code performs a full discovery again

h2zero commented 5 days ago

The attributes are not stored in flash, only bond info is if enabled, so you would need to do discovery after reboot.