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!

Parents
  • 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!

Reply
  • 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!

Children
Related