BLE Nimble SPP packet fragmentation (packet > MTU)
- Dominant language
- C
- Stars
- 893
- Forks
- 512
- Avg merge
- 13d 31m
- Merged PRs (30d)
- 7
Description
## Problem Description
MTU size: 120
Sent packet size from server: 130
Received size on client: 117
Using two ESP32, one acts as client and the other as server. The client subscribes to the servers notify characteristics, and server sends a package that is larger than exchanged MTU.
On client side, the packet is received, however it's always truncated to the MTU-3 size.
Weird thing is, if I send similar packet to the server, GATT callback BLE_GATT_ACCESS_OP_WRITE_CHR gets called as many time as necessary for the whole packet to be received.
So my question is, does client handle packet fragmentation automatically just like GATT server, or do I have to implement my own algorithm?
Thanks!
Kristjan
Please check the code below:
### Expected Behavior
Callback gets called as many times as it takes to receive the whole packet.
### Actual Behavior
Callback is called only once with MTU-3 size, regardless of the actual packet size.
### Code to reproduce this issue
This snippet is from CLIENT when gap event is called with BLE_GAP_EVENT_NOTIFY_RX
```cpp
esp_err_t ble_spp_get_notification(struct ble_gap_event *event)
{
// Flatten the buffer to get the full data
uint16_t fullLength = OS_MBUF_PKTLEN(event->notify_rx.om);
uint8_t fullData[fullLength];
if (ble_hs_mbuf_to_flat(event->notify_rx.om, &fullData, fullLength, NULL) != 0) {
ESP_LOGE(tag, "Failed to get all write data");
return 0;
}
printf("Flat buffer size: %d\n", fullLength);
for(int i = 0; i < fullLength; i++) {
printf("%d ", fullData[i]);
}
return ESP_OK;
}
```
This is how packet is constructed on SERVER. As you can see the size of packet is 130 bytes of 1's.
```cpp
uint8_t send[130] = {0};
for(int i = 0; i < sizeof(send); i++) {
send[i] = 1;
}
// allocate MTU size
packet = calloc(130, sizeof(uint8_t));
if(!packet) {
return ESP_ERR_NO_MEM;
}
txom = ble_hs_mbuf_from_flat(send, 130);
ESP_LOGI("SPP", "packet size: %d, val_handle: %d", sizeof(send), ble_spp_svc_gatt_read_val_handle);
ret = ble_gattc_notify_custom(get_conn_handle(),ble_spp_svc_gatt_read_val_handle,txom);
ESP_ERROR_CHECK_WITHOUT_ABORT(ret);
free(packet);
packet = NULL;
```
// If your code is longer than 30 lines, [GIST](https://gist.github.com) is preferred.
## Debug Logs
CLIENT
```
I (00:00:01.979) NimBLE: Connection established
I (00:00:01.981) NimBLE:
I (00:00:01.982) NimBLE: GATT procedure initiated: discover all services
I (00:00:01.998) NimBLE: GATT procedure initiated: exchange mtu
I (00:00:02.087) NimBLE: mtu update event; conn_handle=0 cid=4 mtu=120
I (00:00:02.282) NimBLE: GATT procedure initiated: discover all characteristics;
I (00:00:02.285) NimBLE: start_handle=1 end_handle=5
I (00:00:02.482) NimBLE: GATT procedure initiated: discover all characteristics;
I (00:00:02.484) NimBLE: start_handle=6 end_handle=9
I (00:00:02.682) NimBLE: GATT procedure initiated: discover all characteristics;
I (00:00:02.684) NimBLE: start_handle=10 end_handle=17
I (00:00:02.882) NimBLE: GATT procedure initiated: discover all characteristics;
I (00:00:02.884) NimBLE: start_handle=18 end_handle=21
I (00:00:03.082) NimBLE: GATT procedure initiated: discover all characteristics;
I (00:00:03.084) NimBLE: start_handle=22 end_handle=65535
I (00:00:03.282) NimBLE: GATT procedure initiated: discover all descriptors;
I (00:00:03.284) NimBLE: chr_val_handle=8 end_handle=9
I (00:00:03.383) NimBLE: GATT procedure initiated: discover all descriptors;
I (00:00:03.385) NimBLE: chr_val_handle=12 end_handle=13
I (00:00:03.482) NimBLE: GATT procedure initiated: discover all descriptors;
I (00:00:03.485) NimBLE: chr_val_handle=20 end_handle=21
I (00:00:03.583) NimBLE: GATT procedure initiated: discover all descriptors;
I (00:00:03.585) NimBLE: chr_val_handle=24 end_handle=65535
I (00:00:03.782) NimBLE: Service discovery complete; status=0 conn_handle=0
I (00:00:03.785) NimBLE: GATT procedure initiated: write;
I (00:00:03.786) NimBLE: att_handle=25 len=2
I (00:00:03.882) NimBLE: Subscribe complete; status=0 conn_handle=0 attr_handle=25
I (00:00:08.035) NimBLE: received notification; conn_handle=0 attr_handle=24 attr_len=117
Flat buffer size: 117
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
```
SERVER
```
I (00:40:19.584) BLE: BLE Connection Successful!
I (00:40:19.586) BLE: handle=0 our_ota_addr_type=0 our_ota_addr=
I (00:40:19.589) BLE: 10:52:1c:5d:d9:32
I (00:40:19.600) BLE: our_id_addr_type=0 our_id_addr=
I (00:40:19.601) BLE: 10:52:1c:5d:d9:32
I (00:40:19.602) BLE: peer_ota_addr_type=0 peer_ota_addr=
I (00:40:19.614) BLE: 24:0a:c4:8e:0e:ce
I (00:40:19.615) BLE: peer_id_addr_type=0 peer_id_addr=
I (00:40:19.626) BLE: 24:0a:c4:8e:0e:ce
I (00:40:19.627) BLE: conn_itvl=40 conn_latency=0 supervision_timeout=256 encrypted=0 authenticated=0 bonded=0
I (00:40:19.639) NimBLE: GAP procedure initiated: stop advertising.
I (00:40:19.656) BLE: mtu update event; conn_handle=0 cid=4 mtu=120
I (00:40:21.438) BLE: subscribe event; conn_handle=0 attr_handle=24 reason=1 prevn=0 curn=1 previ=0 curi=0
I (00:40:21.441) BLE: subscribe event; cur_notify=1
value handle; val_handle=12
I (00:40:21.452) BLE_GAP_SUBSCRIBE_EVENT: conn_handle from subscribe=0
esp32> ble --send 1
I (00:40:25.594) SPP: packet size: 130, val_handle: 24
I (00:40:25.596) NimBLE: GATT procedure initiated: notify;
I (00:40:25.597) NimBLE: att_handle=24
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at the BLE_GAP_EVENT_NOTIFY_RX handling on the client and the ble_gattc_notify_custom call on the server, then trace how notification buffers are processed versus GATT write callbacks. Reproduce the 130-byte notification with an MTU of 120 and compare the received length. Done means the expected fragmentation behavior is implemented or the notification limitation is clearly documented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- embedded-iot, networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100