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
714 stars 150 forks source link

When device is paired on reconnect of the device not working #464

Closed JuanJoseMoralesCalvo closed 5 months ago

JuanJoseMoralesCalvo commented 2 years ago

Hi im trying to create an HID Gamepad so when i first pair the device all works great but when i (for example) reset the module it doesnt work. Any suggestion on the connect CallBack?

CybershoesVR commented 2 years ago

same here. On PC reconnect works, but on Quest2 does not work. I also use the ESP32-BLE-Gamepad library https://github.com/lemmingDev/ESP32-BLE-Gamepad It did work in summer, but now, not anymore.

h2zero commented 2 years ago

Can either of you share a debug log output when this occurs?

JuanJoseMoralesCalvo commented 2 years ago

The workaround for me was to add security to the BLE, but honestly i dont know the reason :D

Attach to setup() after advertising:

NimBLESecurity *SECURITY_MANDATORY = new BLESecurity(); SECURITY_MANDATORY->setAuthenticationMode(ESP_LE_AUTH_BOND);

CybershoesVR commented 2 years ago

Thank you but this did not work. I tried a few other setting but decided it'll be better to go with debugging as @h2zero suggested.

from the log: GamePadPlus V3 is a BT gamepad device that reconnects successful. Cybershoes2 is our BT gamepad device that does reconnect but gamepad functionality fails upon reconnect.

Dear @h2zero, please can you hint at what makes the difference?

odh_logs_2022-10-18 13.55.53.833.txt

h2zero commented 2 years ago

Looks like you need to enable bonding. Try adding this after initialization: `NimBLEDevice::setSecurityAuth(true, true, true);'

Have a look at the secure client/server examples.

CybershoesVR commented 2 years ago

yes, I did that as you see below. I am using NimBLE 1.38 with ESP32-BLE-Gamepad 0.5.1, because using NimBLE 1.40 makes my ESP32-C crash.

void BleGamepad::taskServer(void *pvParameter)
{
    BleGamepad *BleGamepadInstance = (BleGamepad *)pvParameter; // static_cast<BleGamepad *>(pvParameter);
    NimBLEDevice::init(BleGamepadInstance->deviceName);
    NimBLEServer *pServer = NimBLEDevice::createServer();
    pServer->setCallbacks(BleGamepadInstance->connectionStatus);

    BleGamepadInstance->hid = new NimBLEHIDDevice(pServer);
    BleGamepadInstance->inputGamepad = BleGamepadInstance->hid->inputReport(BleGamepadInstance->configuration.getHidReportId()); // <-- input REPORTID from report map
    BleGamepadInstance->connectionStatus->inputGamepad = BleGamepadInstance->inputGamepad;

    BleGamepadInstance->hid->manufacturer()->setValue(BleGamepadInstance->deviceManufacturer);

    BleGamepadInstance->hid->pnp(0x01, vid, pid, 0x0110);
    BleGamepadInstance->hid->hidInfo(0x00, 0x01);

    NimBLESecurity *SECURITY_MANDATORY = new BLESecurity();
    SECURITY_MANDATORY->setAuthenticationMode(ESP_LE_AUTH_BOND); // ESP_LE_AUTH_REQ_BOND_MITM,
    SECURITY_MANDATORY->setCapability(ESP_IO_CAP_NONE);

    /**
     * @brief Set the authorization mode for this device.
     * @param bonding If true we allow bonding, false no bonding will be performed.
     * @param mitm If true we are capable of man in the middle protection, false if not.
     * @param sc If true we will perform secure connection pairing, false we will use legacy pairing.
     */

    //NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc);
    NimBLEDevice::setSecurityAuth(true, true, true);

    //NimBLEDevice::setSecurityAuth( BLE_SM_PAIR_AUTHREQ_MITM); // BEST must pair again each time but no need to unpair
    //NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_SC);    //same as usual

    uint8_t *customHidReportDescriptor = new uint8_t[hidReportDescriptorSize];
    memcpy(customHidReportDescriptor, tempHidReportDescriptor, hidReportDescriptorSize);

    for (int i = 0; i < hidReportDescriptorSize; i++)
        Serial.printf("%02x", customHidReportDescriptor[i]);

    BleGamepadInstance->hid->reportMap((uint8_t *)customHidReportDescriptor, hidReportDescriptorSize);
    BleGamepadInstance->hid->startServices();

    BleGamepadInstance->onStarted(pServer);

    NimBLEAdvertising *pAdvertising = pServer->getAdvertising();
    pAdvertising->setAppearance(HID_GAMEPAD);
    pAdvertising->addServiceUUID(BleGamepadInstance->hid->hidService()->getUUID());
    pAdvertising->start();
    //https://github.com/h2zero/NimBLE-Arduino/issues/464
    //The workaround for me was to add security to the BLE, but honestly i dont know the reason :D
    //Attach to setup() after advertising:
    //NimBLESecurity *SECURITY_MANDATORY = new BLESecurity();
    //SECURITY_MANDATORY->setAuthenticationMode(ESP_LE_AUTH_BOND);
    //end of inserted code

    BleGamepadInstance->hid->setBatteryLevel(BleGamepadInstance->batteryLevel);

    ESP_LOGD(LOG_TAG, "Advertising started!");
    vTaskDelay(portMAX_DELAY); // delay(portMAX_DELAY);
}

In a variation I also tried without

    NimBLESecurity *SECURITY_MANDATORY = new BLESecurity();
    SECURITY_MANDATORY->setAuthenticationMode(ESP_LE_AUTH_BOND); // ESP_LE_AUTH_REQ_BOND_MITM,
    SECURITY_MANDATORY->setCapability(ESP_IO_CAP_NONE);
CybershoesVR commented 2 years ago

reconnecting our Cybershoes gives an encryption failed:

15:55:28.310  1683  1704 D BluetoothGattServer: onConnectionUpdated() - Device=34:B4:72:00:A1:D6 interval=9 latency=0 timeout=600 status=0
15:55:28.356  1412  2092 W bt_btif       : btif_gatt_set_encryption_cb() - Encryption failed (1)
15:55:28.356  1412  2092 E bt_btif       : bta_hh_security_cmpl() - encryption failed; status=0x000e, reason=0x000a

reconnecting GamePadPlus

15:54:46.249  1683  1704 D BluetoothGattServer: onConnectionUpdated() - Device=20:21:03:07:B9:98 interval=12 latency=0 timeout=200 status=0
15:54:46.331  1412  2092 W bt_stack      : [WARNING:bta_gattc_api.cc(652)] notification already registered
15:54:46.332  1412  2092 I chatty        : uid=1002(bluetooth) bt_main_thread identical 1 line
15:54:46.332  1412  2092 W bt_stack      : [WARNING:bta_gattc_api.cc(652)] notification already registered
15:54:46.332  1412  2092 W bt_btif       : bta_hh_co_open: Found an existing device with the same handle dev_status=2, address=20:21:03:07:b9:98, attr_mask=0x0000, sub_class=0x00, app_id=255
15:54:46.332  1412  1662 W bt_btif       : btif_hh_upstreams_evt: BTA_HH_OPN_EVT: handle=16, status =0
15:54:46.332  1412  1662 W bt_btif       : BTA_HH_OPEN_EVT: Found device...Getting dscp info for handle ... 16
15:54:46.332  1412  1662 I bt_btif_dm    : get_cod remote_cod = 0x00000504
15:54:46.332  1412  1662 I bt_btif_dm    : get_cod remote_cod = 0x00000504
15:54:46.336  1412  1412 D BluetoothAdapterService: getAdapterService() - returning com.android.bluetooth.btservice.AdapterService@590f4fe
15:54:46.336  1412  1662 W bt_btif       : btif_hh_upstreams_evt: name = GamePadPlus V3
15:54:46.336  1412  1662 W bt_btif       : bta_hh_co_send_hid_info: fd = 108, name = [GamePadPlus V3], dscp_len = 187
15:54:46.336  1412  1662 W bt_btif       : bta_hh_co_send_hid_info: vendor_id = 0x1949, product_id = 0x0402, version= 0x0111,ctry_code=0x00
15:54:46.347  1412  1412 D BluetoothAdapterService: isQuetModeEnabled() - Enabled = false

and finally

15:54:46.547  1034  1142 I InputReader   : Device added: id=15, name='GamePadPlus V3', sources=0x01000511
15:54:46.548  2020  2020 D GamepadListener: Adding Device: GamePadPlus V3 pid: 1026 sources: 16778513
15:54:46.549  3026  3026 D GamepadListener: Adding Device: GamePadPlus V3 pid: 1026 sources: 16778513
15:54:46.549  2236  2236 I [OAO] ShellOverlayServiceJNI: NativePTKServiceNotifyDeviceConnectionChanged
15:54:46.549  2236  2236 D [OAO] ShellApplication: PTKService enumerated keyboard: {vendorId: 0x1949 (0n6473), productId: 0x402 (0n1026)}
15:54:46.554  1734  1734 D GamepadListener: Adding Device: GamePadPlus V3 pid: 1026 sources: 16778513
15:54:46.577  2971  2971 D GamepadListener: Adding Device: GamePadPlus V3 pid: 1026 sources: 16778513
CybershoesVR commented 2 years ago

also no success with:

    NimBLEDevice::setSecurityAuth(true, true, true);
    NimBLESecurity *SECURITY_MANDATORY = new BLESecurity();

    /**
     * @brief Set requested authentication mode
     * @param [in] auth_req A bitmask containing one or more of:
     * * ESP_LE_AUTH_NO_BOND              0x00
     * * ESP_LE_AUTH_BOND                 0x01
     * * ESP_LE_AUTH_REQ_MITM             (1 << 2)
     * * ESP_LE_AUTH_REQ_BOND_MITM        (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_MITM)
     * * ESP_LE_AUTH_REQ_SC_ONLY          (1 << 3)
     * * ESP_LE_AUTH_REQ_SC_BOND          (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_SC_ONLY)
     * * ESP_LE_AUTH_REQ_SC_MITM          (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY)
     * * ESP_LE_AUTH_REQ_SC_MITM_BOND     (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY | ESP_LE_AUTH_BOND)
     */
    SECURITY_MANDATORY->setAuthenticationMode(ESP_LE_AUTH_REQ_BOND_MITM); // ESP_LE_AUTH_REQ_BOND_MITM,ESP_LE_AUTH_BOND
    //SECURITY_MANDATORY->setCapability(ESP_IO_CAP_NONE);

    SECURITY_MANDATORY->setInitEncryptionKey(BLE_SM_PAIR_KEY_DIST_ENC);
    SECURITY_MANDATORY->setRespEncryptionKey(BLE_SM_PAIR_KEY_DIST_ENC);
CybershoesVR commented 2 years ago

sometimes (once in 5-10 attempts) connection and reconnection (but then I need to confirm pairing again) works successfully with:

NimBLEDevice::setSecurityAuth(true, true, false);
h2zero commented 2 years ago

I'm not too sure what the error is yet, the logs suggest an error in a value passed during pairing. What kind of pairing does the other device expect?

If not using PIN codes or any other mitm protection then try setting the last 2 parameters of the NimBLEDevice::setSecurityAuth call to false. Otherwise just set the last parameter to false if mitm is required.

h2zero commented 2 years ago

You should also remove this:


NimBLESecurity *SECURITY_MANDATORY = new BLESecurity();

    /**
     * @brief Set requested authentication mode
     * @param [in] auth_req A bitmask containing one or more of:
     * * ESP_LE_AUTH_NO_BOND              0x00
     * * ESP_LE_AUTH_BOND                 0x01
     * * ESP_LE_AUTH_REQ_MITM             (1 << 2)
     * * ESP_LE_AUTH_REQ_BOND_MITM        (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_MITM)
     * * ESP_LE_AUTH_REQ_SC_ONLY          (1 << 3)
     * * ESP_LE_AUTH_REQ_SC_BOND          (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_SC_ONLY)
     * * ESP_LE_AUTH_REQ_SC_MITM          (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY)
     * * ESP_LE_AUTH_REQ_SC_MITM_BOND     (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY | ESP_LE_AUTH_BOND)
     */
    SECURITY_MANDATORY->setAuthenticationMode(ESP_LE_AUTH_REQ_BOND_MITM); // ESP_LE_AUTH_REQ_BOND_MITM,ESP_LE_AUTH_BOND
    //SECURITY_MANDATORY->setCapability(ESP_IO_CAP_NONE);

    SECURITY_MANDATORY->setInitEncryptionKey(BLE_SM_PAIR_KEY_DIST_ENC);
    SECURITY_MANDATORY->setRespEncryptionKey(BLE_SM_PAIR_KEY_DIST_ENC);

That is conflicting with the security settings above it.

JuanJoseMoralesCalvo commented 2 years ago

@CybershoesVR Try this workaround from chewara, it sounds like its happening the same:

class MyCallbacks : public BLEServerCallbacks { void onConnect(BLEServer pServer){ Serial.println("connected"); BLEDescriptor desc = input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902)); uint8_t val[] = {0x01, 0x00}; desc->setValue(val, 2); }

void onDisconnect(BLEServer* pServer){ Serial.println("disconnected"); } };

Issue Open: https://github.com/nkolban/esp32-snippets/issues/632

JuanJoseMoralesCalvo commented 2 years ago

You have to add something like this on the onConenct:

BLEDescriptor *desc = input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902)); uint8_t val[] = {0x01, 0x00}; desc->setValue(val, 2);

fabdelgado commented 2 years ago

I’m facing the same issue.

@JuanJoseMoralesCalvo Did your suggestion work for you?

JuanJoseMoralesCalvo commented 2 years ago

The workaround on comment #12 didnt work as i understand its only for the nkolban esp32 libraries. Finally by adding security it worked.

NimBLESecurity *SECURITY_MANDATORY = new NimBLESecurity(); SECURITY_MANDATORY->setAuthenticationMode(ESP_LE_AUTH_BOND); // ESP_LE_AUTH_REQ_BOND_MITM, SECURITY_MANDATORY->setCapability(ESP_IO_CAP_NONE);

fabdelgado commented 2 years ago

On which lines of which file should I put this?

Please explain me if I should add or replace code?

h2zero commented 2 years ago

I would encourage not to use the security class as it has been deprecated.

The equivalent call is simply:

NimBLEDevice::setSecurityAuth(true, false, false); // enable bonding, no MITM, no SC

The IO caps default to NONE. Just put that line somewhere after initializing NimBLE.

fabdelgado commented 2 years ago

@h2zero like this example in my gist?

https://gist.github.com/fabdelgado/60c715dec2651b46a0d342c46179fe48

h2zero commented 2 years ago

Looks good to me

fabdelgado commented 2 years ago

I will try that!

fabdelgado commented 2 years ago

It has not worked for me with my chromecast v3 Google TV. You can review the video I have recorded for you.

https://user-images.githubusercontent.com/8621547/200147816-289740df-03e3-4b02-9f8e-d16e6ccdcda3.mp4

h2zero commented 2 years ago

Hmm, please try erasing the flash of the esp32 then upload your code. Also remove any devices bonded to the TV if you can.

fabdelgado commented 2 years ago

@h2zero this worked for me, thanks for the help.

https://user-images.githubusercontent.com/8621547/200183433-a0bfaea0-bfe1-4ad7-8a33-ad92f576229e.mp4

This is the code that I have used, I have left it for future issues.

`void setup() {

NimBLEDevice::init("NimBLE");

NimBLEDevice::setSecurityAuth(true, false, false); // enable bonding, no MITM, no SC }`

fabdelgado commented 2 years ago

@h2zero I have another related problem, when I press forget device and then restart the Chromecast it unbinds the remote from Chromecast instead of ESP32. This problem happened to me before the problem of connecting and disconnecting. Do you have any idea?

h2zero commented 2 years ago

I don't have any idea why that would happen, wouldn't have anything to do with the esp32 though.