<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://devzone.nordicsemi.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Dynamic BLE TX Power Config Fails (nRF5340-DK)</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/122118/dynamic-ble-tx-power-config-fails-nrf5340-dk</link><description>Hello everyone, 
 I’m working on a project using two nRF5340-DK boards to measure throughput and RSSI while dynamically changing the PHY and TX power. I started from the official throughput example and added: 
 
 
 RSSI measurement (I think this works</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Tue, 10 Jun 2025 21:47:35 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/122118/dynamic-ble-tx-power-config-fails-nrf5340-dk" /><item><title>RE: Dynamic BLE TX Power Config Fails (nRF5340-DK)</title><link>https://devzone.nordicsemi.com/thread/538723?ContentTypeID=1</link><pubDate>Tue, 10 Jun 2025 21:47:35 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:9b592ed6-e87c-47a2-a784-e2c0514acbca</guid><dc:creator>Sigurd</dc:creator><description>&lt;p&gt;Hi!&lt;/p&gt;
&lt;p&gt;Try this main.c&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include &amp;lt;zephyr/kernel.h&amp;gt;
#include &amp;lt;zephyr/console/console.h&amp;gt;
#include &amp;lt;zephyr/sys/printk.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;zephyr/types.h&amp;gt;

#include &amp;lt;zephyr/bluetooth/bluetooth.h&amp;gt;
#include &amp;lt;zephyr/bluetooth/crypto.h&amp;gt;
#include &amp;lt;zephyr/bluetooth/conn.h&amp;gt;
#include &amp;lt;zephyr/bluetooth/gatt.h&amp;gt;
#include &amp;lt;zephyr/bluetooth/hci.h&amp;gt;
#include &amp;lt;zephyr/bluetooth/uuid.h&amp;gt;
#include &amp;lt;bluetooth/services/throughput.h&amp;gt;
#include &amp;lt;bluetooth/scan.h&amp;gt;
#include &amp;lt;bluetooth/gatt_dm.h&amp;gt;

#include &amp;lt;zephyr/shell/shell_uart.h&amp;gt;

#include &amp;lt;dk_buttons_and_leds.h&amp;gt;

#include &amp;lt;zephyr/net/buf.h&amp;gt;
#include &amp;lt;zephyr/sys/byteorder.h&amp;gt;
#include &amp;lt;zephyr/bluetooth/hci_vs.h&amp;gt;

#include &amp;quot;main.h&amp;quot;

int8_t g_tx_power = 0;

#define DEVICE_NAME	CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
#define INTERVAL_MIN	0x6	/* 6 units, 7.5 ms, only used to setup connection */
#define INTERVAL_MAX	0x6	/* 6 units, 7.5 ms, only used to setup connection */

#define THROUGHPUT_CONFIG_TIMEOUT K_SECONDS(20)

static K_SEM_DEFINE(throughput_sem, 0, 1);

static volatile bool data_length_req;
static volatile bool test_ready;
static struct bt_conn *default_conn;
static uint16_t default_conn_handle;
static struct bt_throughput throughput;
static const struct bt_uuid *uuid128 = BT_UUID_THROUGHPUT;
static struct bt_gatt_exchange_params exchange_params;
static struct bt_le_conn_param *conn_param =
	BT_LE_CONN_PARAM(INTERVAL_MIN, INTERVAL_MAX, 0, 400);

static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA_BYTES(BT_DATA_UUID128_ALL,
		0xBB, 0x4A, 0xFF, 0x4F, 0xAD, 0x03, 0x41, 0x5D,
		0xA9, 0x6C, 0x9D, 0x6C, 0xDD, 0xDA, 0x83, 0x04),
};

static const struct bt_data sd[] = {
	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};

static void button_handler_cb(uint32_t button_state, uint32_t has_changed);

static const char *phy2str(uint8_t phy)
{
	switch (phy) {
	case 0: return &amp;quot;No packets&amp;quot;;
	case BT_GAP_LE_PHY_1M: return &amp;quot;LE 1M&amp;quot;;
	case BT_GAP_LE_PHY_2M: return &amp;quot;LE 2M&amp;quot;;
	case BT_GAP_LE_PHY_CODED: return &amp;quot;LE Coded&amp;quot;;
	default: return &amp;quot;Unknown&amp;quot;;
	}
}

static void instruction_print(void)
{
	printk(&amp;quot;\nType &amp;#39;config&amp;#39; to change the configuration parameters.\n&amp;quot;);
	printk(&amp;quot;You can use the Tab key to autocomplete your input.\n&amp;quot;);
	printk(&amp;quot;Type &amp;#39;run&amp;#39; when you are ready to run the test.\n&amp;quot;);
}

static int read_conn_rssi_val(uint16_t handle, int8_t *rssi_out)
{
    struct net_buf *cmd, *rsp;
    struct bt_hci_cp_read_rssi *cp;
    struct bt_hci_rp_read_rssi *rp;
    int err;

    cmd = bt_hci_cmd_create(BT_HCI_OP_READ_RSSI, sizeof(*cp));
    if (!cmd) {
        printk(&amp;quot;ERR: HCI cmd buffer\n&amp;quot;);
        return -ENOMEM;
    }

    cp = net_buf_add(cmd, sizeof(*cp));
    cp-&amp;gt;handle = sys_cpu_to_le16(handle);

    err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_RSSI, cmd, &amp;amp;rsp);
    if (err) {
        printk(&amp;quot;ERR: HCI_READ_RSSI %d\n&amp;quot;, err);
        return err;
    }

    rp = (struct bt_hci_rp_read_rssi *)rsp-&amp;gt;data;
    *rssi_out = rp-&amp;gt;rssi;
    net_buf_unref(rsp);

    return 0;
}

void scan_filter_match(struct bt_scan_device_info *device_info,
		       struct bt_scan_filter_match *filter_match,
		       bool connectable)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(device_info-&amp;gt;recv_info-&amp;gt;addr, addr, sizeof(addr));

	printk(&amp;quot;Filters matched. Address: %s connectable: %d\n&amp;quot;,
		addr, connectable);
}

void scan_filter_no_match(struct bt_scan_device_info *device_info,
			  bool connectable)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(device_info-&amp;gt;recv_info-&amp;gt;addr, addr, sizeof(addr));

	printk(&amp;quot;Filter not match. Address: %s connectable: %d\n&amp;quot;,
				addr, connectable);
}

void scan_connecting_error(struct bt_scan_device_info *device_info)
{
	printk(&amp;quot;Connecting failed\n&amp;quot;);
}

BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match,
		scan_connecting_error, NULL);

static void exchange_func(struct bt_conn *conn, uint8_t att_err,
			  struct bt_gatt_exchange_params *params)
{
	struct bt_conn_info info = {0};
	int err;

	printk(&amp;quot;MTU exchange %s\n&amp;quot;, att_err == 0 ? &amp;quot;successful&amp;quot; : &amp;quot;failed&amp;quot;);

	err = bt_conn_get_info(conn, &amp;amp;info);
	if (err) {
		printk(&amp;quot;Failed to get connection info %d\n&amp;quot;, err);
		return;
	}

	if (info.role == BT_CONN_ROLE_CENTRAL) {
		instruction_print();
		test_ready = true;
	}
}

static void discovery_complete(struct bt_gatt_dm *dm,
			       void *context)
{
	int err;
	struct bt_throughput *throughput = context;

	printk(&amp;quot;Service discovery completed\n&amp;quot;);

	bt_gatt_dm_data_print(dm);
	bt_throughput_handles_assign(dm, throughput);
	bt_gatt_dm_data_release(dm);

	exchange_params.func = exchange_func;

	err = bt_gatt_exchange_mtu(default_conn, &amp;amp;exchange_params);
	if (err) {
		printk(&amp;quot;MTU exchange failed (err %d)\n&amp;quot;, err);
	} else {
		printk(&amp;quot;MTU exchange pending\n&amp;quot;);
	}
}

static void discovery_service_not_found(struct bt_conn *conn,
					void *context)
{
	printk(&amp;quot;Service not found\n&amp;quot;);
}

static void discovery_error(struct bt_conn *conn,
			    int err,
			    void *context)
{
	printk(&amp;quot;Error while discovering GATT database: (%d)\n&amp;quot;, err);
}

struct bt_gatt_dm_cb discovery_cb = {
	.completed         = discovery_complete,
	.service_not_found = discovery_service_not_found,
	.error_found       = discovery_error,
};

static void connected(struct bt_conn *conn, uint8_t hci_err)
{
	struct bt_conn_info info = {0};
	int err;

	if (hci_err) {
		if (hci_err == BT_HCI_ERR_UNKNOWN_CONN_ID) {
			/* Canceled creating connection */
			return;
		}

		printk(&amp;quot;Connection failed, err 0x%02x %s\n&amp;quot;, hci_err, bt_hci_err_to_str(hci_err));
		return;
	}

	if (default_conn) {
		printk(&amp;quot;Connection exists, disconnect second connection\n&amp;quot;);
		bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
		return;
	}

	default_conn = bt_conn_ref(conn);

	err = bt_conn_get_info(default_conn, &amp;amp;info);
	if (err) {
		printk(&amp;quot;Failed to get connection info %d\n&amp;quot;, err);
		return;
	}

	printk(&amp;quot;Connected as %s\n&amp;quot;,
	       info.role == BT_CONN_ROLE_CENTRAL ? &amp;quot;central&amp;quot; : &amp;quot;peripheral&amp;quot;);
	printk(&amp;quot;Conn. interval is %u units\n&amp;quot;, info.le.interval);

	if (info.role == BT_CONN_ROLE_PERIPHERAL) {
		err = bt_conn_set_security(conn, BT_SECURITY_L2);
		if (err) {
			printk(&amp;quot;Failed to set security: %d\n&amp;quot;, err);
		}
	}
}

void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err security_err)
{
	printk(&amp;quot;Security changed: level %i, err: %i %s\n&amp;quot;, level, security_err,
	       bt_security_err_to_str(security_err));

	if (security_err != 0) {
		printk(&amp;quot;Failed to encrypt link\n&amp;quot;);
		bt_conn_disconnect(conn, BT_HCI_ERR_PAIRING_NOT_SUPPORTED);
		return;
	}

	struct bt_conn_info info = {0};
	int err;

	err = bt_conn_get_info(default_conn, &amp;amp;info);
	if (info.role == BT_CONN_ROLE_CENTRAL) {
		err = bt_gatt_dm_start(default_conn,
				       BT_UUID_THROUGHPUT,
				       &amp;amp;discovery_cb,
				       &amp;amp;throughput);
		if (err) {
			printk(&amp;quot;Discover failed (err %d)\n&amp;quot;, err);
		}
	}
}

static void scan_init(void)
{
	int err;
	struct bt_le_scan_param scan_param = {
		.type = BT_LE_SCAN_TYPE_PASSIVE,
		.options = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
		.interval = 0x0010,
		.window = 0x0010,
	};

	struct bt_scan_init_param scan_init = {
		.connect_if_match = 1,
		.scan_param = &amp;amp;scan_param,
		.conn_param = conn_param
	};

	bt_scan_init(&amp;amp;scan_init);
	bt_scan_cb_register(&amp;amp;scan_cb);

	err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, uuid128);
	if (err) {
		printk(&amp;quot;Scanning filters cannot be set\n&amp;quot;);

		return;
	}

	err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false);
	if (err) {
		printk(&amp;quot;Filters cannot be turned on\n&amp;quot;);
	}
}

static void scan_start(void)
{
	int err;

	err = bt_scan_start(BT_SCAN_TYPE_SCAN_PASSIVE);
	if (err) {
		printk(&amp;quot;Starting scanning failed (err %d)\n&amp;quot;, err);
		return;
	}
}

static void adv_start(void)
{
	const struct bt_le_adv_param *adv_param =
		BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE |
				BT_LE_ADV_OPT_ONE_TIME,
				BT_GAP_ADV_FAST_INT_MIN_2,
				BT_GAP_ADV_FAST_INT_MAX_2,
				NULL);
	int err;

	err = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd,
			      ARRAY_SIZE(sd));
	if (err) {
		printk(&amp;quot;Failed to start advertiser (%d)\n&amp;quot;, err);
		return;
	}
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	struct bt_conn_info info = {0};
	int err;

	printk(&amp;quot;Disconnected, reason 0x%02x %s\n&amp;quot;, reason, bt_hci_err_to_str(reason));

	test_ready = false;
	if (default_conn) {
		bt_conn_unref(default_conn);
		default_conn = NULL;
	}

	err = bt_conn_get_info(conn, &amp;amp;info);
	if (err) {
		printk(&amp;quot;Failed to get connection info (%d)\n&amp;quot;, err);
		return;
	}

	/* Re-connect using same roles */
	if (info.role == BT_CONN_ROLE_CENTRAL) {
		scan_start();
	} else {
		adv_start();
	}
}

static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
{
	printk(&amp;quot;Connection parameters update request received.\n&amp;quot;);
	printk(&amp;quot;Minimum interval: %d, Maximum interval: %d\n&amp;quot;,
	       param-&amp;gt;interval_min, param-&amp;gt;interval_max);
	printk(&amp;quot;Latency: %d, Timeout: %d\n&amp;quot;, param-&amp;gt;latency, param-&amp;gt;timeout);

	return true;
}

static void le_param_updated(struct bt_conn *conn, uint16_t interval,
			     uint16_t latency, uint16_t timeout)
{
	printk(&amp;quot;Connection parameters updated.\n&amp;quot;
	       &amp;quot; interval: %d, latency: %d, timeout: %d\n&amp;quot;,
	       interval, latency, timeout);

	k_sem_give(&amp;amp;throughput_sem);
}

static void le_phy_updated(struct bt_conn *conn,
			   struct bt_conn_le_phy_info *param)
{
	printk(&amp;quot;LE PHY updated: TX PHY %s, RX PHY %s\n&amp;quot;,
	       phy2str(param-&amp;gt;tx_phy), phy2str(param-&amp;gt;rx_phy));

	k_sem_give(&amp;amp;throughput_sem);
}

static void le_data_length_updated(struct bt_conn *conn,
				   struct bt_conn_le_data_len_info *info)
{
	if (!data_length_req) {
		return;
	}

	printk(&amp;quot;LE data len updated: TX (len: %d time: %d)&amp;quot;
	       &amp;quot; RX (len: %d time: %d)\n&amp;quot;, info-&amp;gt;tx_max_len,
	       info-&amp;gt;tx_max_time, info-&amp;gt;rx_max_len, info-&amp;gt;rx_max_time);

	data_length_req = false;
	k_sem_give(&amp;amp;throughput_sem);
}

static uint8_t throughput_read(const struct bt_throughput_metrics *met)
{
	printk(&amp;quot;[peer] received %u bytes (%u KB)&amp;quot;
	       &amp;quot; in %u GATT writes at %u bps\n&amp;quot;,
	       met-&amp;gt;write_len, met-&amp;gt;write_len / 1024, met-&amp;gt;write_count,
	       met-&amp;gt;write_rate);

	k_sem_give(&amp;amp;throughput_sem);

	return BT_GATT_ITER_STOP;
}

static void throughput_received(const struct bt_throughput_metrics *met)
{
	static uint32_t kb;

	if (met-&amp;gt;write_len == 0) {
		kb = 0;
		printk(&amp;quot;\n&amp;quot;);

		return;
	}

	if ((met-&amp;gt;write_len / 1024) != kb) {
		kb = (met-&amp;gt;write_len / 1024);
		printk(&amp;quot;=&amp;quot;);
	}
}

static void throughput_send(const struct bt_throughput_metrics *met)
{
	printk(&amp;quot;\n[local] received %u bytes (%u KB)&amp;quot;
		&amp;quot; in %u GATT writes at %u bps\n&amp;quot;,
		met-&amp;gt;write_len, met-&amp;gt;write_len / 1024,
		met-&amp;gt;write_count, met-&amp;gt;write_rate);
}

static const struct bt_throughput_cb throughput_cb = {
	.data_read = throughput_read,
	.data_received = throughput_received,
	.data_send = throughput_send
};

static struct button_handler button = {
	.cb = button_handler_cb,
};

void select_role(bool is_central)
{
	int err;
	static bool role_selected;

	if (role_selected) {
		printk(&amp;quot;\nCannot change role after it was selected once.\n&amp;quot;);
		return;
	} else if (is_central) {
		printk(&amp;quot;\nCentral. Starting scanning\n&amp;quot;);
		scan_start();
	} else {
		printk(&amp;quot;\nPeripheral. Starting advertising\n&amp;quot;);
		adv_start();
	}

	role_selected = true;

	/* The role has been selected, button are not needed any more. */
	err = dk_button_handler_remove(&amp;amp;button);
	if (err) {
		printk(&amp;quot;Button disable error: %d\n&amp;quot;, err);
	}
}

static void button_handler_cb(uint32_t button_state, uint32_t has_changed)
{
	ARG_UNUSED(has_changed);

	if (button_state &amp;amp; DK_BTN1_MSK) {
		select_role(true);
	} else if (button_state &amp;amp; DK_BTN2_MSK) {
		select_role(false);
	}
}

static void buttons_init(void)
{
	int err;

	err = dk_buttons_init(NULL);
	if (err) {
		printk(&amp;quot;Buttons initialization failed.\n&amp;quot;);
		return;
	}

	/* Add dynamic buttons handler. Buttons should be activated only when
	 * during the board role choosing.
	 */
	dk_button_handler_add(&amp;amp;button);
}



static int connection_configuration_set(const struct shell *shell,
			const struct bt_le_conn_param *conn_param,
			const struct bt_conn_le_phy_param *phy,
			const struct bt_conn_le_data_len_param *data_len)
{
	int err;
	struct bt_conn_info info = {0};

	err = bt_conn_get_info(default_conn, &amp;amp;info);
	if (err) {
		shell_error(shell, &amp;quot;Failed to get connection info %d&amp;quot;, err);
		return err;
	}

	if (info.role != BT_CONN_ROLE_CENTRAL) {
		shell_error(shell,
		&amp;quot;&amp;#39;run&amp;#39; command shall be executed only on the central board&amp;quot;);
	}

	err = bt_conn_le_phy_update(default_conn, phy);
	if (err) {
		shell_error(shell, &amp;quot;PHY update failed: %d\n&amp;quot;, err);
		return err;
	}

	shell_print(shell, &amp;quot;PHY update pending&amp;quot;);
	err = k_sem_take(&amp;amp;throughput_sem, THROUGHPUT_CONFIG_TIMEOUT);
	if (err) {
		shell_error(shell, &amp;quot;PHY update timeout&amp;quot;);
		return err;
	}

	if (info.le.interval != conn_param-&amp;gt;interval_max) {
		err = bt_conn_le_param_update(default_conn, conn_param);
		if (err) {
			shell_error(shell,
				    &amp;quot;Connection parameters update failed: %d&amp;quot;,
				    err);
			return err;
		}

		shell_print(shell, &amp;quot;Connection parameters update pending&amp;quot;);
		err = k_sem_take(&amp;amp;throughput_sem, THROUGHPUT_CONFIG_TIMEOUT);
		if (err) {
			shell_error(shell,
				    &amp;quot;Connection parameters update timeout&amp;quot;);
			return err;
		}
	}

	if (info.le.data_len-&amp;gt;tx_max_len != data_len-&amp;gt;tx_max_len) {
		data_length_req = true;

		err = bt_conn_le_data_len_update(default_conn, data_len);
		if (err) {
			shell_error(shell, &amp;quot;LE data length update failed: %d&amp;quot;,
				    err);
			return err;
		}

		shell_print(shell, &amp;quot;LE Data length update pending&amp;quot;);
		err = k_sem_take(&amp;amp;throughput_sem, THROUGHPUT_CONFIG_TIMEOUT);
		if (err) {
			shell_error(shell, &amp;quot;LE Data Length update timeout&amp;quot;);
			return err;
		}
	}

	if (g_tx_power != 0) {


		    shell_print(shell, &amp;quot;Setting TX power to : %d dBm&amp;quot;,
                g_tx_power);

    struct net_buf *buf, *rsp = NULL;
    struct bt_hci_cp_vs_write_tx_power_level *cp;
    struct bt_hci_rp_vs_write_tx_power_level *rp;
    int err;

    buf = bt_hci_cmd_create(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL,
                            sizeof(*cp));
    if (!buf) {
        shell_error(shell, &amp;quot;No HCI buffer&amp;quot;);
        return -ENOMEM;
    }

	err = bt_hci_get_conn_handle(default_conn,
						&amp;amp;default_conn_handle);
	if (err) {
		shell_print(shell,&amp;quot;No connection handle (err %d)&amp;quot;, err);
		return err;
	} 

    cp = net_buf_add(buf, sizeof(*cp));
    cp-&amp;gt;handle_type    = BT_HCI_VS_LL_HANDLE_TYPE_CONN;
    cp-&amp;gt;handle         = sys_cpu_to_le16(default_conn_handle);
    cp-&amp;gt;tx_power_level = g_tx_power;

    err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL,
                               buf, &amp;amp;rsp);
    if (err) {
        shell_error(shell, &amp;quot;Error setting TX power: %d&amp;quot;, err);
        return err;
    }

    rp = (void *)rsp-&amp;gt;data;
    shell_print(shell, &amp;quot;Actual TX power: %d dBm&amp;quot;,
                rp-&amp;gt;selected_tx_power);

    net_buf_unref(rsp);
}

	return 0;
}

int test_run(const struct shell *shell,
	     const struct bt_le_conn_param *conn_param,
	     const struct bt_conn_le_phy_param *phy,
	     const struct bt_conn_le_data_len_param *data_len)
{
	int err;
	uint64_t stamp;
	int64_t delta;
	uint32_t data = 0;
	int64_t  rssi_sum   = 0;
    int      rssi_count = 0;

	/* a dummy data buffer */
	static char dummy[495];

	if (!default_conn) {
		shell_error(shell, &amp;quot;Device is disconnected %s&amp;quot;,
			    &amp;quot;Connect to the peer device before running test&amp;quot;);
		return -EFAULT;
	}

	if (!test_ready) {
		shell_error(shell, &amp;quot;Device is not ready.&amp;quot;
			&amp;quot;Please wait for the service discovery and MTU exchange end&amp;quot;);
		return 0;
	}

	shell_print(shell, &amp;quot;\n==== Starting throughput test ====&amp;quot;);

	err = connection_configuration_set(shell, conn_param, phy, data_len);
	if (err) {
		return err;
	}

	shell_print(shell, &amp;quot;The test is in progress and will require around %d seconds &amp;quot;
		&amp;quot;to complete.&amp;quot;, CONFIG_BT_THROUGHPUT_DURATION / 1000);

	/* Make sure that all BLE procedures are finished. */
	k_sleep(K_MSEC(500));

	/* reset peer metrics */
	err = bt_throughput_write(&amp;amp;throughput, dummy, 1);
	if (err) {
		shell_error(shell, &amp;quot;Reset peer metrics failed.&amp;quot;);
		return err;
	}

	/* get cycle stamp */
	stamp = k_uptime_get_32();

	while (true) {
		err = bt_throughput_write(&amp;amp;throughput, dummy, 495);
		if (err) {
			shell_error(shell, &amp;quot;GATT write failed (err %d)&amp;quot;, err);
			break;
		}

		{
            int8_t rssi;
            if (read_conn_rssi_val(bt_conn_index(default_conn), &amp;amp;rssi) == 0) {
                rssi_sum   += rssi;
                rssi_count += 1;
            }
        }

		data += 495;
		if (k_uptime_get_32() - stamp &amp;gt; CONFIG_BT_THROUGHPUT_DURATION) {
			break;
		}
	}

	delta = k_uptime_delta(&amp;amp;stamp);

	printk(&amp;quot;\nDone\n&amp;quot;);
	printk(&amp;quot;[local] sent %u bytes (%u KB) in %lld ms at %llu kbps\n&amp;quot;,
	       data, data / 1024, delta, ((uint64_t)data * 8 / delta));

	if (rssi_count &amp;gt; 0) {
        int8_t avg_rssi = rssi_sum / rssi_count;
        printk(&amp;quot;Average RSSI: %d dBm over %d samples\n&amp;quot;,
               avg_rssi, rssi_count);
    } else {
        printk(&amp;quot;No RSSI samples collected\n&amp;quot;);
    }

	/* read back char from peer */
	err = bt_throughput_read(&amp;amp;throughput);
	if (err) {
		shell_error(shell, &amp;quot;GATT read failed (err %d)&amp;quot;, err);
		return err;
	}

	k_sem_take(&amp;amp;throughput_sem, THROUGHPUT_CONFIG_TIMEOUT);

	instruction_print();

	return 0;
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected = connected,
	.disconnected = disconnected,
	.le_param_req = le_param_req,
	.le_param_updated = le_param_updated,
	.le_phy_updated = le_phy_updated,
	.le_data_len_updated = le_data_length_updated,
	.security_changed = security_changed
};

int main(void)
{
	int err;

	printk(&amp;quot;Starting Bluetooth Throughput example\n&amp;quot;);

	err = bt_enable(NULL);
	if (err) {
		printk(&amp;quot;Bluetooth init failed (err %d)\n&amp;quot;, err);
		return 0;
	}

	printk(&amp;quot;Bluetooth initialized\n&amp;quot;);

	scan_init();

	err = bt_throughput_init(&amp;amp;throughput, &amp;amp;throughput_cb);
	if (err) {
		printk(&amp;quot;Throughput service initialization failed.\n&amp;quot;);
		return 0;
	}

	printk(&amp;quot;\n&amp;quot;);

	if (IS_ENABLED(CONFIG_SOC_SERIES_NRF54HX) || IS_ENABLED(CONFIG_SOC_SERIES_NRF54LX)) {
		printk(&amp;quot;Press button 0 or type \&amp;quot;central\&amp;quot; on the central board.\n&amp;quot;);
		printk(&amp;quot;Press button 1 or type \&amp;quot;peripheral\&amp;quot; on the peripheral board.\n&amp;quot;);
	} else {
		printk(&amp;quot;Press button 1 or type \&amp;quot;central\&amp;quot; on the central board.\n&amp;quot;);
		printk(&amp;quot;Press button 2 or type \&amp;quot;peripheral\&amp;quot; on the peripheral board.\n&amp;quot;);
	}

	buttons_init();

	return 0;
}
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;PS: Max tx-power on nRF5340&amp;nbsp;is 3 dbm.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>