Open ArdenKolodner opened 2 weeks ago
Another note on this that I just discovered: the log_w("- Truncating to %d bytes (maximum notify size)", _mtu - 3);
line turns out to take a total of 9ms to run(!!), which was single-handedly throttling my device's BLE throughput rates – our goal is 10ms per packet, but we have our own processing to do between packets, of course. ~This could be solved by having the peripheral device increase its MTU, but then there's more unnecessary data being transmitted, also throttling throughput. So a benefit of fixing this issue will be an immediate and automatic increase in BLE transmission times for packets above 23 bytes!~
Update: Lowering the Arduino log level to Error (or no output) fixes this, actually. Should have thought of that earlier lol.
@ArdenKolodner - From the log, it sounds like it is an Arduino as Component project.
In order to verify the issue, we need a minimum Arduino Sketch that can be used to reproduce the issue and latter to verify a potential fix. Please provide such sketch, but have in mind that we don't fix IDF Code, only the ESP32 Arduino Core or its official libraries.
@SuGlider Here is a sketch. I cobbled it together from the Arduino example sketches for BLE client and server, so it should be as close to a textbook example as possible. The steps to use it: set up a BLE server that has a service and characteristic given by clientServiceUUID and clientCharUUID (or change their values), then on another device (I used an ESP32S3 dev board), turn the debug log level to Warning or above and upload this sketch. You should see that the client connection triggers the server connection callback, and when notify() is called, it will cause the truncation warning to occur because the server is trying to incorrectly send notification data to the client. You can also connect a client with high MTU, such as a laptop to see that that will receive the data correctly, without causing another warning. Please let me know if anything is unclear.
(Side note: I've noticed a possibly related bug where disconnecting the peripheral device will also cause the server to forcibly disconnect all clients connected to it, because it thinks the server's connections are actually client connections with 0 servers using them. This may be related, but is definitely a bug in the Bluedroid stack implementation that ESP32 uses, not Arduino. I can provide more info on it if you discover that it's relevant.)
Board
ESP32C3
Device Description
Plain module attached by USB-C
Hardware Configuration
The issue occurs when no other hardware is present.
Version
latest master (checkout manually)
IDE Name
VSCode
Operating System
macOS 14.6.1
Flash frequency
80MHz
PSRAM enabled
yes
Upload speed
115200
Description
I have an ESP32-C3 in dual-mode BLE. I'll call it the central device. It has a client connection to another device (also an ESP32-C3), which I'll call the peripheral device. It is also a server (
BLEServer
viaBLEDevice::createServer()
) which has a client connected to it. That client is my laptop.When sending a BLE notification with a characteristic value longer than 20 bytes, I got the following warning:
[W][BLECharacteristic.cpp:527] notify(): - Truncating to 20 bytes (maximum notify size)
However, the server had set its MTU to 500 viaBLEDevice::setMTU(500)
. I tried using multiple BLE client applications (LightBlue, Python via Bleak, and react-native-ble-plx), confirming that the warning occurs with all of them. Additionally, all can support an MTU of 500, so there shouldn't be any truncation. Indeed, all of the data was being transmitted successfully, with no truncation, despite the warning.During debugging, I found that the warning is not being generated when the server sends the notification to my laptop. It's actually trying to send 2 notifications: one to the peripheral device, and one to my laptop. The laptop one works fine, but the peripheral device's negotiated MTU is 23, which is too short. I didn't bother to have the peripheral select an MTU because it's not supposed to be receiving notifications from the server – it's a server as well, not a client! The server should not be sending notifications to it in the first place.
I think the problem is that the connection to the peripheral device, which is a client connection, is getting misidentified as a server connection. When the central device connects to the peripheral,
BLEServer::addPeerDevice
gets called with_client
set to false, so the central device mistakenly thinks this connection is from an incoming client, instead of what's really going on, which is that the central device is the client. Then the connection gets added tom_connectedServersMap
. However, this would happen even if_client
were true – the argument is totally ignored. But then later, whenBLECharacteristic::notify()
callsgetService()->getServer()->getPeerDevices(false)
, thegetPeerDevices()
method ignores the_client
argument again and returns the whole list of peer devices (m_connectedServersMap
) which now includes the client connection.Thus
notify()
thinks there are two clients to notify instead of just one, and it tries to notify both. The notification to the peripheral device does not throw an error, for some reason -esp_ble_gatts_send_indicate
returns ESP_OK for both notifications. But the peripheral device shouldn't be getting it anyway.The root of the problem seems to be that
BLEServer::handleGATTServerEvent()
is getting called on anESP_GATTS_CONNECT_EVT
event, when the peripheral device connects. This prompts anaddPeerDevice(..., false)
call that should not occur. As I don't have a ton of experience with BLE outside of using this library, I don't know why this is happening.Sketch
Debug Message
Other Steps to Reproduce
No response
I have checked existing issues, online documentation and the Troubleshooting Guide