espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.2k stars 7.34k forks source link

Notify more than 20 bytes - MTU #8190

Closed joseitor closed 10 months ago

joseitor commented 1 year ago

Board

ESP32 Wrover Module

Device Description

ESP32 WROVER - E

Hardware Configuration

Nothing relevant

Version

v2.0.9

IDE Name

Arduino IDE

Operating System

Windows 11

Flash frequency

80Mhz

PSRAM enabled

yes

Upload speed

921600

Description

I need to send by BLE more than 20 bytes in only one notification (restrictions of the APP). I have tried with setMTU() and updatePeerMTU() functions. Using updatePeerMTU(), the ESP32 doesn't show the warning of "[W][BLECharacteristic.cpp:526] notify(): - Truncating to 20 bytes (maximum notify size)" but still, the array of bytes that arrives to the another side is truncated to 20 bytes.

Is those functions working well? Is there any way to notify more than 20 bytes in our ESP32? Before I was using a rn4870, quite old, and I could make it. Is quite hard to belive that a newer and so advanced chip as ESP32 have that big limitation still.

Sketch

BLEDevice::setMTU(510);
log_i("MTU set to: %i", BLEDevice::getMTU());

class ServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    log_i("Device connected");
    uint16_t bleID = pServer->getConnId();
    pServer->updatePeerMTU(bleID, 510);
    log_i("updateMTU to: %i", pServer->getPeerMTU(bleID));
  }

Debug Message

[W][BLECharacteristic.cpp:526] notify(): - Truncating to 20 bytes (maximum notify size)

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

Gspohu commented 1 year ago

The MTU in Bluetooth is the maximum length of an ATT PDU that can be transferred in a single packet over a BLE link. The default value is 23 bytes, 3 of which are used for headers, leaving 20 bytes for payload.

In Bluetooth 4.2, the MTU size can be negotiated up to 247 bytes. However, this is dependent on both devices on the link supporting this feature and agreeing on the maximum MTU size.

It's important to remember that the MTU size negotiation is a bilateral process. Both the ESP32 and the other device must support and agree on the larger MTU size. If one device does not support it, the MTU size will default to 23 bytes.

Here are some potential solutions to your issue:

  1. Confirm that the other device supports a larger MTU size: Check the documentation or specifications for the app or device that is receiving the notifications. Confirm that it supports MTU size negotiation and find out what the maximum supported size is.

  2. Try a lower MTU size: While 510 is the theoretical maximum, it may not be supported by all devices or implementations. Try negotiating a lower MTU size, like 50 or 100, to see if that works.

joseitor commented 1 year ago

@Gspohu Thank you very much for your detailed and clear explanation

Apart from the original app that I need work with, I'm testing in different BLE app for Android. I am testing now in "BLE Tester" app that even when I connect to ESP32 it shows the MTU set, so I guess it accept the change of it, but still it says 20.

About lower MTU, I have tested now with 26 that is actually what I need, send 23 bytes at once, and it still doesn't work :/

Has anyone got ever send a notification longer than 20 bytes using the ESP32 module? I have make a deep search and I cannot find any example or reference where a MTU change is made, besides those 2 functions that I use and are in the code, but not in any example or .ino.

Gspohu commented 1 year ago

We can try a simpler test :

#include <BLEDevice.h>
#include <BLEServer.h>

BLEServer* pServer = NULL;

void setup()
{
  Serial.begin(115200);
  Serial.println("Setting up...");

  BLEDevice::init("ESP32");

  pServer = BLEDevice::createServer();

  uint16_t mtu = 67;
  BLEDevice::setMTU(mtu);

  Serial.print("MTU set to: ");
  Serial.println(BLEDevice::getMTU());
}

void loop()
{
}

I know that this small piece of code can't answer to your needs, but it's just to test if you can set a different MTU size.

joseitor commented 1 year ago

Yes, It works:

15:28:56.686 -> Setting up...
15:28:57.410 -> MTU set to: 67

But also in my original code:

BLEDevice::setMTU(26);
log_i("MTU set to: %i", BLEDevice::getMTU());

class ServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    log_i("Device connected");
    uint16_t bleID = pServer->getConnId();
    pServer->updatePeerMTU(bleID, 26);
    log_i("updateMTU to: %i", pServer->getPeerMTU(bleID));
  }

I receive the right log_i messages: [I][BLER.cpp:49] begin(): MTU set to: 26 [I][BLER.cpp:19] onConnect(): Device connected [I][BLER.cpp:23] onConnect(): updateMTU of 0 ID to: 26

and if I send a notify longer than 23, now it says [W][BLECharacteristic.cpp:526] notify(): - Truncating to 23 bytes (maximum notify size)

So it looks like the code is applied rifgt, but at the end, it's not working. The notify message is still truncated to 20 bytes (even if it says at 23), and in the app it says that MTU is still 20.

Gspohu commented 1 year ago

I'm sorry to insist, but are you really sure that the client-side application is capable of handling an MTU of more than 22 ? Or even the phone. You could test with other apps like NRF connect.

Another point, I might say something stupid, but as I have a very small part of your code, it's hard to say. But in your code, you try to set the MTU after a connection has been established. It is possible that the MTU is already negotiated to its default value (23) before your code has a chance to change it. Try setting the MTU before you create the server.

If you're always stuck, try this code :

#include <BLEDevice.h>
#include <BLEServer.h>

BLEServer* pServer = NULL;

class ServerCallbacks : public BLEServerCallbacks
{
  void onConnect(BLEServer* pServer)
  {
    log_i("Device connected");
    uint16_t bleID = pServer->getConnId();
    log_i("updateMTU to: %i", pServer->getPeerMTU(bleID));
  }
};

void setup()
{
  Serial.begin(115200);
  Serial.println("Setting up...");

  BLEDevice::init("ESP32");

  uint16_t mtu = 26;
  BLEDevice::setMTU(mtu);

  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new ServerCallbacks());

  log_i("MTU set to: %i", BLEDevice::getMTU());
}

void loop()
{
}
joseitor commented 1 year ago

I have tried with NRF connect as you said, and it has the option of "MTU request" so i could tried more scenarios and got the next results:

So, looks like is impossible (or I cannot get yet) request change of MTU from ESP32, and also both MTU functions doesn't work really well, because using one it stops advertising about the truncating part when is still happening, and the another it really changes the MTU but keep saying the message is truncated when it's NOT.

Anyway, still not possible to make the MTU change from ESP32, and I have tried calling those functions in several parts of the program, before start BLE, before connect, right after, a few seconds after...same result.

Gspohu commented 1 year ago

Ok maybe there is a bug in Arduino framework. Have you try with espidf ?

And have you look at this issue : https://github.com/espressif/arduino-esp32/issues/6926

joseitor commented 1 year ago

Not, I can't for now. I have never programed using espidf yet.

I had a look. I found the NimBLE library that maybe allow me to do that. Is that what did you mean?

Thanks for your help, hope someone else be interested in the topic and help to solve it, try do get it or find a example where someone is able to notify more than 20bytes.

Gspohu commented 1 year ago

Yes and this code https://github.com/espressif/arduino-esp32/issues/6926#issuecomment-1293841321 it seem to achieve, what you want to do, but I haven't tested.

joseitor commented 1 year ago

I tried but it doesn't work :/ I'm stuck, and is hard to believe that such a powerful chip is not able to do something so basic in BLE devices.

raphael-bmec-co commented 1 year ago

We do this without issue:

  1. The mobile device requests the MTU after making the connection.
  2. We use the characteristic 'write' method to set the data.
  3. We then call nofity
joseitor commented 1 year ago

The point is we cannot change the mobile part. This mobile app is fixed, we don't have access to it. Before, with another bluetooth module it worked, but now we try to migrate to ESP32 due all the advantages, but we cannot sent this notify over 20 bytes, when a much older BLE module made it with the same app (that doesn't request MTU change or anything)

VojtechBartoska commented 10 months ago

Hello, closing this as expired, if needed you can reopen.

HamzaHajeir commented 1 month ago

Yes, It works:

15:28:56.686 -> Setting up...
15:28:57.410 -> MTU set to: 67

But also in my original code:

BLEDevice::setMTU(26);
log_i("MTU set to: %i", BLEDevice::getMTU());

class ServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    log_i("Device connected");
    uint16_t bleID = pServer->getConnId();
    pServer->updatePeerMTU(bleID, 26);
    log_i("updateMTU to: %i", pServer->getPeerMTU(bleID));
  }

I receive the right log_i messages: [I][BLER.cpp:49] begin(): MTU set to: 26 [I][BLER.cpp:19] onConnect(): Device connected [I][BLER.cpp:23] onConnect(): updateMTU of 0 ID to: 26

and if I send a notify longer than 23, now it says [W][BLECharacteristic.cpp:526] notify(): - Truncating to 23 bytes (maximum notify size)

So it looks like the code is applied rifgt, but at the end, it's not working. The notify message is still truncated to 20 bytes (even if it says at 23), and in the app it says that MTU is still 20.

I think you'll need to address the extra 3 bytes, thus set peer's MTU to 29 rather than 26.