LLPM questions

Dear Nordic engineers

I ask a question about LLPM some time ago and I accidentally closed the discussion.Now there is a new follow up.

devzone.nordicsemi.com/.../llpm-questions

1、This issues has a FAE is working on it.We intercepted the packet and found that communication packet in the air is normal.This problem is because the callback function is not entered.Please give me some  good suggestions for this problem.  

2、Because this issues was only discovered during their internal testing, and there are no related complaints about products currently on the market, we suspect it is a problem with the test computer.Can be put on hold for now.

Now they have some new questions and need your help.

1、How to modify the connection interval of LLPM?We try to use function call set_conn_params in ble_conn_params.c to increase the connection interval  when received USB_DC_SUSPEND event.This is correct.When LLPM mode is enabled,the connection interval parameter ranges from 1000us to 7000us.Now we hope that the power consumption can be lower,so we would like to set the connection interval to be larger,but if modify the larger parameter ,it will not take effect.Is there any other way to modify the connection interval?

2、The LLPM mode communicates with the dongle.When there is a lot of communicates data,the event queue will overflow,causing the connection to be disconnected.Please tell me,how to increase the size of the event queue?

Thank you for your support and best regards~

  • Dear Kenneth

    Yesterday,I went to customers office.Now I understand why they need to modify the connection interval of LLPM.

    1、After the computer goes to sleep, if the connection interval is still 1ms, the power consumption of the computer will be particularly high, which does not meet the specification.So they want to modify the connection interval to 7ms, but after modifying it to 7ms, there will be a timeout and disconnection. This problem has not been resolved.

    So I have an idea: Is it possible to change the connection mode of dongle and keyboard from LLPM mode to BLE mode after the computer goes to sleep, because the power consumption of BLE mode will be lower, and then switch from BLE mode to LLPM after the computer wakes up mode, which solves their troubles. Do you think this will work? And how do I go about switching these two modes, by the relevant examples?

    2、Finally, they also asked about the problem of abnormal CAP and NUM indicators, is there any new update?

    Best regards.

  • Regarding 1) We don't have any example, but I can see they are trying to use ble_conn_params.c to set connection parameters, and they try to add a custom connection parameter update relying on USB state events (suspend and resume). This seems like a good approach. I have created an internal jira to look into this for future (DESK-1119).

    Regarding 2) No update from the internal jira (NCSDK-13208), it may be related to the zephyr Bluetooth host, so you may ask them to try to update to latest NCS release and see if they experience the same.

    Best regards,
    Kenneth

  • Dear Kenneth

    1. That is to say, my idea is feasible, allowing them to switch between the two modes in the USB event. So I would like to know how to switch between these two modes, or just need to update the connection interval. They tried updating a longer connection interval, which works to switch from LLPM mode to BLE mode, but switching from BLE mode to LLPM mode fails. Please give me some good advice.

    2. I would suggest that they use NCS1.9.1 for testing.

    Best regards.

  • 1) If it works switching from LLPM->BLE, but fails to switch from BLE->LLPM then ask them to check if they have done this in two steps as written in the Limitations:
    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrfxlib/softdevice_controller/limitations.html# 

    "DRGN-11297: Maximum connection interval of 10 ms before entering LLPM-mode
    The maximum connection interval that can be active when switching to a connection interval of 1 ms is 10 ms.

    Workaround: An application that use a higher interval than 10 ms must perform two connection updates to use 1 ms connection interval:

    A first update to 10 ms connection interval.

    A second update to 1 ms connection interval."

    Best regards,
    Kenneth

  • Dear Kenneth

    We update the connection interval twice according to the method you said,and switch from BLE mode to LLPM mode,the following error will occur:

           static void update_peer_conn_params(const struct connected_peer *peer) -->
           LOG_WRN("Cannot update conn parameters for peer %p (err:%d)",
    error code is 5
    The following is the code,please heip me and give me some advice.Help us to see the code,why this error is happing.
    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <bluetooth/conn.h>
    #include <bluetooth/hci.h>
    #include <bluetooth/gatt_dm.h>
    
    #ifdef CONFIG_BT_LL_SOFTDEVICE
    #include "sdc_hci_vs.h"
    #endif /* CONFIG_BT_LL_SOFTDEVICE */
    
    #define MODULE ble_conn_params
    #include <caf/events/module_state_event.h>
    #include <caf/events/ble_common_event.h>
    #include "ble_event.h"
    
    #include "usb_event.h"  //Marcus added
    
    #include <logging/log.h>
    LOG_MODULE_REGISTER(MODULE, CONFIG_DESKTOP_BLE_CONN_PARAMS_LOG_LEVEL);
    
    #define CONN_INTERVAL_LLPM_US		1000   /* 1 ms */
    #define CONN_INTERVAL_LLPM_REG		0x0d01 /* 1 ms */
    #if (CONFIG_CAF_BLE_USE_LLPM && (CONFIG_BT_MAX_CONN > 2))
     #define CONN_INTERVAL_BLE_REG		0x0008 /* 10 ms */
    #else
     #define CONN_INTERVAL_BLE_REG		0x0006 /* 7.5 ms */
    #endif
    #define CONN_SUPERVISION_TIMEOUT	400
    
    #define CONN_PARAMS_ERROR_TIMEOUT	K_MSEC(100)
    
    struct connected_peer {
    	struct bt_conn *conn;
    	bool discovered;
    	bool llpm_support;
    	uint16_t requested_latency;
    };
    
    static struct connected_peer peers[CONFIG_BT_MAX_CONN];
    static struct k_work_delayable conn_params_update;
    
    static bool slow_con_interval = false; //Marcus
    
    static struct connected_peer *find_connected_peer(const struct bt_conn *conn)
    {
    	for (size_t i = 0; i < ARRAY_SIZE(peers); i++) {
    		if (peers[i].conn == conn) {
    			return &peers[i];
    		}
    	}
    
    	return NULL;
    }
    
    static int set_conn_params(struct bt_conn *conn, uint16_t conn_latency,
    			   bool peer_llpm_support)
    {
    	int err;
    
    #ifdef CONFIG_CAF_BLE_USE_LLPM
    	if (peer_llpm_support) {
    		struct net_buf *buf;
    		sdc_hci_cmd_vs_conn_update_t *cmd_conn_update;
    		uint16_t conn_handle;
    
    		err = bt_hci_get_conn_handle(conn, &conn_handle);
    		if (err) {
    			LOG_ERR("Failed obtaining conn_handle (err:%d)", err);
    			return err;
    		}
    
    		buf = bt_hci_cmd_create(SDC_HCI_OPCODE_CMD_VS_CONN_UPDATE,
    					sizeof(*cmd_conn_update));
    		if (!buf) {
    			LOG_ERR("Could not allocate command buffer");
    			return -ENOBUFS;
    		}
    
    		cmd_conn_update = net_buf_add(buf, sizeof(*cmd_conn_update));
    		cmd_conn_update->connection_handle   = conn_handle;
    		cmd_conn_update->conn_interval_us    = CONN_INTERVAL_LLPM_US;
    		cmd_conn_update->conn_latency        = conn_latency;
    		cmd_conn_update->supervision_timeout = CONN_SUPERVISION_TIMEOUT;
    
    		err = bt_hci_cmd_send_sync(SDC_HCI_OPCODE_CMD_VS_CONN_UPDATE, buf,
    					   NULL);
    	} else
    #endif /* CONFIG_CAF_BLE_USE_LLPM */
    	{
    		struct bt_le_conn_param param = {
    			.interval_min = CONN_INTERVAL_BLE_REG,
    			.interval_max = CONN_INTERVAL_BLE_REG,
    			.latency = conn_latency,
    			.timeout = CONN_SUPERVISION_TIMEOUT,
    		};
    
    		err = bt_conn_le_param_update(conn, &param);
    
    		if (err == -EALREADY) {
    			/* Connection parameters are already set. */
    			err = 0;
    		}
    	}
    
    	return err;
    }
    
    static void update_peer_conn_params(const struct connected_peer *peer)
    {
    	__ASSERT_NO_MSG(peer);
    	/* Do not update peripheral's connection parameters before the discovery
    	 * is completed.
    	 */
    	if (!peer->discovered) {
    		return;
    	}
    
    	struct bt_conn *conn = peer->conn;
    	uint16_t latency = peer->requested_latency;
    
    	__ASSERT_NO_MSG(conn);
    	int err = set_conn_params(conn, latency, peer->llpm_support);
    
    	if (err) {
    		LOG_WRN("Cannot update conn parameters for peer %p (err:%d)",
    			(void *)peer, err);
    		/* Retry to update the connection parameters after an error. */
    		k_work_reschedule(&conn_params_update,
    				      CONN_PARAMS_ERROR_TIMEOUT);
    	} else {
    		LOG_INF("Conn params for peer: %p set: %s, latency: %"PRIu16,
    		  (void *)peer->conn,
    		  (IS_ENABLED(CONFIG_CAF_BLE_USE_LLPM) && peer->llpm_support) ?
    		  "LLPM" : "BLE", latency);
    	}
    }
    
    static bool conn_params_update_required(struct connected_peer *peer)
    {
    	if (!peer->conn) {
    		return false;
    	}
    
    	struct bt_conn_info info;
    	int err = bt_conn_get_info(peer->conn, &info);
    
    	if (err) {
    		LOG_ERR("Cannot get conn info (%d)", err);
    		return true;
    	}
    
    	__ASSERT_NO_MSG(info.role == BT_CONN_ROLE_CENTRAL);
    
    	if ((peer->llpm_support && (info.le.interval != CONN_INTERVAL_LLPM_REG)) ||
    	    (!peer->llpm_support && (info.le.interval != CONN_INTERVAL_BLE_REG)) ||
    	    (info.le.latency != peer->requested_latency)) {
    		return true;
    	}
    
    	return false;
    }
    
    static void conn_params_update_fn(struct k_work *work)
    {
    	__ASSERT_NO_MSG(work != NULL);
    
    	for (size_t i = 0; i < ARRAY_SIZE(peers); i++) {
    		if (conn_params_update_required(&peers[i])) {
    			update_peer_conn_params(&peers[i]);
    		}
    	}
    }
    
    static void ble_peer_conn_params_event_handler(const struct ble_peer_conn_params_event *event)
    {
    	if (event->updated) {
    		/* Ignore information that connection parameters were already
    		 * updated.
    		 */
    		return;
    	}
    
    	struct connected_peer *peer = find_connected_peer(event->id);
    
    	__ASSERT_NO_MSG(peer);
    	peer->requested_latency = event->latency;
    	k_work_reschedule(&conn_params_update, K_NO_WAIT);
    
    	LOG_INF("Request to update conn: %p latency to: %"PRIu16,
    		event->id, event->latency);
    }
    
    static void init(void)
    {
    	k_work_init_delayable(&conn_params_update, conn_params_update_fn);
    }
    
    static void peer_connected(struct bt_conn *conn)
    {
    	struct connected_peer *new_peer = NULL;
    
    	for (size_t i = 0; i < ARRAY_SIZE(peers); i++) {
    		if (!peers[i].conn) {
    			new_peer = &peers[i];
    			break;
    		}
    	}
    	__ASSERT_NO_MSG(new_peer);
    
    	new_peer->conn = conn;
    }
    
    static void peer_disconnected(struct bt_conn *conn)
    {
    	struct connected_peer *peer = find_connected_peer(conn);
    
    	if (peer) {
    		peer->conn = NULL;
    		peer->llpm_support = false;
    		peer->discovered = false;
    		peer->requested_latency = 0;
    	}
    }
    
    static void peer_discovered(struct bt_conn *conn, bool peer_llpm_support)
    {
    	struct connected_peer *peer = find_connected_peer(conn);
    
    	if (peer) {
    		peer->llpm_support = peer_llpm_support;
    		peer->discovered = true;
    		update_peer_conn_params(peer);
    	}
    }
    
    static bool event_handler(const struct event_header *eh)
    {
    	if (is_module_state_event(eh)) {
    		const struct module_state_event *event =
    			cast_module_state_event(eh);
    
    		if (check_state(event, MODULE_ID(ble_state),
    				MODULE_STATE_READY)) {
    			static bool initialized;
    
    			__ASSERT_NO_MSG(!initialized);
    			initialized = true;
    
    			init();
    			module_set_state(MODULE_STATE_READY);
    		}
    
    		return false;
    	}
    
    	if (is_ble_discovery_complete_event(eh)) {
    		const struct ble_discovery_complete_event *event =
    			cast_ble_discovery_complete_event(eh);
    
    		peer_discovered(bt_gatt_dm_conn_get(event->dm),
    				event->peer_llpm_support);
    
    		return false;
    	}
    
    	if (is_ble_peer_event(eh)) {
    		const struct ble_peer_event *event =
    			cast_ble_peer_event(eh);
    
    		if (event->state == PEER_STATE_CONNECTED) {
    			peer_connected(event->id);
    		} else if (event->state == PEER_STATE_DISCONNECTED) {
    			peer_disconnected(event->id);
    		}
    
    		return false;
    	}
    
    	if (is_ble_peer_conn_params_event(eh)) {
    		ble_peer_conn_params_event_handler(
    			cast_ble_peer_conn_params_event(eh));
    
    		return false;
    	}
            /* Marcus added */
            if (is_usb_state_event(eh)) {
    		const struct usb_state_event *event = cast_usb_state_event(eh);
    
    		switch (event->state) {
    		case USB_STATE_POWERED:
    		case USB_STATE_ACTIVE:
    
                          if(slow_con_interval == true){
    
                            slow_con_interval = false;
    	                k_work_reschedule(&conn_params_update, K_NO_WAIT);
    
                          }
    
    			break;
    		case USB_STATE_SUSPENDED:
    
                          if(slow_con_interval == false){
    
                               struct bt_le_conn_param param = {
    			    .interval_min = 0x0050, /* 100ms  */
    			    .interval_max = 0x0050,/* 100ms  */
    			    .latency = 0x0006,
    			    .timeout = 400,
    		            };
                                     /* Marcus : update all peer*/
                                	for (size_t i = 0; i < ARRAY_SIZE(peers); i++) {
    		                   struct bt_conn *conn = peers[i].conn;
    			          int err = bt_conn_le_param_update(conn, &param);
                                                                       
    		                   if (err == -EALREADY) {
    			           /* Connection parameters are already set. */
    			            err = 0;                                           	                   
    	                            }
    		                }
                              slow_con_interval = true;
                          }
    	
    			break;
    		default:
    			/* Ignore */
    			break;
    		}
    		return false;
    	}
    
    
    	/* If event is unhandled, unsubscribe. */
    	__ASSERT_NO_MSG(false);
    
    	return false;
    }
    
    EVENT_LISTENER(MODULE, event_handler);
    EVENT_SUBSCRIBE(MODULE, module_state_event);
    EVENT_SUBSCRIBE(MODULE, ble_discovery_complete_event);
    EVENT_SUBSCRIBE(MODULE, ble_peer_event);
    EVENT_SUBSCRIBE(MODULE, ble_peer_conn_params_event);
    EVENT_SUBSCRIBE(MODULE, usb_state_event);  //Marcus added
    
    
     
    Best regard.

      

Related