apache / apache/mynewt-nimble

BLE Nimble SPP packet fragmentation (packet > MTU)

Open
#1,206 3 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.