How to count retransmissions when receiving specific NUS data from nRF52832

hello Nordic

i am working with two nrf52832 socs, with ncs v2.9.0.

I would like to know how many retransmissions occurred during a Connection Event in which specific NUS data was received.

Do you have any idea?

I have already read the following ticket.

 https://devzone.nordicsemi.com/f/nordic-q-a/97591/how-can-i-get-the-number-ble-tx-re-transmissions/419584 

hope to read you soon

best regards

fmasataka

Parents
  • Hello,

    There is no direct way of doing this no, normally the application don't care about this, since it's fully handled by the link layer. 

    My best suggestion would be to look at whether any of the BLE proprietary features may help you:
    https://docs.nordicsemi.com/bundle/ncs-latest/page/nrfxlib/softdevice_controller/README.html 

    There is for instance something called: QoS Conn Event Reports

    An example in the nRF Connect SDK that use this is the LLPM example:
    https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/samples/bluetooth/llpm/README.html 

    Kenneth

  • Hi, Kenneth

    Thanks for the reply.

    There is no direct way of doing this no, normally the application don't care about this, since it's fully handled by the link layer. 

    Okay, I see.

    I'm considering how to derive retransmissions count during a Connection Event in which specific NUS data was received based on connection event counter.

    I've integrated function enable_qos_conn_evt_report and on_vs_evt, used in LLPM sample code, into Central UART sample code(※1) to obtain QoS connection event reports. I've also integrated function sdc_hci_cmd_vs_get_next_conn_event_counter(※2) to obtain next connection event counter at any desired time.

    ※1: https://github.com/nrfconnect/sdk-nrf/tree/main/samples/bluetooth/central_uart

    ※2: https://docs.nordicsemi.com/bundle/nrfxlib-apis-2.9.0/page/group_HCI_VS_API_ga7c3c54b74ce0554f0250cd38ebd7b4aa.html#ga7c3c54b74ce0554f0250cd38ebd7b4aa

    I modified Central UART sample code as follows.

    static uint16_t event_counter = 0;
    static uint16_t next_conn_event_counter = 0;
    static int re_tx_count = 0;
    
    static bool on_vs_evt(struct net_buf_simple *buf)
    {
    	uint8_t code;
    	sdc_hci_subevent_vs_qos_conn_event_report_t *evt;
    
    	code = net_buf_simple_pull_u8(buf);
    	if (code != SDC_HCI_SUBEVENT_VS_QOS_CONN_EVENT_REPORT) {
    		return false;
    	}
    
    	evt = (void *)buf->data;
    	llpm_latency.crc_mismatches += evt->crc_error_count;
    	event_counter = evt->event_counter;
    	re_tx_count = evt->nak_count + evt->rx_timeout + evt->crc_error_count;
    
    	return true;
    }
    
    static int enable_qos_conn_evt_report(void)
    {
    	int err;
    	sdc_hci_cmd_vs_qos_conn_event_report_enable_t cmd_enable;
    
    	err = bt_hci_register_vnd_evt_cb(on_vs_evt);
    	if (err) {
    		printk("Failed registering vendor specific callback (err %d)\n",
    		       err);
    		return err;
    	}
    
    	cmd_enable.enable = true;
    
    	err = hci_vs_sdc_qos_conn_event_report_enable(&cmd_enable);
    	if (err) {
    		printk("Could not enable QoS reports (err %d)\n", err);
    		return err;
    	}
    
    	printk("Connection event reports enabled\n");
    	return 0;
    }
    
    void update_conn_event_counter(void)
    {
    	uint16_t conn_handle;
    	struct bt_conn_info conn_info;
    
    	int err = bt_conn_get_info(nus_client.conn, &conn_info);
    	if (err) {
    		printk("Failed to get connection info (err %d)\n", err);
    		return;
    	}
    	conn_handle = conn_info.id;
    	
    	sdc_hci_cmd_vs_get_next_conn_event_counter_t cmd = {
    		.conn_handle = conn_handle
    	};
        sdc_hci_cmd_vs_get_next_conn_event_counter_return_t return_params;
    
        uint8_t status = sdc_hci_cmd_vs_get_next_conn_event_counter(&cmd, &return_params);
    
        if (status == 0) {
            next_conn_event_counter = return_params.next_conn_event_counter;
        }
    }
    
    static uint8_t ble_data_received(struct bt_nus_client *nus,
    						const uint8_t *data, uint16_t len)
    {
    	ARG_UNUSED(nus);
    
    	int err;
    
    	for (uint16_t pos = 0; pos != len;) {
    		struct uart_data_t *tx = k_malloc(sizeof(*tx));
    
    		if (!tx) {
    			LOG_WRN("Not able to allocate UART send data buffer");
    			return BT_GATT_ITER_CONTINUE;
    		}
    
    		/* Keep the last byte of TX buffer for potential LF char. */
    		size_t tx_data_size = sizeof(tx->data) - 1;
    
    		if ((len - pos) > tx_data_size) {
    			tx->len = tx_data_size;
    		} else {
    			tx->len = (len - pos);
    		}
    
    		memcpy(tx->data, &data[pos], tx->len);
    
    		pos += tx->len;
    
    		/* Append the LF character when the CR character triggered
    		 * transmission from the peer.
    		 */
    		if ((pos == len) && (data[len - 1] == '\r')) {
    			tx->data[tx->len] = '\n';
    			tx->len++;
    		}
    
    		
    		update_conn_event_counter();
    		printk("event_counter%d\n",event_counter);
    		printk("re_tx_count:%d\n", re_tx_count);
    		printk("next_conn_event_counter:%d\n", next_conn_event_counter);
    		
    		err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    		if (err) {
    			k_fifo_put(&fifo_uart_tx_data, tx);
    		}
    	}
    
    	return BT_GATT_ITER_CONTINUE;
    }

    And an example of console output is shown below.

    event_counter33
    re_tx_count:0
    next_conn_event_counter:35
    event_counter73
    re_tx_count:0
    next_conn_event_counter:75
    event_counter113
    next_conn_event_counter:115
    event_counter153
    re_tx_count:0
    next_conn_event_counter:155

    Is it correct to assume that the connection event counter when specific NUS data is received is either event_counter or next_conn_event_counter-1? If so, what I want to do is feasible.

    Please give me your opinion on whether this approach is versatile or not. In particular, I am wondering if it is valid no matter what value is set for connection interval.

Reply
  • Hi, Kenneth

    Thanks for the reply.

    There is no direct way of doing this no, normally the application don't care about this, since it's fully handled by the link layer. 

    Okay, I see.

    I'm considering how to derive retransmissions count during a Connection Event in which specific NUS data was received based on connection event counter.

    I've integrated function enable_qos_conn_evt_report and on_vs_evt, used in LLPM sample code, into Central UART sample code(※1) to obtain QoS connection event reports. I've also integrated function sdc_hci_cmd_vs_get_next_conn_event_counter(※2) to obtain next connection event counter at any desired time.

    ※1: https://github.com/nrfconnect/sdk-nrf/tree/main/samples/bluetooth/central_uart

    ※2: https://docs.nordicsemi.com/bundle/nrfxlib-apis-2.9.0/page/group_HCI_VS_API_ga7c3c54b74ce0554f0250cd38ebd7b4aa.html#ga7c3c54b74ce0554f0250cd38ebd7b4aa

    I modified Central UART sample code as follows.

    static uint16_t event_counter = 0;
    static uint16_t next_conn_event_counter = 0;
    static int re_tx_count = 0;
    
    static bool on_vs_evt(struct net_buf_simple *buf)
    {
    	uint8_t code;
    	sdc_hci_subevent_vs_qos_conn_event_report_t *evt;
    
    	code = net_buf_simple_pull_u8(buf);
    	if (code != SDC_HCI_SUBEVENT_VS_QOS_CONN_EVENT_REPORT) {
    		return false;
    	}
    
    	evt = (void *)buf->data;
    	llpm_latency.crc_mismatches += evt->crc_error_count;
    	event_counter = evt->event_counter;
    	re_tx_count = evt->nak_count + evt->rx_timeout + evt->crc_error_count;
    
    	return true;
    }
    
    static int enable_qos_conn_evt_report(void)
    {
    	int err;
    	sdc_hci_cmd_vs_qos_conn_event_report_enable_t cmd_enable;
    
    	err = bt_hci_register_vnd_evt_cb(on_vs_evt);
    	if (err) {
    		printk("Failed registering vendor specific callback (err %d)\n",
    		       err);
    		return err;
    	}
    
    	cmd_enable.enable = true;
    
    	err = hci_vs_sdc_qos_conn_event_report_enable(&cmd_enable);
    	if (err) {
    		printk("Could not enable QoS reports (err %d)\n", err);
    		return err;
    	}
    
    	printk("Connection event reports enabled\n");
    	return 0;
    }
    
    void update_conn_event_counter(void)
    {
    	uint16_t conn_handle;
    	struct bt_conn_info conn_info;
    
    	int err = bt_conn_get_info(nus_client.conn, &conn_info);
    	if (err) {
    		printk("Failed to get connection info (err %d)\n", err);
    		return;
    	}
    	conn_handle = conn_info.id;
    	
    	sdc_hci_cmd_vs_get_next_conn_event_counter_t cmd = {
    		.conn_handle = conn_handle
    	};
        sdc_hci_cmd_vs_get_next_conn_event_counter_return_t return_params;
    
        uint8_t status = sdc_hci_cmd_vs_get_next_conn_event_counter(&cmd, &return_params);
    
        if (status == 0) {
            next_conn_event_counter = return_params.next_conn_event_counter;
        }
    }
    
    static uint8_t ble_data_received(struct bt_nus_client *nus,
    						const uint8_t *data, uint16_t len)
    {
    	ARG_UNUSED(nus);
    
    	int err;
    
    	for (uint16_t pos = 0; pos != len;) {
    		struct uart_data_t *tx = k_malloc(sizeof(*tx));
    
    		if (!tx) {
    			LOG_WRN("Not able to allocate UART send data buffer");
    			return BT_GATT_ITER_CONTINUE;
    		}
    
    		/* Keep the last byte of TX buffer for potential LF char. */
    		size_t tx_data_size = sizeof(tx->data) - 1;
    
    		if ((len - pos) > tx_data_size) {
    			tx->len = tx_data_size;
    		} else {
    			tx->len = (len - pos);
    		}
    
    		memcpy(tx->data, &data[pos], tx->len);
    
    		pos += tx->len;
    
    		/* Append the LF character when the CR character triggered
    		 * transmission from the peer.
    		 */
    		if ((pos == len) && (data[len - 1] == '\r')) {
    			tx->data[tx->len] = '\n';
    			tx->len++;
    		}
    
    		
    		update_conn_event_counter();
    		printk("event_counter%d\n",event_counter);
    		printk("re_tx_count:%d\n", re_tx_count);
    		printk("next_conn_event_counter:%d\n", next_conn_event_counter);
    		
    		err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    		if (err) {
    			k_fifo_put(&fifo_uart_tx_data, tx);
    		}
    	}
    
    	return BT_GATT_ITER_CONTINUE;
    }

    And an example of console output is shown below.

    event_counter33
    re_tx_count:0
    next_conn_event_counter:35
    event_counter73
    re_tx_count:0
    next_conn_event_counter:75
    event_counter113
    next_conn_event_counter:115
    event_counter153
    re_tx_count:0
    next_conn_event_counter:155

    Is it correct to assume that the connection event counter when specific NUS data is received is either event_counter or next_conn_event_counter-1? If so, what I want to do is feasible.

    Please give me your opinion on whether this approach is versatile or not. In particular, I am wondering if it is valid no matter what value is set for connection interval.

Children
Related