NimBLE-Arduino icon indicating copy to clipboard operation
NimBLE-Arduino copied to clipboard

Advice: Using example Client and connecting gamepad

Open nevsie opened this issue 1 month ago • 10 comments

Hi All, I am still pretty fresh into Nimble and this library - so please forgive my ignorance. Currently, I have an ESP32 CS Supermini up and running, scanning, connecting, and outputting gamepad data (characteristics). Great.

But I then hit a stumbling block - when the gamepad disconnects and I try to reconnect. The script identifies there are clients in the list, matches it, gets disconnected client, and then fails to reconnect. I have also noticed that when I try to reconnect, the serial dump of the advertised device has all zeros for the UUID

Any help / advice is really appreciated. N

23:12:19.050 -> I NimBLEScan: Scan response from: 7a:52:7b:78:a5:3e
23:12:22.498 -> I NimBLEScan: New advertiser: 00:00:00:00:00:00
23:12:22.498 -> D NimBLEScanCallbacks: Discovered: Name: Q37XDV for Android, Address: 00:00:00:00:00:00, appearance: 963, serviceUUID: 0x1812
23:12:22.543 -> I NimBLEScan: Scan response from: 00:00:00:00:00:00
23:12:22.543 -> Suitable Advertised Device found: Name: Q37XDV for Android, Address: 00:00:00:00:00:00, appearance: 963, manufacturer data: 4f54415f424d37363901007500fb4419b64c0a333143, serviceUUID: 0x1812
23:12:22.543 -> Found Our Service
23:12:22.543 -> D NimBLEScan: >> stop()
23:12:22.543 -> D NimBLEScan: << stop()
23:12:22.543 -> Client List Size: 1
23:12:22.543 -> Client match in list.
23:12:22.543 -> Get Disconnected Client.
23:12:22.543 -> D NimBLEClient: >> connect(00:00:00:00:00:00)
23:12:22.543 -> E NimBLEClient: Invalid peer address; (NULL)
23:12:22.543 -> Failed to connect
23:12:22.543 -> Failed to connect, starting scan

nevsie avatar Oct 28 '25 23:10 nevsie

Additionally... When using a different controller (which has multiple connection methods), I have tried connecting as a simple joystick, and as a HID device. The HID device (i.e. 10 bytes in callback, two joysticks data, etc) suffers the reconnect issue above. Yet the simple joystick (4 bytes in callback, single joystick basic buttons only), reconnects without issue.

23:35:12.202 -> I NimBLEScan: Scan response from: 54:7f:16:ae:3c:9c
23:35:13.670 -> I NimBLEScan: New advertiser: 00:00:00:00:00:00
23:35:13.670 -> D NimBLEScanCallbacks: Discovered: Name: GamePadPlus V3, Address: 00:00:00:00:00:00, appearance: 963, serviceUUID: 0x1812
23:35:13.670 -> I NimBLEScan: Scan response from: 00:00:00:00:00:00
23:35:13.715 -> Suitable Advertised Device found: Name: GamePadPlus V3, Address: 00:00:00:00:00:00, appearance: 963, manufacturer data: 4f54415f424d3736380b000c0023401400250331333400000000000001, serviceUUID: 0x1812
23:35:13.715 -> Found Our Service
23:35:13.715 -> D NimBLEScan: >> stop()
23:35:13.715 -> D NimBLEScan: << stop()
23:35:13.715 -> Client List Size: 1
23:35:13.715 -> Client match in list.
23:35:13.715 -> Get Disconnected Client.
23:35:13.715 -> D NimBLEClient: >> connect(00:00:00:00:00:00)
23:35:13.715 -> E NimBLEClient: Invalid peer address; (NULL)
23:35:13.715 -> Failed to connect
23:35:13.715 -> Failed to connect, starting scan
23:35:14.683 -> D NimBLEScan: >> start: duration=10000
23:35:14.716 -> D NimBLEScan: Scan started
23:35:14.716 -> D NimBLEScan: << start()
23:39:58.557 -> I NimBLEScan: Scan response from: 73:f4:5f:8f:a5:d6
23:39:58.929 -> I NimBLEScan: New advertiser: 02:25:00:14:40:23
23:39:58.929 -> D NimBLEScanCallbacks: Discovered: Name: GamePadPlus V3, Address: 02:25:00:14:40:23, appearance: 963, serviceUUID: 0x1812
23:39:59.011 -> I NimBLEScan: Scan response from: 02:25:00:14:40:23
23:39:59.011 -> Suitable Advertised Device found: Name: GamePadPlus V3, Address: 02:25:00:14:40:23, appearance: 963, manufacturer data: 4f54415f424d3736380b000c0023401400250231333400000000000008, serviceUUID: 0x1812
23:39:59.011 -> Found Our Service
23:39:59.011 ->  NimBLEScan: >> stop()
23:39:59.011 -> D NimBLEScan: << stop()
23:39:59.011 -> Client List Size: 1
23:39:59.011 -> Client match in list.
23:39:59.011 -> Get Disconnected Client.
23:39:59.011 -> D NimBLEClient: >> connect(02:25:00:14:40:23)
23:39:59.011 -> D NimBLERemoteCharacteristic: >> deleteDescriptors
23:39:59.053 -> D NimBLERemoteCharacteristic: << deleteDescriptors
23:39:59.053 -> D NimBLERemoteCharacteristic: >> deleteDescriptors
23:39:59.053 -> D NimBLERemoteCharacteristic: << deleteDescriptors
23:39:59.053 -> D NimBLERemoteCharacteristic: >> deleteDescriptors
23:39:59.053 -> D NimBLERemoteCharacteristic: << deleteDescriptors
23:39:59.140 -> D NimBLEClient: >> handleGapEvent 
23:39:59.140 -> D NimBLEClient: >> handleGapEvent 
23:39:59.140 -> D NimBLEClient: >> handleGapEvent 
23:39:59.140 -> I NimBLEClient: mtu update: mtu=196
23:39:59.140 -> D NimBLEClientCallbacks: onMTUChange: default
23:39:59.176 -> D NimBLEClient: << handleGapEvent
23:39:59.176 -> D NimBLEClient: exchangeMTUCb: status=0, mtu=196
23:39:59.176 -> Connected
23:39:59.176 -> D NimBLEClient: << connect()
23:39:59.176 -> Connected to: 02:25:00:14:40:23 RSSI: -38

nevsie avatar Oct 28 '25 23:10 nevsie

This is really strange, can you share your code?

h2zero avatar Oct 29 '25 00:10 h2zero

Hi, And thanks for your response. I wanted to try and eliminate as much as possible, so wanted to go back to your original code (NimBLE_Client.ino) version and update that for my services, etc. Code as below: The only changes from the original, specify service 1812, and then characteristic 2A4B and 2A4D. Enabling security: NimBLEDevice::setSecurityAuth(true, false, false); And finally I removed the writes. Thanks, N

/** NimBLE_Client Demo:
 *
 *  Demonstrates many of the available features of the NimBLE client library.
 *
 *  Created: on March 24 2020
 *      Author: H2zero
 */

#include <Arduino.h>
#include <NimBLEDevice.h>

static const NimBLEAdvertisedDevice* advDevice;
static bool                          doConnect  = false;
static uint32_t                      scanTimeMs = 5000; /** scan time in milliseconds, 0 = scan forever */

/**  None of these are required as they will be handled by the library with defaults. **
 **                       Remove as you see fit for your needs                        */
class ClientCallbacks : public NimBLEClientCallbacks {
    void onConnect(NimBLEClient* pClient) override { Serial.printf("Connected\n"); }

    void onDisconnect(NimBLEClient* pClient, int reason) override {
        Serial.printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
        NimBLEDevice::getScan()->start(scanTimeMs, false, true);
    }

    /********************* Security handled here *********************/
    void onPassKeyEntry(NimBLEConnInfo& connInfo) override {
        Serial.printf("Server Passkey Entry\n");
        /**
         * This should prompt the user to enter the passkey displayed
         * on the peer device.
         */
        NimBLEDevice::injectPassKey(connInfo, 123456);
    }

    void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key) override {
        Serial.printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
        /** Inject false if passkeys don't match. */
        NimBLEDevice::injectConfirmPasskey(connInfo, true);
    }

    /** Pairing process complete, we can check the results in connInfo */
    void onAuthenticationComplete(NimBLEConnInfo& connInfo) override {
        if (!connInfo.isEncrypted()) {
            Serial.printf("Encrypt connection failed - disconnecting\n");
            /** Find the client with the connection handle provided in connInfo */
            NimBLEDevice::getClientByHandle(connInfo.getConnHandle())->disconnect();
            return;
        }
    }
} clientCallbacks;

/** Define a class to handle the callbacks when scan events are received */
class ScanCallbacks : public NimBLEScanCallbacks {
    void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
        Serial.printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
        if (advertisedDevice->isAdvertisingService(NimBLEUUID("1812"))) {
            Serial.printf("Found Our Service\n");
            /** stop scan before connecting */
            NimBLEDevice::getScan()->stop();
            /** Save the device reference in a global for the client to use*/
            advDevice = advertisedDevice;
            /** Ready to connect now */
            doConnect = true;
        }
    }

    /** Callback to process the results of the completed scan or restart it */
    void onScanEnd(const NimBLEScanResults& results, int reason) override {
        Serial.printf("Scan Ended, reason: %d, device count: %d; Restarting scan\n", reason, results.getCount());
        NimBLEDevice::getScan()->start(scanTimeMs, false, true);
    }
} scanCallbacks;

/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
    std::string str  = (isNotify == true) ? "Notification" : "Indication";
    str             += " from ";
    str             += pRemoteCharacteristic->getClient()->getPeerAddress().toString();
    str             += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
    str             += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
    str             += ", Value = " + std::string((char*)pData, length);
    Serial.printf("%s\n", str.c_str());
}

/** Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
    NimBLEClient* pClient = nullptr;

    /** Check if we have a client we should reuse first **/
    if (NimBLEDevice::getCreatedClientCount()) {
        /**
         *  Special case when we already know this device, we send false as the
         *  second argument in connect() to prevent refreshing the service database.
         *  This saves considerable time and power.
         */
        pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress());
        if (pClient) {
            if (!pClient->connect(advDevice, false)) {
                Serial.printf("Reconnect failed\n");
                return false;
            }
            Serial.printf("Reconnected client\n");
        } else {
            /**
             *  We don't already have a client that knows this device,
             *  check for a client that is disconnected that we can use.
             */
            pClient = NimBLEDevice::getDisconnectedClient();
        }
    }

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

        pClient = NimBLEDevice::createClient();

        Serial.printf("New client created\n");

        pClient->setClientCallbacks(&clientCallbacks, false);
        /**
         *  Set initial connection parameters:
         *  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, 150 * 10ms = 1500ms timeout
         */
        pClient->setConnectionParams(12, 12, 0, 150);

        /** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */
        pClient->setConnectTimeout(5 * 1000);

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

    if (!pClient->isConnected()) {
        if (!pClient->connect(advDevice)) {
            Serial.printf("Failed to connect\n");
            return false;
        }
    }

    Serial.printf("Connected to: %s RSSI: %d\n", pClient->getPeerAddress().toString().c_str(), pClient->getRssi());

    /** Now we can read/write/subscribe the characteristics of the services we are interested in */
    NimBLERemoteService*        pSvc = nullptr;
    NimBLERemoteCharacteristic* pChr = nullptr;
    NimBLERemoteDescriptor*     pDsc = nullptr;

    pSvc = pClient->getService("1812");
    if (pSvc) {
        pChr = pSvc->getCharacteristic("2A4B");
    }

    if (pChr) {
        if (pChr->canRead()) {
            Serial.printf("%s Value: %s\n", pChr->getUUID().toString().c_str(), pChr->readValue().c_str());
        }

        if (pChr->canNotify()) {
            if (!pChr->subscribe(true, notifyCB)) {
                pClient->disconnect();
                return false;
            }
        } else if (pChr->canIndicate()) {
            /** Send false as first argument to subscribe to indications instead of notifications */
            if (!pChr->subscribe(false, notifyCB)) {
                pClient->disconnect();
                return false;
            }
        }
    } else {
        Serial.printf("DEAD service not found.\n");
    }

    pSvc = pClient->getService("1812");
    if (pSvc) {
        pChr = pSvc->getCharacteristic("2A4D");
        if (pChr) {
            if (pChr->canRead()) {
                Serial.printf("%s Value: %s\n", pChr->getUUID().toString().c_str(), pChr->readValue().c_str());
            }

            pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
            if (pDsc) {
                Serial.printf("Descriptor: %s  Value: %s\n", pDsc->getUUID().toString().c_str(), pDsc->readValue().c_str());
            }

            if (pChr->canNotify()) {
                if (!pChr->subscribe(true, notifyCB)) {
                    pClient->disconnect();
                    return false;
                }
            } else if (pChr->canIndicate()) {
                /** Send false as first argument to subscribe to indications instead of notifications */
                if (!pChr->subscribe(false, notifyCB)) {
                    pClient->disconnect();
                    return false;
                }
            }
        }
    } else {
        Serial.printf("BAAD service not found.\n");
    }

    Serial.printf("Done with this device!\n");
    return true;
}

void setup() {
    Serial.begin(115200);
    Serial.printf("Starting NimBLE Client\n");

    /** Initialize NimBLE and set the device name */
    NimBLEDevice::init("NimBLE-Client");

    /**
     * Set the IO capabilities of the device, each option will trigger a different pairing method.
     *  BLE_HS_IO_KEYBOARD_ONLY   - Passkey pairing
     *  BLE_HS_IO_DISPLAY_YESNO   - Numeric comparison pairing
     *  BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
     */
    // NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
    // NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison

    /**
     * 2 different ways to set security - both calls achieve the same result.
     *  no bonding, no man in the middle protection, BLE secure connections.
     *  These are the default values, only shown here for demonstration.
     */
    NimBLEDevice::setSecurityAuth(true, false, false);
    // NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);

    /** Optional: set the transmit power */
    NimBLEDevice::setPower(3); /** 3dbm */
    NimBLEScan* pScan = NimBLEDevice::getScan();

    /** Set the callbacks to call when scan events occur, no duplicates */
    pScan->setScanCallbacks(&scanCallbacks, false);

    /** Set scan interval (how often) and window (how long) in milliseconds */
    pScan->setInterval(100);
    pScan->setWindow(100);

    /**
     * Active scan will gather scan response data from advertisers
     *  but will use more energy from both devices
     */
    pScan->setActiveScan(true);

    /** Start scanning for advertisers */
    pScan->start(scanTimeMs);
    Serial.printf("Scanning for peripherals\n");
}

void loop() {
    /** Loop here until we find a device we want to connect to */
    delay(10);

    if (doConnect) {
        doConnect = false;
        /** Found a device we want to connect to, do it now */
        if (connectToServer()) {
            Serial.printf("Success! we should now be getting notifications, scanning for more!\n");
        } else {
            Serial.printf("Failed to connect, starting scan\n");
        }

        NimBLEDevice::getScan()->start(scanTimeMs, false, true);
    }
}

The Debug output for the initial connection and reconnection are below here:

11:46:01.227 -> I NimBLEScan: New advertiser: 03:25:00:14:40:23
11:46:01.274 -> D NimBLEScanCallbacks: Discovered: Name: GamePadPlus V3, Address: 03:25:00:14:40:23, appearance: 963, serviceUUID: 0x1812
11:46:01.274 -> I NimBLEScan: Scan response from: 03:25:00:14:40:23
11:46:01.274 -> Advertised Device found: Name: GamePadPlus V3, Address: 03:25:00:14:40:23, appearance: 963, manufacturer data: 4f54415f424d3736380b000c0023401400250331333400000000000001, serviceUUID: 0x1812
11:46:01.274 -> Found Our Service
11:46:01.274 -> D NimBLEScan: >> stop()
11:46:01.274 -> D NimBLEScan: << stop()
11:46:01.274 -> New client created
11:46:01.274 -> D NimBLEClient: >> connect(03:25:00:14:40:23)
11:46:01.353 -> D NimBLEClient: >> handleGapEvent 
11:46:01.353 -> D NimBLEClient: >> handleGapEvent 
11:46:01.353 -> D NimBLEClient: >> handleGapEvent 
11:46:01.353 -> I NimBLEClient: mtu update: mtu=196
11:46:01.353 -> D NimBLEClientCallbacks: onMTUChange: default
11:46:01.398 -> D NimBLEClient: << handleGapEvent
11:46:01.398 -> D NimBLEClient: exchangeMTUCb: status=0, mtu=196
11:46:01.398 -> Connected
11:46:01.398 -> D NimBLEClient: << connect()
11:46:01.398 -> Connected to: 03:25:00:14:40:23 RSSI: -56
11:46:01.398 -> D NimBLEClient: >> getService: uuid: 0x1812
11:46:01.398 -> D NimBLEClient: Service Discovered >> status: 0 handle: 19
11:46:01.444 -> D NimBLEClient: Service Discovered >> status: 14 handle: -1
11:46:01.444 -> D NimBLEClient: << Service Discovered
11:46:01.444 -> D NimBLERemoteService: >> getCharacteristic: uuid: 0x2a4b
11:46:01.444 -> D NimBLERemoteService: >> retrieveCharacteristics()
11:46:01.491 -> D NimBLERemoteService: Characteristic Discovery >> status: 0 handle: 30
11:46:01.534 -> D NimBLERemoteService: Characteristic Discovery >> status: 14 handle: -1
11:46:01.534 -> D NimBLERemoteService: << Characteristic Discovery
11:46:01.569 -> D NimBLERemoteService: << retrieveCharacteristics()
11:46:01.569 -> D NimBLERemoteService: << Characteristic found
11:46:01.569 -> D NimBLERemoteValueAttribute: >> readValue()
11:46:01.569 -> I NimBLERemoteValueAttribute: Read complete; status=261
11:46:01.569 -> D NimBLEClient: >> secureConnection()
11:46:01.569 -> D NimBLEDeviceCallbacks: onStoreStatus: default
11:46:01.615 -> D NimBLEDeviceCallbacks: onStoreStatus: default
11:46:01.799 -> D NimBLEClient: >> handleGapEvent 
11:46:01.799 -> D NimBLEDeviceCallbacks: onStoreStatus: default
11:46:01.799 -> D NimBLEClient: >> handleGapEvent 
11:46:01.799 -> D NimBLEClient: << handleGapEvent
11:46:01.845 -> D NimBLEClient: << secureConnection: success
11:46:01.882 -> I NimBLERemoteValueAttribute: Read complete; status=0
11:46:01.882 -> D NimBLERemoteValueAttribute: Got 122 bytes
11:46:01.882 -> I NimBLERemoteValueAttribute: Read complete; status=14
11:46:01.882 -> D NimBLERemoteValueAttribute: << readValue
11:46:01.882 -> 0x2a4b Value: 	��u�&�*��
11:46:01.882 -> D NimBLEClient: >> getService: uuid: 0x1812
11:46:01.882 -> D NimBLEClient: << getService: found the service with uuid: 0x1812
11:46:01.923 -> D NimBLERemoteService: >> getCharacteristic: uuid: 0x2a4d
11:46:01.923 -> D NimBLERemoteService: >> retrieveCharacteristics()
11:46:01.923 -> D NimBLERemoteService: Characteristic Discovery >> status: 0 handle: 22
11:46:01.969 -> D NimBLERemoteService: Characteristic Discovery >> status: 0 handle: 26
11:46:02.002 -> D NimBLERemoteService: Characteristic Discovery >> status: 14 handle: -1
11:46:02.002 -> D NimBLERemoteService: << Characteristic Discovery
11:46:02.002 -> D NimBLERemoteService: << retrieveCharacteristics()
11:46:02.034 -> D NimBLERemoteService: << Characteristic found
11:46:02.034 -> D NimBLERemoteValueAttribute: >> readValue()
11:46:02.034 -> I NimBLERemoteValueAttribute: Read complete; status=0
11:46:02.034 -> D NimBLERemoteValueAttribute: Got 10 bytes
11:46:02.034 -> I NimBLERemoteValueAttribute: Read complete; status=14
11:46:02.034 -> D NimBLERemoteValueAttribute: << readValue
11:46:02.034 -> 0x2a4d Value: 
11:46:02.073 -> D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0xc01d
11:46:02.073 -> D NimBLERemoteCharacteristic: >> retrieveDescriptors() for characteristic: 0x2a4d
11:46:02.073 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 28
11:46:02.108 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 29
11:46:02.108 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 30
11:46:02.108 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 31
11:46:02.153 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 32
11:46:02.153 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 33
11:46:02.191 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 34
11:46:02.191 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 35
11:46:02.191 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 14 handle: -1
11:46:02.191 -> D NimBLERemoteCharacteristic: << Descriptor Discovery
11:46:02.191 -> D NimBLERemoteCharacteristic: << retrieveDescriptors(): found 0 descriptors.
11:46:02.191 -> D NimBLERemoteCharacteristic: >> retrieveDescriptors() for characteristic: 0x2a4d
11:46:02.232 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 28
11:46:02.232 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 29
11:46:02.269 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 30
11:46:02.269 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 31
11:46:02.269 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 32
11:46:02.307 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 33
11:46:02.307 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 34
11:46:02.307 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 35
11:46:02.307 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 14 handle: -1
11:46:02.339 -> D NimBLERemoteCharacteristic: << Descriptor Discovery
11:46:02.339 -> D NimBLERemoteCharacteristic: << retrieveDescriptors(): found 0 descriptors.
11:46:02.339 -> D NimBLERemoteCharacteristic: << getDescriptor: not found
11:46:02.339 -> D NimBLERemoteCharacteristic: >> setNotify()
11:46:02.339 -> D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0x2902
11:46:02.374 -> D NimBLERemoteCharacteristic: >> retrieveDescriptors() for characteristic: 0x2a4d
11:46:02.374 -> D NimBLERemoteCharacteristic: Descriptor Discovery >> status: 0 handle: 28
11:46:02.374 -> D NimBLERemoteCharacteristic: << Descriptor Discovery
11:46:02.374 -> D NimBLERemoteCharacteristic: << retrieveDescriptors(): found 1 descriptors.
11:46:02.374 -> D NimBLERemoteCharacteristic: << getDescriptor: found
11:46:02.374 -> D NimBLERemoteCharacteristic: << setNotify()
11:46:02.407 -> D NimBLERemoteValueAttribute: >> writeValue()
11:46:02.407 -> I NimBLERemoteValueAttribute: Write complete; status=0
11:46:02.407 -> D NimBLERemoteValueAttribute: << writeValue
11:46:02.407 -> Done with this device!
11:46:02.407 -> Success! we should now be getting notifications, scanning for more!
11:46:16.829 -> I NimBLEScan: New advertiser: 00:00:00:00:00:00
11:46:16.829 -> D NimBLEScanCallbacks: Discovered: Name: GamePadPlus V3, Address: 00:00:00:00:00:00, appearance: 963, serviceUUID: 0x1812
11:46:16.871 -> I NimBLEScan: Scan response from: 5a:95:e5:f8:53:79
11:46:16.871 -> Advertised Device found: Name: , Address: 5a:95:e5:f8:53:79
11:46:16.871 -> Service Data:
11:46:16.871 -> UUID: 0xfeaa, Data: @+��~`��ߏ�+�[qv��
11:46:16.871 -> I NimBLEScan: Scan response from: 00:00:00:00:00:00
11:46:16.904 -> Advertised Device found: Name: GamePadPlus V3, Address: 00:00:00:00:00:00, appearance: 963, manufacturer data: 4f54415f424d3736380b000c0023401400250331333400000000000001, serviceUUID: 0x1812
11:46:16.904 -> Found Our Service
11:46:16.904 -> D NimBLEScan: >> stop()
11:46:16.904 -> D NimBLEScan: << stop()
11:46:16.904 -> D NimBLEClient: >> connect(00:00:00:00:00:00)
11:46:16.904 -> E NimBLEClient: Invalid peer address; (NULL)
11:46:16.904 -> Failed to connect
11:46:16.904 -> Failed to connect, starting scan
11:46:16.904 -> D NimBLEScan: >> start: duration=5000

nevsie avatar Oct 29 '25 11:10 nevsie

Thanks, I think this is related to an upstream bug, can you test this with an esp32s3 or c3? Otherwise, if you disable bonding do the addresses get corrected?

h2zero avatar Oct 29 '25 13:10 h2zero

This is currently using a ESP32 C3 Supermini. Annoyingly I do not have the S3 variant to hand. - I will order some. I tested on a ESP32 D1 Mini WROOM module, and this worked fine. So I suspect you are right about it being specific to this device.

Considering the boards age, I cannot imagine a fix will be coming any time soon. Do you have any ideas or thoughts for a work around? In the case of all zeros, delete the client it thought it was matched to and then connect fresh?

For now I can work on the WROOM board, and await the S3 options. Thanks, N

nevsie avatar Oct 29 '25 14:10 nevsie

Not much that I can suggest other than changing the hardware and seeing if the problem follows.

h2zero avatar Oct 29 '25 14:10 h2zero

Annoyingly I am tied the C3 Supermini, as it is being used to fit a custom motor driver board. And size constraints are quite limiting. I agree the problem is not with the Nimble library, I had just hoped I could work around it somehow - I'll see what I can work out. Thanks for the help.

nevsie avatar Oct 29 '25 14:10 nevsie

It could be an issue with that particular unit, if you have another to try with I suggest testing that.

h2zero avatar Oct 29 '25 15:10 h2zero

In all honesty, I am pulling my hair out with inconsistencies on this!!! I check across multiple esp32 devices, and then reload the programme and run the tests again, and i get different results. Across both the D1 and ESP32 C3. Sometimes they all work, sometimes a few. sometimes the third controller prevents others following it from connecting, other times it does not. At this point - I will flag that i think the Third controller may have an issue. I think on one test run, it wrote "Tasty" to the report map!!! Which now means the controller report map is blank. However, this does not change the randomness of it connect sometimes and not others. Likewise it sometimes prevents those following it and sometimes it does not.

And this is not being critical - but all controllers work first time on default BluePad32, zero issues connecting and reconnecting. So I know they can work consistently on ESP32 C3's etc. I need to find out what and how they are doing things to see a difference. But I want a lighter setup, and with specific controls that are painful on Bluepad32 - hence wanting nimble to work.

On the ESP32 D1 MINI

If I set security up as: NimBLEDevice::setSecurityAuth(true, true, true); One gamepad I can connect, disconnect, reconnect without issue. A second different game pad - works first time, but does not reconnect. A third controller will not connect at all. A fourth will not connect at all. Going back to the first that connected and reconnected before - no longer works.

Remove the third controller again. First works fine, second connects, disconnects, on reconnect caused the task watchdog to poop and restart, then connected! Third connects, disconnect, reconnects.


Change this to: NimBLEDevice::setSecurityAuth(false, true, true); First controller connects, disconnects, reconnects. Second controller connects disconnects and reconnects. Third controller nothing, fourth controller nothing. Back to first, and nothing again.

If I remove the third controller from the mix... First, Second and Fourth all work fine.

I reupload script. run again. First works all Second no longer works Third works all fourth nothing.

On a ESP32 C3 Supermini Plus

Change this to: NimBLEDevice::setSecurityAuth(true, true, true); First connects, but cannot reconnect - I get all zero mac code Second nothing, all zeros Third Connects, cannot reconnect, all zeros fourth Connects but cannot reconnect, all zeros.


Change this to: NimBLEDevice::setSecurityAuth(false, true, true); first connects and reconnects no issue second connect and reconnects Third cannot connect at all - all zeros fourth cannot connect - all zeros

reconnect first, all works reconnect second, all works third and fourth still do not work.

On a ESP32 C3 Supermini

Change this to: NimBLEDevice::setSecurityAuth(true, true, true); first connects, but cannot reconnect, all zeros Second connects, cannot reconnect, all zeros Third cannot connect at all - all zeros fourth connects, cannot reconnect - all zeros


Change this to: NimBLEDevice::setSecurityAuth(false, true, true); first connects and reconnects no issue Second cannot connect at all - all zeros third connects and reconnects no issue Fourth cannot connect at all - all zeros

nevsie avatar Oct 29 '25 23:10 nevsie

Wish I had any suggestion but I would need to be able to reproduce this and debug at a low level.

h2zero avatar Oct 30 '25 21:10 h2zero