Achieving Stable BLE Notifications with nRF52833

Hi,

I’m currently developing an application for the nRF52833 that collects sensor data via SPI and sends it out over BLE using notifications. My primary goal is to achieve a stable, periodic notification interval. Specifically, I want the time between consecutive packets received by the central device to be as stable as possible.

Current Implementation

Here’s my current approach:

static void notify_work_handler(struct k_work *work)
{
      rc = bt_gatt_notify(NULL, &ess.attrs[1], val_ptr, sizeof(*val_ptr));
      int freq = 1000 / get_current_MODR();
   k_work_reschedule(k_work_delayable_from_work(work), K_MSEC(freq));
}

This setup uses bt_gatt_notify  to queue notifications. However, this causes the following issues:

Batching of Notifications: Notifications are sent in bursts, as many as the BLE stack can fit into a single connection interval. (According to my understanding)

My Desired Outcome

  • The arrival time between two notification packtes should be stable. CurrentlyI observing multiple packets at once and sometimes skipping of connection intervals.
  • I want to avoid multiple notifications arriving at the central device at the same time.

I also don't really care if values inbetween are lost. I would rather just send out the latest value at a regular interval. I understand that i would need to set the connection interval to my desired frequency but how would i go about the queuing of the packages and only sending the latest value once per connection interval?

In the appended image i captured the time difference between the arrival of the packets using the nrfSniffer with Wireshark. I set the frequency in the notify worker to 50hz and the connection interval was 7.5ms

Any advice would be greatly appreciated!

  • Hello,

    Sorry for the late reply. 

    What is your connection interval?

    What I see from your graph can probably be explained. Most of the time it is either 14 or 22ms. Probably depending on how long the actual transmission took. I guess that some times the packet (the notification) was queued directly before the connection event (the event that occurs every connection interval), so just a bit too late to be added to the upcoming transmission. In those cases, you will probably get two packets queued for the next connection event. This will result in that the next transmission will spend a bit more time on air, which is probably why you see some increased time. 

    I also see that many of your samples (about every other one) is very close to 0. This probably means that these are two notifications sent on the same connection event. (The previous notification and the current notification). Since you will receive them in your app chronologically, the time between the previous and the current will be very short. Almost directly after. 

    So I guess we need some more details. How long is the packet that you are usually sending? How often do you send it? And what is your connection interval?

    There is something called radio notifications. Using this, you will be notified a given amount of time before the radio will be used. You can use this to sample your data, and queue it up directly before the connection event. You probably need to play around with the timing to find the sweetspot so that you will have time to sample and queue the packet long enough before the connection event.

    Best regards,

    Edvin

  • Hi,

    Thanks for the reply!

    The Graph also makes sense to me. The questions is more how to get a stable frequency. I will take a look at the radio notifications. I guess i would then set the connection interval time to my desired frequency and then i will get some sort of callback in the radio notifications at every connection event where i can then queue it up inside? Do you have any examples for getting started (I'm using the nrF connect SDK).  Also does anything change for the central device when using radio notifications?

    Edit - 

    I also found this Event Trigger example Do you think this would be an option in my case or would the radio notifications suit better here?

    docs.nordicsemi.com/.../README.html

    Best

  • Hello,

    Sorry, I meant to link to the radio notification in the previous link, but I forgot. The radio notification is found under ncs\nrf\samples\bluetooth\radio_notification.

    I am not sure about the sample that you linked to. On one hand, it looks like you can separate on multiple links, which you can't do in the radio_notification sample. However, I am not sure whether the events in the Event Trigger are triggered before or after the actual connection event, and whether you can control how long before/after it will trigger. 

    In the radio_notification sample you can adjust the time between your upcoming event and the connection event.

    Best regards,

    Edvin

  • Hi,

    I implemented an example application for testing using the radio notifications. It looks a lot better now although there is still one problem remaining. In the appended image i set the connection interval to 11ms. The packets on average also arrive at that time however there a still a few exceptions. The spikes can be explained by one connection interval being missed and then in the next one two packets are sent in one connection interval. Do you have any suggestions on how i could fix that. I already tried increasing the preparation time in the radio notification.  I appended the code and the image:

    #include <zephyr/sys/printk.h>
    #include <zephyr/types.h>
    #include <zephyr/kernel.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/hci_vs.h>
    #include <zephyr/bluetooth/controller.h>
    #include <zephyr/sys_clock.h>
    #include <bluetooth/radio_notification_cb.h>
    
    
    
    
    static void radio_notification_conn_prepare_cb(struct bt_conn *conn)
    {
        int test_val = 10;
        bt_gatt_notify(NULL, &ess.attrs[1], &test_val, sizeof(test_val));
    }
    
    void main(void)
    {
        bleSetup();
        // Setup radio notification callback
        static const struct bt_radio_notification_conn_cb radio_callbacks = {
            .prepare = radio_notification_conn_prepare_cb,
        };
    
        int err = bt_radio_notification_conn_cb_register(
            &radio_callbacks,
            BT_RADIO_NOTIFICATION_CONN_CB_PREPARE_DISTANCE_US_RECOMMENDED
        );
        if (err) {
            printk("Failed registering radio notification callback (err %d)\n", err);
            return;
        }
    
        while (true) {
            if (isConnected()) {
                printk("Connection established.\n");
                break;
            }
            k_sleep(K_MSEC(100));
        }
    }
    

  • Hello,

    You can't prevent packet loss, as that will happen, and you will have retransmissions. Also, from the Bluetooth Low Energy specification, once a packet is sent, but not Acked, it shall be retransmitted until it is acked *

    *There is one exception, which is a fairly new feature were you can use isochrounous channels, where you can set a given amount of retransmissions. This is typically intended for Bluetooth LE Audio, but I guess it is possible to use it to discard packets that are not acked in a non-audio application as well.

    Another option is to use something called periodic advertising and scanning. Here, the devices will not be connected, but one will advertise periodically (without the added 0-10ms of random delay), and the scanner can use this to sync up with the advertiser. Here you will not have a two way communication channel, though.

    If you want to investigate any of these two, you can check out:

    ncs\zephyr\samples\bluetooth\iso_peripheral and iso_central (or iso_broadcast and iso_receive. I haven't tried these, but I guess this is how they work).

    ncs\zephyr\samples\bluetooth\periodic_adv and periodic_sync.

    But with a standard BLE link, what you have now is as good as it gets.

    Best regards,

    Edvin

Related