BLE Fragmentation data loss

I am using an nRF52840 processor within a Particle Argon IoT module. I am polling a third party BLE peripheral (Shelly relay) for status data once a minute. The data packet that device responds with is 779 bytes long. I am consistently losing 3 bytes of that data.

Using a PC and its BLE, I poll for the exact same data from the peripheral BLE device. I have confirmed that the 3 bytes lost by the Argon device are the 245th, 490th, and 735th bytes. In other words, after the first sent packet, the first byte between each fragment is being lost.

I've reported this to Particle, and they pointed out that the fragmentation is dealt within the Nordic SoftDevice software.

Is this a known Nordic problem? 

The Particle OS does have a BLE software component to it which I believe could potentially be the source to this problem. 

Any insight would be appreciated.

Parents
  • Hello,

    The easiest way to troubleshoot this would likely be by capturing a bluetooth sniffer trace and compare the packet exchange when it works and when it doesn't. Our nRF Sniffer for Bluetooth LE is a low cost option, but it requires that you have a nRF52840 Dongle or one of the other supported boards laying around. Is the code to poll the sensor provided by Particle or is it something you have written. Either way, it would be nice to see the code showing how it is implemented if possible. I'm not aware of any known bugs in the stack that could explain the data loss.

    Best regards,

    Vidar

  • Here's their code (Particle). I call this member function getValue() four times in rapid succession asking for 244 bytes of data. I get 3 full packets of 244 bytes, and one final packet of 44 bytes for a total of 776 bytes. The message is supposed to be 779 bytes long. When I examine that data I see that the 245th, 490th, and 735th bytes are missing. (BLE_MAX_ATTR_PACKET_SIZE is 244).

    ssize_t BleCharacteristic::getValue(uint8_t* buf, size_t len) const {
        if (buf == nullptr || len == 0) {
            return SYSTEM_ERROR_INVALID_ARGUMENT;
        }
        len = std::min(len, (size_t)BLE_MAX_ATTR_VALUE_PACKET_SIZE);
        if (impl()->connHandle() != BLE_INVALID_CONN_HANDLE){
            return hal_ble_gatt_client_read(impl()->connHandle(), impl()->attrHandles().value_handle, buf, len, nullptr);
        }
        else if (impl()->isLocal()) {
            return hal_ble_gatt_server_get_characteristic_value(impl()->attrHandles().value_handle, buf, len, nullptr);
        }
        return SYSTEM_ERROR_INVALID_STATE;
    }
     
    I don't know if hal_ble_gatt_client_read is your SoftDevice layer being called, but I think so. Is there something special that needs to happen for fragmented packages?
  • Sorry, looks like there’s another layer in the Particle OS before SoftDevice. I’ll be looking into that further. 

    That said, I’d really like to get some insight into how a layer such as Particle’s OS should conduct itself when dealing with a fragmented packet being received from a peripheral device. 

  • Vidar,

    I was able to change Particle's OS to allow getValue reads of more than 244 bytes each call. After I did that, each call to getValue returned 245 bytes (I asked for the length my buffer can hold (1024) each call). Looping through, I now get the full message length (in this test case the message was 798 bytes and I got 245+245+245+63).

    I've observed that limiting read requests (ultimately) to SoftDevice, any value short of 245 causes loss of data. For example reading 244 each time loses 1 byte each full buffer return. Reading only 200 each call loses 45, etc.

    Does SoftDevice have a bug? Or is this a documented (or implied) feature that must be adhered to for proper fragmented BLE coms. I have also informed Particle about this. Not sure who repairs this.

    EDIT: Is this maybe an MTU issue? I tried finding the API for SoftDevice so I can read up on its use, but I was not successful. 

    EDIT2: It is an MTU issue. Apparently, Particle believes it has set it for a payload of 244 bytes each fragment, when SoftDevice has it as 245. So 1 byte is always lost per full fragment regardless of what the MTU is set to in Particle.

    I consider this a Particle issue and not a SoftDevice one.  Thanks for your time Vidar. (Tusen takk).

  • Thank you for reporting back and providing an explanation to the issue you observed. As you may know, the default att MTU for BLE devices is always 23 bytes. The longer MTU in this case is negotiated between the devices after the connection is established. The payload for a read response can be up to MTU - 1 byte ATT header while for a write or read request it is MTU - 3 bytes.

    Softdevice API documentation for future reference

    sd_ble_gattc_read()

    GATTC Characteristic or Descriptor Value Read

  • Hallo Vidar,

    any chance I can get a copy of the nRF52840 SoftDevice API doc?

    Also, do you have an opinion on how such a discrepancy could arise (other than a direct "it was set wrongly")? Is there something in the fragmentation algorithm that could explain this?

  • Hi, 

    Unfortunately, we don't have an offline version of the SD API documentation available. I think it's difficult to speculate what the root cause of this issue without some debugging or code review. This is why I initially suggested capturing sniffer trace to analyze the packet exchange on-air. This allows you to verify if the read requests and responses are as expected. If they are, you would know that the bytes are being lost at the application level. 

Reply
  • Hi, 

    Unfortunately, we don't have an offline version of the SD API documentation available. I think it's difficult to speculate what the root cause of this issue without some debugging or code review. This is why I initially suggested capturing sniffer trace to analyze the packet exchange on-air. This allows you to verify if the read requests and responses are as expected. If they are, you would know that the bytes are being lost at the application level. 

Children
No Data
Related