BLE Pairing not possible with Nuki 2.0

Thanks again Felix.
Now I get answer from Nuki. The key point was the way to read pData value (using your “debug_dump_binary())” is ok.
I can now go further to the next steps, thks to you.
I’ll keep you in touch.

It worked fine for me on my LolinD32…do you have the code for next steps? Check lock status and open/close?

Hi,

I have started writing an arduino library for use with an ESP32 to connect/configure a Nuki smart lock 2.0 via BLE.
I intend to make this open source on GIT once it is working, for others to use and improve…

I have started with the BLE API (which looks really well documented btw) but I allready get stuck at the first part connecting.

I wrote my own code but as it is not working I was looking for a fix and came across the code in this post:

It seems I get a timeout on the first step; connect BLE to Nuki (pClient->connect(myNukiAddr);).
This happens after 30 seconds, which I suspect is initiated by the Nuki as I see the same happening in NRFconnect.

It looks a bit like the ESP32 tries to establish a connection but the Nuki does not respond…? And the ESP BLE connect needs to finish before you can start registering on chars and sending the messages…

Any help is appreciated.

This is the code I used (as allready present in this topic, only changed the BLE address)

/**
 * A BLE client example that is rich in capabilities.
 * There is a lot new capabilities implemented.
 * author unknown
 * updated by chegewara
 */

#include "BLEDevice.h"
#include "esp_log.h"
#include "Arduino.h"
//#include "BLEScan.h"

// The remote service we wish to connect to.
static BLEUUID serviceUUID("a92ee100-5501-11e4-916c-0800200c9a66");//Pairing service

// The characteristic of the remote service we are interested in.
static BLEUUID    charUUID("a92ee101-5501-11e4-916c-0800200c9a66");//Characteristic of pairing service
//std::vector<uint8_t> response(200);
static boolean connected = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
uint8_t arrayFV[] = {0x01,0x00,0x03,0x00,0x27,0xA7}; //"0x0100030027A7";//first value to send as an array of byte to initiate Nuki Pairing

void debug_dump_binary(const void* buffer, size_t size)
{

  for(size_t i = 0; i < size; ++i)
  {
if(i > 0 && (i % 8) == 0)
{
  Serial.println("");
}
Serial.printf("%0x ", ((const uint8_t*) buffer)[i]);
  }
}

static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
Serial.print("******************** Notify callback for characteristic ");
Serial.println(pBLERemoteCharacteristic->getUUID().toString().c_str());
Serial.print(" of data length ");
Serial.println(length);
Serial.print("data: ");
//fw: don't print this directly. the value is binary, not a 0-terminated string
//Serial.println((char*)pData);
debug_dump_binary(pData, length);
 //   for(int i= 0;i<length;i++) response.push_back(*pData++);
} // end notifyCallback

class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient* pclient) {
  }

  void onDisconnect(BLEClient* pclient) {
connected = false;  
Serial.println("onDisconnect");
  }
};

bool connectToServer() {
Serial.print("Forming a connection to ");
std::string myNukiAddr="54:d2:72:4F:98:48";

BLEClient*  pClient  = BLEDevice::createClient();
Serial.println(" - Created client");

pClient->setClientCallbacks(new MyClientCallback());

// Connect to the remove BLE Server.
Serial.print(" Essai de connexion à Nuki");
  
pClient->connect(myNukiAddr);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
Serial.println(" - Connected to server");
delay(100);
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
  Serial.print("Failed to find our service UUID: ");
  Serial.println(serviceUUID.toString().c_str());
  pClient->disconnect();
  return false;
}
Serial.println(" - Found our service");

// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
  Serial.print("Failed to find our characteristic UUID: ");
  Serial.println(charUUID.toString().c_str());
  pClient->disconnect();
  return false;
}
Serial.println(" - Found our characteristic");
Serial.print("CanRead : ");
Serial.println(pRemoteCharacteristic->canRead()); 
Serial.print("CanWrite : ");
Serial.println(pRemoteCharacteristic->canWrite()); 
Serial.print("CanNotify : ");
Serial.println(pRemoteCharacteristic->canNotify()); 
Serial.print("CanIndicate : ");
Serial.println(pRemoteCharacteristic->canIndicate()); 
   
connected = true;
Serial.print("connected : ");
Serial.println(connected);
return connected;
}

void setup() {
//esp_log_level_set("*", ESP_LOG_DEBUG);
  Serial.begin(115200);
  Serial.println("Starting Arduino BLE Client application...");
  BLEDevice::init("");

if (connectToServer()) {
  Serial.println("We are now connected to the BLE Server.");
} else {
  Serial.println("We have failed to connect to the server; there is nothin more we will do.");
} 
   //register for indication   
   //fw: don't need this. this is done by registerForNotify()
//const uint8_t indicationOn[] = {0x2,0x0};
//pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)indicationOn,2,true);
pRemoteCharacteristic->registerForNotify(notifyCallback, false); //false = indication, true = notification
delay(100);
   //write Value
if(pRemoteCharacteristic->canWrite()) {
  //fw: and here's your problem. you were sending the first byte only (writeValue(arrayFV, 1)
  pRemoteCharacteristic->writeValue(arrayFV, sizeof(arrayFV), true);
        delay(50);           
  }
 std::string value = pRemoteCharacteristic->readValue();
  Serial.print("The characteristic value was: ");
  Serial.println(value.c_str());
} // End of setup


// This is the Arduino main loop function.
void loop() {
  //fw: I don't think you should loop here. loop() is looping anyways
  Serial.println("debut loop...");
  if (connected) {
Serial.println("Still connected, loop");
   // Read the value of the characteristic.
 std::string value = pRemoteCharacteristic->readValue();
  Serial.print("The characteristic value was: ");
  Serial.println(value.c_str());

  delay(1000); // Delay between loops.
}
else { 
 Serial.println("not connected");
}

} // End of loop

This is the output where you see I get a onDisconnect:

Starting Arduino BLE Client application...
Forming a connection to  - Created client
 Essai de connexion à Nuki[D][BLEClient.cpp:96] connect(): >> connect(54:d2:72:4f:98:48)
[D][BLEDevice.cpp:593] addPeerDevice(): add conn_id: 0, GATT role: client
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:158] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:158] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
onDisconnect
 - Connected to server
[I][BLEDevice.cpp:604] removePeerDevice(): remove: 0, GATT role client
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown

omg, I found it. bit ashamed to say but I made a typo in the BLE address :disappointed:
note to self: look for the most obvious solution/cause when having an issue

1 Like

I wrote a Nuki Fob based on the NRF51822. I my code I do not register for indications and it’s still able to pair and perform lock/unlock actions on a V1 Nuki. Apparently, I still receive indications despite not writing to the CCCD. Now users report they can’t pair with their V2 Nuki. Can you confirm that enabling indications is necessary for the V2 Nuki, but not the V1?