Issues with MTU auto negotiation with peer

I’m reverse engineering getting data from a LiFePo BMS with bluetooth. I have decompiled the BMS android app and viewing the app’s log data with “adb logcat”. This device requires the user to write to a given characteristic with certain commands to request the BMS to transmit the data through a different chacteristic. I’ve successfully written the commands and received data throught the “onDataReceive” for the proper chacteristic. However, the data packet is cut at 20 bytes when it should be greater than that.

I’m getting errors during the BLE.connect(addr) of my peer device:

0000040840 [wiring.ble] TRACE: New peripheral is connected.
0000040840 [wiring.ble] TRACE: Start discovering services.
0000041382 [wiring.ble] TRACE: Start discovering characteristics of service: 1800.
0000041639 [hal.ble] ERROR: sd_ble_gattc_exchange_mtu_request() failed: 17.
0000041831 [wiring.ble] TRACE: Characteristic discovered.
0000041832 [wiring.ble] TRACE: Start discovering characteristics of service: 1801.
0000042101 [wiring.ble] TRACE: Characteristic discovered.
0000042102 [wiring.ble] TRACE: Start discovering characteristics of service: FFE0.
0000042369 [wiring.ble] TRACE: Characteristic discovered.
0000042370 [wiring.ble] TRACE: Start discovering characteristics of service: 180A.
0000042439 [hal.ble] ERROR: sd_ble_gattc_exchange_mtu_request() failed: 17.
0000042656 [wiring.ble] TRACE: Characteristic discovered.
0000042658 [wiring.ble] TRACE: Start discovering characteristics of service: 180F.
0000042756 [wiring.ble] TRACE: Characteristic discovered.
0000042757 [wiring.ble] TRACE: Start discovering characteristics of service: F000FFC0-0451-4000-B000-000000000000.
0000043106 [wiring.ble] TRACE: Characteristic discovered.
0000043239 [hal.ble] ERROR: sd_ble_gattc_exchange_mtu_request() failed: 17.

I read the [other thread about setting MTU] (How to change MTU size of BLE?) and saw someone with a similar issue and it was stated the peer device may be to blame. I suspect my peer device is not allowing the auto-negotiation. I think this because I see this in the decompiled app’s source code:

bluetoothGatt.requestMtu(40);

The app’s source code is forcing 40 bytes MTU. That corresponds with the other parts of the source code that show it pulling data out of indexes > 20.

Is there still no way to force the MTU size on a given device connection through Device OS API? I tried this, but it gave compile errors:

hal_ble_gatt_set_att_mtu(40, NULL);

I’m feeling dangerous now (with the limited knowledge I have). :grin:

After downloading the Workbench and getting the local toolchain setup, I did a local compile after editing the deviceOS/5.1.0/hal/src/nRF52840/ble_halo.cpp file to add the DEBUG statements. This is what I get now:

0000054443 [hal.ble] TRACE: hal_ble_gap_connect().
0000054848 [hal.ble] TRACE: BLE role: 2, connection handle: 0
0000054848 [hal.ble] TRACE: | interval(ms)  latency  timeout(ms) |
0000054849 [hal.ble] TRACE:   36*1.25          0       500*10
0000054850 [wiring.ble] TRACE: New peripheral is connected.
0000054850 [wiring.ble] TRACE: Start discovering services.
0000054850 [hal.ble] TRACE: hal_ble_gatt_client_discover_all_services().
0000055300 [wiring.ble] TRACE: Start discovering characteristics of service: 1800.
0000055300 [hal.ble] TRACE: hal_ble_gatt_client_discover_characteristics().
0000055647 [hal.ble] TRACE: Request to change ATT_MTU from 23 to 247 for connection: 0
0000055647 [hal.ble] ERROR: sd_ble_gattc_exchange_mtu_request() failed: 17.
0000055648 [hal.ble] TRACE: Retry to perform ATT MTU exchange procedure.
0000055750 [wiring.ble] TRACE: Characteristic discovered.
0000055750 [wiring.ble] TRACE: Start discovering characteristics of service: 1801.
0000055751 [hal.ble] TRACE: hal_ble_gatt_client_discover_characteristics().
0000055930 [wiring.ble] TRACE: Characteristic discovered.
0000055930 [wiring.ble] TRACE: Start discovering characteristics of service: FFE0.
0000055930 [hal.ble] TRACE: hal_ble_gatt_client_discover_characteristics().
0000056200 [hal.ble] TRACE: | interval(ms)  latency  timeout(ms) |
0000056200 [hal.ble] TRACE:   10*1.25          0       300*10
0000056282 [wiring.ble] TRACE: Characteristic discovered.
0000056283 [wiring.ble] TRACE: Start discovering characteristics of service: 180A.
0000056283 [hal.ble] TRACE: hal_ble_gatt_client_discover_characteristics().
0000056448 [hal.ble] TRACE: Request to change ATT_MTU from 23 to 247 for connection: 0
0000056448 [hal.ble] ERROR: sd_ble_gattc_exchange_mtu_request() failed: 17.
.
.
.
0000056808 [hal.ble] TRACE: hal_ble_gatt_client_read().
0000056832 [hal.ble] TRACE: hal_ble_gatt_client_read().
0000056857 [hal.ble] TRACE: hal_ble_gatt_client_configure_cccd().
0000056882 [hal.ble] TRACE: hal_ble_gatt_client_configure_cccd().
0000056907 [hal.ble] TRACE: hal_ble_gatt_client_configure_cccd().
0000056932 [hal.ble] TRACE: hal_ble_gatt_client_configure_cccd().
0000057033 [hal.ble] TRACE: hal_ble_gatt_client_write_without_response().   <-- send msg to BMS to report back status
0000057045 [hal.ble] TRACE: hal_ble_gatt_client_write_without_response().  <-- send msg to BMS to report back status section 2
0000057107 [app] INFO: got bmsChar data rx: 20 bytes
0000057107 [app] INFO: Data 0: cc f0 1e 34:
0000057108 [app] INFO: Data 4: f0 1e 34 0:
0000057108 [app] INFO: Data 8: 1e 34 0 0:
0000057108 [app] INFO: Data 12: 34 0 0 0:
0000057109 [app] INFO: Data 16: 0 0 0 0:
.
.
.
0000057249 [hal.ble] TRACE: Request to change ATT_MTU from 23 to 247 for connection: 0
0000057269 [hal.ble] TRACE: Effective ATT MTU: 131.

I did another experiment where I changed ble_hal_impl.h and set the max MTU to 40. I see the debug messages change to state “request to change ATT_MTU from 23 to 40”, but the onDataReceived still reports back only 20 bytes received when the peer device sends the response.

Well, after spending countless hours learning about BLE + MTU, diving through the Device OS source code and even writing my own Arduino BLU peripheral to emulate the BMS device, I now realize I made a couple of stupid mistakes.

#1 - I saw the BMS android app set MTU of 40 and referencing the battery state at index 36 and manually setting the MTU to 40, I assumed the BMS characteristic notify size was 40. Well, stupid me didn’t really notice the source code took the received packet of 20 bytes and converted it into a 40 position character string. Meaning a 0x58 packet byte got converted to the ascii string “88”. :frowning:

#2 - While the ERROR I was getting from the Device OS was just like that other thread, that was a red herring. When I coded up my own BLE peripheral with a known good BLE stack (Arduino BLE on Seeed XIAO nrf52840), I noticed the same ERROR messages coming from the Argon/Device OS. It seems the Device OS tries to run the MTU negotiation until it’s successful and after the 2nd or 3rd time it works correctly. After I adjusted the code that talks to the BMS device to allow some time between the connection and the first time I send a command, I then saw a proper MTU negotiation occur. :partying_face:

BTW - in case anyone cares, in Device OS 5.1.0, here is the proper command to manually set the MTU size:

hal_ble_gatt_server_set_desired_att_mtu(40, NULL);

Hopefully this tale will help anyone else coming in new to BLE to avoid the mistakes I made: check your assumptions and bias at the door. :slight_smile:

4 Likes