Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service not discovered in BLE_Client example #269

Open
SensorsIot opened this issue Dec 12, 2017 · 36 comments
Open

Service not discovered in BLE_Client example #269

SensorsIot opened this issue Dec 12, 2017 · 36 comments

Comments

@SensorsIot
Copy link

SensorsIot commented Dec 12, 2017

I use the BLE_notify example without changes and try to connect to it using the BLE_Client example. The only changes I did there was to copy the two UUID strings of the notify example to the client example to be sure they match.
NRF connect shows the service but BLE_Client does not show it and also does not connect.
Output: BLE Advertised Device found: Name: MyESP32, Address: 24:0a:c4:12:ef:a2, txPower: -21
I had this issue with many other examples but I am pretty sure it worked last week.
To be sure I also downloaded the newest ESP32 environment from GitHub, with no change
I also have a Xiaomi Flower care device around:
Output: BLE Advertised Device found: Name: Flower care, Address: c4:7c:8d:62:87:f7, serviceUUID: 0000fe95-0000-1000-8000-00805f9b34fb
If I copy this UUID to the client sketch, it connects.

@chegewara
Copy link
Collaborator

Hi,
one question, are you using library from https://github.com/nkolban/ESP32_BLE_Arduino or from https://github.com/nkolban/esp32-snippets. In other words, in arduino ide are installing library from zip file or from library manager?

@SensorsIot
Copy link
Author

From the original distribution (with git clone)

@chegewara
Copy link
Collaborator

Original distribution is https://github.com/nkolban/esp32-snippets

@SensorsIot
Copy link
Author

So there are differences between espressif/arduino-esp32 and https://github.com/nkolban/esp32-snippets? Which one do you suggest?

@chegewara
Copy link
Collaborator

At the moment, until we dont get support c++ exceptions in arduino-esp32 i suggest to not change to esp32-snippets, and when we have c++ exceptions working in arduino-ide most likely library delivered with arduino-esp32, which you call original distribution, will be updated.

Back to your issue, i will investigate it and back to you soon. In mean time, can you turn on verbose logging in arduino-ide and provide more logs?

@SensorsIot
Copy link
Author

I switched it on and compiled the client file. Where should I post the log?

@chegewara
Copy link
Collaborator

pastebin or here

@SensorsIot
Copy link
Author

It is here:
https://pastebin.com/5299TxCV

@chegewara
Copy link
Collaborator

Ok, ive found the problem. Those two examples are not prepared to work with each other without some changes. As you can see in this line https://github.com/nkolban/ESP32_BLE_Arduino/blob/f8fe9d7cdfb20caa54b70849826d1ac6e375ff78/examples/BLE_client/BLE_client.ino#L78
BLE_client expecting that server is advertising UUID, but BLE_notify example is not advertising any UUID. You can change it very easy by adding this line
pServer->getAdvertising()->addServiceUUID(BLEUUID(SERVICE_UUID)); somwhere in here https://github.com/nkolban/ESP32_BLE_Arduino/blob/f8fe9d7cdfb20caa54b70849826d1ac6e375ff78/examples/BLE_notify/BLE_notify.ino#L78
tis will let you to connect 2 esp32 by using BLE_client and BLE_notify examples. There is one more thing you have to remember. When you are advertising 128 bit UUID then name of device cant be longer than 5 characters.

@chegewara
Copy link
Collaborator

You can also change this line https://github.com/nkolban/ESP32_BLE_Arduino/blob/f8fe9d7cdfb20caa54b70849826d1ac6e375ff78/examples/BLE_client/BLE_client.ino#L78 and search device you want to connect to not by advertised UUID but ie by name, or served service etc

@SensorsIot
Copy link
Author

Now it works. Very good! Thank you very much for your help. Now I can go on with my Polar H7 emulation... And with my new video where I need BLE UART functionality. So it is well possible that I have to come back because for the moment I can only transfer a few characters to my Smartphone...
Just a small question concerning your additional line: Why do I have to use BLEUUID(SERVICE_UUID) and not only (SERVICE_UUID)?

@chegewara
Copy link
Collaborator

chegewara commented Dec 12, 2017

You can use both, BLEUUID(SERVICE_UUID) and (SERVICE_UUID) because this method is overloaded and can be used with BLEUUID or with string uuid.

EDIT you can use both only in case if SERVICE_UUID is 128 bit uuid in string variable. In case you want to use 16 or 32 bit uuid only BLEUUID(SERVICE_UUID) works

@SensorsIot
Copy link
Author

SensorsIot commented Dec 12, 2017

I try now to read the values sent by notify.
in BLE_notify I inserted:
cscCharacteristic.setValue(csc, 11);
cscCharacteristic.notify();

In BLE_client I am able to see the values once during connect of the characteristics:
in connectToServer(BLEAddress pAddress) at the bottom I inserted:
// Read the value of the characteristic.
std::string value = pRemoteCharacteristic->readValue();
Serial.println("The characteristic value was: ");
for (int i = 0; i < value.length(); i++) Serial.println(value[i], HEX);
Serial.print("length: ");
Serial.println(value.length());

But not during notifyCallback:

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.println("The characteristic value was: ");
// Read the value of the characteristic.
std::string value = pBLERemoteCharacteristic->readValue();
for (int i = 0; i < value.length(); i++) Serial.println(value[i], HEX);
Serial.print("length: ");
Serial.println(value.length());
}
The callback is called, but the line std::string value = pBLERemoteCharacteristic->readValue(); blocks the execution
In NRFconnect the csc value counts up

The whole code is here (strongly based on the notify example)
https://pastebin.com/PbtSvvVU

And the notifier: https://pastebin.com/0ngKNkef (based on the Cadence and speed service)

@chegewara
Copy link
Collaborator

In my case with original BLE_notify and BLE_client i can receive notifications, but i will investigate custom code.

@chegewara
Copy link
Collaborator

I see the problem. Here you have created chracteristic with notify:
BLECharacteristic cscCharacteristic(BLEUUID((uint16_t)0x2A5B), BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); thats good, but if you will check with nRF connect there is missing something. With properly implemented notify/indicate functionality you will see three arrows like on this picture http://a2.mzstatic.com/us/r30/Purple18/v4/21/65/4d/21654db0-6c4e-2237-18a5-624b22e783da/screen696x696.jpeg

This leads us to conclusion there is missing something. Every characteristic with notify and/or indicate needs to have descriptor 2902. There is special library which help to create and add this descriptor BLE2902.

@SensorsIot
Copy link
Author

Thank you for your help. Sorry for coming back late. I was away. Now my H7 fake works and delivers values to nRF connect. I can switch notifications on and off using BLE2902. Great. I again watched Neils videos and understood them now much more. The next problem I have is on the client side and unfortunately, Neil did not explain in his client video is how to display the notified values in the client. I get them once the client connects to the server and, if I put a pRemoteCharacteristic->readValue(); in the loop() I get also the updates. But not in a callback, which would be much nicer to avoid getting the same value twice...
Also, I do not know how I can disable notifications from my ESP client (with the phone I just push the three arrows and the BLE2902 disables notifications).
Is there a documentation of these commands? I looked into Neils ESP32 book but it is somehow a mixture between c and c++

@chegewara
Copy link
Collaborator

No rush, we dont have timeline when you have to comeback and response.

Thats strange, you should have get notifications and read value in notificationCallback. See this code:
https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino#L18

If you asking about turning on/off notifications, you need to read remoteDescriptor BLE2902 descriptor from pRemoteCharacteristic and then writeValue(bool):
https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLERemoteDescriptor.h#L36

pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902)->writeValue(0)  <- TURN OFF
pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902)->writeValue(1)  <- TURN ON NOTIFICATIONS ONLY
pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902)->writeValue(2)  <- TURN ON INDICATIONS ONLY
pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902)->writeValue(3)  <- TURN ON NOTIFICATIONS AND INDICATIONS

@SensorsIot
Copy link
Author

Interesting things happen:

  1. You are right. If I use Neils original script it triggers the notification callback. As soon as I try to read the value (std::string value = pBLERemoteCharacteristic->readValue();) it blocks in the callback function at the first call.
  2. I tried to switch the notification on and off in loop :
    if (onoff) pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue(0);
    else pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue(1);
    If I look at Serial of the server, I get this message:
    E (2597062) BT: gatts_write_attr_perm_check - GATT_INVALID_PDU
    and the notifications do not stop.

@SensorsIot
Copy link
Author

SensorsIot commented Dec 15, 2017

Sorry for #1. I was stupid and did not understand the concept. Now it works.
#2 still open...

@chegewara
Copy link
Collaborator

chegewara commented Dec 15, 2017

Nothing wrong happened, im glad it works.

@chegewara
Copy link
Collaborator

Sorry it takes so long, but honestly documentation on bluetooth.org sucks. This is the right solution to turn on notifications:

const uint8_t v[]={0x1,0x0};
pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)v,2,false);

Explanation:

  • you need to send 2 bytes
  • value to send is 1 in bug endian order

If something else requires explanation dont hesitate to ask.

@SensorsIot
Copy link
Author

I tried now your code and get the following response in the Server:
E (516057) BT: GATTS_SendRsp conn_id: 3 waiting for op_code = 00
E (516057) BT: Sending response failed
E (517077) BT: GATTS_SendRsp conn_id: 3 waiting for op_code = 00
E (517077) BT: Sending response failed
The notifications do not stop

I put the statement in the loop after (connectToServer(*pServerAddress). If I put it at the end of init the esp crashes. Is there a particular place where it has to go?
BTW what would then be the command to switch it on?

@chegewara
Copy link
Collaborator

chegewara commented Dec 15, 2017

For comparision i will post my code. This is client code, which will switch on/off notifications:

/**
 * A BLE client example that is rich in capabilities.
 */

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

// The remote service we wish to connect to.
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
// The characteristic of the remote service we are interested in.
static BLEUUID    charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");

static BLEAddress *pServerAddress;
static boolean doConnect = false;
static boolean connected = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;

static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
    Serial.print("Notify callback for characteristic ");
    Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
    Serial.print(" of data length ");
    Serial.println(length);
}

bool connectToServer(BLEAddress pAddress) {
    Serial.print("Forming a connection to ");
    Serial.println(pAddress.toString().c_str());
    
    BLEClient*  pClient  = BLEDevice::createClient();
    Serial.println(" - Created client");

    // Connect to the remove BLE Server.
    pClient->connect(pAddress);
    Serial.println(" - Connected to server");

    // 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());
      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());
      return false;
    }
    Serial.println(" - Found our characteristic");

    // Read the value of the characteristic.
    std::string value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());

    pRemoteCharacteristic->registerForNotify(notifyCallback);
delay(100);
}
/**
 * Scan for BLE servers and find the first one that advertises the service we are looking for.
 */
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
 /**
   * Called for each advertising BLE server.
   */
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("BLE Advertised Device found: ");
    Serial.println(advertisedDevice.toString().c_str());

    // We have found a device, let us now see if it contains the service we are looking for.
    if (1) {  <-- THIS is not important in my case, i have just 1 device so i want to connect to first found device, but in normal case its required to search device by name, or advertised service or any other parameter

      // 
      Serial.print("Found our device!  address: "); 
      advertisedDevice.getScan()->stop();

      pServerAddress = new BLEAddress(advertisedDevice.getAddress());
      doConnect = true;

    } // Found our server
  } // onResult
}; // MyAdvertisedDeviceCallbacks


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

  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 30 seconds.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);
  pBLEScan->start(1);
} // End of setup.


// This is the Arduino main loop function.
bool onoff = false;

const uint8_t x[]={0x0,0x0};
const uint8_t v[]={0x1,0x0};
void loop() {

  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are 
  // connected we set the connected flag to be true.
  if (doConnect == true) {
    if (connectToServer(*pServerAddress)) {
      Serial.println("We are now connected to the BLE Server.");
      connected = true;
    } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
    }
    doConnect = false;
  }

  // If we are connected to a peer BLE Server, update the characteristic each time we are reached
  // with the current time since boot.
  if (connected) {
    String newValue = "Time since boot: " + String(millis()/1000);
    Serial.println("Setting new characteristic value to \"" + newValue + "\"");
    
    // Set the characteristic's value to be the array of bytes that is actually a string.
    pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
    if (onoff) pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)x, 2, true);
    else     pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)v,2,true);

onoff=onoff?0:1;
  }
  
  delay(10000); // Delay a second between loops.
} // End of loop

Server code is BLE_notify

@SensorsIot
Copy link
Author

Great! Now it works here too. I still get these messages from the server.

E (311726) BT: GATTS_SendRsp conn_id: 3 waiting for op_code = 00
E (311726) BT: Sending response failed

But who cares if it works ;-)
Thank you for your help. I learned a lot in the last two days.
BTW: I was able to transfer messages with a length of 140 bytes. This is important for my next project. It seems, that this BLE is really cool stuff.

@chegewara
Copy link
Collaborator

chegewara commented Dec 15, 2017

Yes, im getting those messages too. But they are from esp-idf stack and sometimes even if they are suggesting its error (E) it is for information purpose only.

PS thanks to you i learned some stuff too, especially about descriptors and switching remote notifications.

@SensorsIot
Copy link
Author

Thanks. They do not appear if I switch notification off from nRF. So it seems to be a difference between the ESP sketch and the nRF. Minor thing...

@kurtbeheydt
Copy link

@chegewara I have the same problem in the BLE_uart.ino demo.
I added as you suggested the extra line (line 100):
pServer->getAdvertising()->addServiceUUID(BLEUUID(SERVICE_UUID));

But the serviceUUID seems not advertised as it should be (see screenshot):
screenshot

I am using version 0.4.13 of the library:
screen shot 2018-06-14 at 18 37 34

When I connect manually, the UART service becomes available, but I can't scan on devices advertising this serviceUUID.

Is there anything I can do?

@chegewara
Copy link
Collaborator

There is 2 options:

  • short UART device name to 5 characters or less,
  • or, which ive found just few days ago, dont advertise name like we always do but add it to scan advertising.

@kurtbeheydt
Copy link

Thx. the first trick did the job!
But I don't quite understand what you mean bij adding it to the scan advertising?

for the moment the advertising part are these lines:

  // Start the service
  pService->start();

  // Start advertising
  pServer->getAdvertising()->addServiceUUID(BLEUUID(SERVICE_UUID));
  pServer->getAdvertising()->start();

@chegewara
Copy link
Collaborator

Yes, but you have regular advertising packet and scan advertising packet, 31 bytes each.

@kurtbeheydt
Copy link

Can you explain that to me with an example?

@chegewara
Copy link
Collaborator

You can set two different set of advertising data:
https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLEAdvertising.cpp#L139-L166

@kurtbeheydt
Copy link

I am trying to understand it, but this does not seem te work with a devicename > 5 characters:

  // Start the service
  pService->start();

  // Start advertising
  BLEAdvertising *pAdvertising;
  pAdvertising = pServer->getAdvertising();
  pAdvertising->addServiceUUID(BLEUUID(SERVICE_UUID));
  BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
  pAdvertising->setScanResponseData(oScanResponseData);
  pAdvertising->start();

@chegewara
Copy link
Collaborator

You need to leave with empty name BLEDevice::init("") and set it here for scan response (oScanResponseData ):
https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLEAdvertising.h#L28

@kurtbeheydt
Copy link

// Start advertising
  BLEAdvertising *pAdvertising;
  pAdvertising = pServer->getAdvertising();
  pAdvertising->addServiceUUID(BLEUUID(SERVICE_UUID));
  BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
  oScanResponseData->setName("myUartDevice");
  pAdvertising->setScanResponseData(oScanResponseData);
  pAdvertising->start();

gives an error now: base operand of '->' has non-pointer type 'BLEAdvertisementData' but in the documentation it should be?

BLE is driving me crazy ;)

@bretmh
Copy link

bretmh commented Sep 3, 2021

I'm not working with Arduino, but with a mobile framework. When writing data I was also receiving the error E (982295) BT_GATT: GATTS_SendRsp conn_id: 3 waiting for op_code = 00

Upon writing to a characteristic, there are typically two options. WriteWithoutResponse or WriteWithRepsonse, make sure to WriteWithResponse to avoid any response errors on ESP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants