Unexpected empty PDU

Hello,

I'm trying to transfer data from peripheral to Central (based on Central Uart and Peripheral Uart samples) continuously with a delay between each packet of about 450us. 

peripheral device 


static void measure() {
        for (int i = 0; i < SAMPLES_PER_CYCLE; i++) {
            double sample = sin(theta);
            theta += theta_increment;

            // Scale the sample to fit within the range of 0 to 255
            int scaled_sample = (int)(sample * AMPLITUDE) + AMPLITUDE;

            // Ensure the sample is within the valid range
            if (scaled_sample < 0) 
                scaled_sample = 0;
            else if (scaled_sample > 255)
                scaled_sample = 255;
			for (uint8_t i = 0 ; i < CHUNK_NUMBER ; i++) {
				chunks[i].detial.sensor.data = (uint8_t)scaled_sample;

				if (i == 2) {// check chunk 3 (counting from 0)
					chunks[i].detial.reserved1.data += 1;
				}
				if (bt_nus_send(NULL, &chunks[i].data, sizeof(chunks[i].data))) {
					// LOG_WRN("Failed to send data over BLE connection");
				}
				// printChunks(i);
				// k_sleep(K_MSEC(1000));
				k_usleep(450);
			}

        }
		// k_sleep(K_MSEC(50));
}

static void sensor_simulated_thread_fn(void)
{
	printk("initialize measure thread.\n");
	while (true) {
		measure();
	}
}

Bluetooth LE parameters are accordingly 

Central device 

prj.conf

CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_SMP=n
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_NUS_SECURITY_ENABLED=n
CONFIG_BT_USER_PHY_UPDATE=y
CONFIG_BT_USER_DATA_LEN_UPDATE=y
CONFIG_BT_BUF_ACL_RX_SIZE=50
CONFIG_BT_BUF_ACL_TX_SIZE=50
CONFIG_BT_L2CAP_TX_MTU=40

hci_ipc.conf
CONFIG_BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT=4000000
CONFIG_BT_CTLR_PHY_2M=y
CONFIG_BT_CTLR_DATA_LENGTH_MAX=50
CONFIG_BT_BUF_ACL_RX_SIZE=50
CONFIG_BT_BUF_ACL_TX_SIZE=50
CONFIG_BT_MAX_CONN=2
CONFIG_BT_CTLR_PHY_CODED=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
CONFIG_BT_CTLR_RX_BUFFERS=18

peripheral device

prj.conf

CONFIG_BT_USER_PHY_UPDATE=y
CONFIG_BT_NUS_SECURITY_ENABLED=n
CONFIG_BT_PERIPHERAL_PREF_MIN_INT=6
CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6
CONFIG_BT_USER_DATA_LEN_UPDATE=y
CONFIG_BT_ATT_PREPARE_COUNT=2
CONFIG_BT_L2CAP_TX_BUF_COUNT=10
CONFIG_BT_BUF_ACL_TX_COUNT=10
CONFIG_BT_CONN_TX_MAX=10

CONFIG_BT_SMP=n
CONFIG_BT_BUF_ACL_RX_SIZE=50
CONFIG_BT_BUF_ACL_TX_SIZE=50
CONFIG_BT_L2CAP_TX_MTU=40



hci_ipc.conf
CONFIG_BT_CTLR_PHY_2M=y
CONFIG_BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT=4000000
CONFIG_BT_CTLR_DATA_LENGTH_MAX=50
CONFIG_BT_BUF_ACL_RX_SIZE=50
CONFIG_BT_BUF_ACL_TX_SIZE=50
CONFIG_BT_MAX_CONN=2
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y

By sniffering the communication, I noticed that Central sends frequently empty PDU each 150us, and after around 10 packets, a delay of around 1733us till the Master sends an empty PDU before the slave starts again to transmit data. 

What I expected here is that an event packet or empty pdu should never be sent since the peripheral keeps transmitting data each 450us (data size = 35 bytes including all ble headers).

here are the connection, extension length and MTU parameters 

What should I do to avoid those empty PDU packets and force peripheral to keep sending packets each 450us?

another question regarding PHY coded mode, when I select Coded mode with S2 coding. I notice a big delay with transmitting the packet over the air (around 1233us) for a packet of 35 bytes and the communication is overloaded with big delay around 6000us before the slave starts to transmit the next packet again. 

according to my calculation, it shouldn't be more than 720us right?

ncs: v2.6.0

slave & master (soc) nrf5340

samples: Peripheral UART (slave), Central Uart (master). 


thanks in advance!

  • Hi Mustafa

    I don't think adding a delay like this is the best way to achieve a consistent rate, since the delay doesn't take into account the time spent by the bt_nus_send(..) function itself. 

    Could you try to run a Zephyr timer instead, set (give) a semaphore in the timer callback, and wait for this semaphore (take) in the transmit loop? 

    Then the timer will run at a consistent 450us, independent of how long the bt_nus_send(..) function might take. 

    Best regards
    Torbjørn

  • Thank Torbjørn for your reply.

    Indeed the delay is not the best way to get a very accurate delay but this is only for demonstration purpose. I don't think that those empty PDUs occur bc of this delay, this delay is only in microseconds and I have max connection event extension of 4 ms and a connection interval of 7.5 ms, so theoretically the PDU should be applied only if there is no communication within 4 ms right? but now the empty pdu is transmitted by Master each 150us right after the notify events by the slave and each 8 empty pdu packets a big delay of around 1736us appears. This delay happens consistently (not randomly seems like). 

    Anyway I implemented your advice by using a timer and semaphore where 16 packets are sent without delay and after that wait 50ms again before sending the next 16 packets without any delay but unfortunately, the problem is still there. 

    static void connected(struct bt_conn *conn, uint8_t err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	if (err) {
    		LOG_ERR("Connection failed (err %u)", err);
    		return;
    	}
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_INF("Connected %s", addr);
    
    	current_conn = bt_conn_ref(conn);
    
    	dk_set_led_on(CON_STATUS_LED);
    	printk("Resuming measure thread.\n");
    	k_timer_start(&sensor_timer, K_MSEC(50), K_MSEC(50));
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Disconnected: %s (reason %u)", addr, reason);
    
    	if (auth_conn) {
    		bt_conn_unref(auth_conn);
    		auth_conn = NULL;
    	}
    
    	if (current_conn) {
    		bt_conn_unref(current_conn);
    		current_conn = NULL;
    		dk_set_led_off(CON_STATUS_LED);
    		k_timer_stop(&sensor_timer);
    	}
    
    }
    void send_sensor_data(struct k_timer *timer_id) {
    	k_sem_give(&sensor_sema);
    }
    
    static void measure() {
            for (int i = 0; i < SAMPLES_PER_CYCLE; i++) {
                double sample = sin(theta);
                theta += theta_increment;
    
                // Scale the sample to fit within the range of 0 to 255
                int scaled_sample = (int)(sample * AMPLITUDE) + AMPLITUDE;
    
                // Ensure the sample is within the valid range
                if (scaled_sample < 0) 
                    scaled_sample = 0;
                else if (scaled_sample > 255)
                    scaled_sample = 255;
    			for (uint8_t i = 0 ; i < CHUNK_NUMBER ; i++) {
    				chunks[i].detial.sensor.data = (uint8_t)scaled_sample;
    
    				if (i == 2) {// check chunk 3 (counting from 0)
    					chunks[i].detial.reserved1.data += 1;
    				}
    				if (bt_nus_send(NULL, &chunks[i].data, sizeof(chunks[i].data))) {
    					 LOG_WRN("Failed to send data over BLE connection");
    				}
    			}
    
            }
    }
    
    static void sensor_simulated_thread_fn(void)
    {
    	printk("initialize measure thread.\n");
    	k_sem_init(&sensor_sema, 0, 1);
    	while (true) {
    		if (k_sem_take(&sensor_sema, K_FOREVER) == 0) {
    			measure();
    		}
    	}
    }

    Thanks again for your time!

  • Hi Mustafa

    I think what you are seeing here is just the end of the connection event. Typically the SoftDevice controller will reserve around 2ms at the end of each event for post processing etc. 

    If you try with a longer connection interval (try somewhere around 15-30ms for instance) you might get longer bursts of packets without delay. 

    Please note that if the link quality is poor, and you drop one of the packets in the sequence, the rest of the connection event will be lost and you won't be able to transmit anything else until the next connection event. So in poor RF conditions a shorter connection interval might be better. 

    And if high throughput is the overall goal I would recommend combining multiple packets into one, in order to more efficiently use the radio. The ideal situation is to negotiate 251 byte data length and send 244 byte packets (leaving 7 bytes for the ATT and L2CAP headers). 

    Best regards
    Torbjørn

Related