링크 스트레시 기반 TX power 동적 제어
- 확인 필요
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
|
CONFIG_BT_CTLR_TX_PWR_PLUS_4=y
|
||||||
|
CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y
|
||||||
|
|||||||
+217
-1
@@ -12,6 +12,8 @@
|
|||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/bluetooth/bluetooth.h>
|
#include <zephyr/bluetooth/bluetooth.h>
|
||||||
#include <zephyr/bluetooth/hci.h>
|
#include <zephyr/bluetooth/hci.h>
|
||||||
|
#include <zephyr/bluetooth/hci_vs.h>
|
||||||
|
#include <zephyr/sys/byteorder.h>
|
||||||
#include <zephyr/bluetooth/conn.h>
|
#include <zephyr/bluetooth/conn.h>
|
||||||
#include <zephyr/bluetooth/uuid.h>
|
#include <zephyr/bluetooth/uuid.h>
|
||||||
#include <zephyr/bluetooth/gatt.h>
|
#include <zephyr/bluetooth/gatt.h>
|
||||||
@@ -39,6 +41,26 @@ static bool advertising_unlimited;
|
|||||||
static uint8_t conn_param_update_attempts;
|
static uint8_t conn_param_update_attempts;
|
||||||
static int64_t last_rx_conn_param_update_ms;
|
static int64_t last_rx_conn_param_update_ms;
|
||||||
|
|
||||||
|
#define BLE_TX_POWER_NORMAL_DBM 4
|
||||||
|
#define BLE_TX_POWER_STRESS_DBM 8
|
||||||
|
#define BLE_RSSI_STRESS_DBM (-80)
|
||||||
|
#define BLE_RSSI_STRESS_COUNT 3
|
||||||
|
#define BLE_RSSI_MONITOR_INTERVAL_MS 500
|
||||||
|
#define BLE_TX_SLOW_MS 50
|
||||||
|
#define BLE_TX_CRITICAL_MS 100
|
||||||
|
#define BLE_TX_STUCK_MS 200
|
||||||
|
#define BLE_TX_SLOW_COUNT 3
|
||||||
|
#define BLE_TX_BUSY_STRESS_RETRY 4
|
||||||
|
|
||||||
|
extern int bt_hci_get_conn_handle(const struct bt_conn *conn, uint16_t *conn_handle);
|
||||||
|
|
||||||
|
static uint16_t current_conn_handle;
|
||||||
|
static bool current_conn_handle_valid;
|
||||||
|
static int8_t current_tx_power_dbm = BLE_TX_POWER_NORMAL_DBM;
|
||||||
|
static uint8_t low_rssi_count;
|
||||||
|
static uint8_t slow_tx_count;
|
||||||
|
static struct k_work_delayable tx_power_monitor_work;
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_BT_SMP)
|
#if IS_ENABLED(CONFIG_BT_SMP)
|
||||||
#define BLE_REQUIRED_SECURITY_LEVEL BT_SECURITY_L4
|
#define BLE_REQUIRED_SECURITY_LEVEL BT_SECURITY_L4
|
||||||
static uint32_t pairing_passkey = BT_PASSKEY_RAND;
|
static uint32_t pairing_passkey = BT_PASSKEY_RAND;
|
||||||
@@ -55,6 +77,150 @@ static struct k_sem nus_tx_done_sem;
|
|||||||
static struct k_work adv_restart_work;
|
static struct k_work adv_restart_work;
|
||||||
static struct k_work_delayable adv_timeout_work;
|
static struct k_work_delayable adv_timeout_work;
|
||||||
static struct k_work_delayable conn_param_update_work;
|
static struct k_work_delayable conn_param_update_work;
|
||||||
|
|
||||||
|
static int ble_tx_power_write(uint8_t handle_type, uint16_t handle, int8_t dbm,
|
||||||
|
const char *reason)
|
||||||
|
{
|
||||||
|
struct net_buf *buf;
|
||||||
|
struct net_buf *rsp;
|
||||||
|
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_alloc(K_NO_WAIT);
|
||||||
|
if (buf == NULL)
|
||||||
|
{
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
cp->handle_type = handle_type;
|
||||||
|
cp->handle = sys_cpu_to_le16(handle);
|
||||||
|
cp->tx_power_level = dbm;
|
||||||
|
|
||||||
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL, buf, &rsp);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_ERR("[BLE] TX power set fail reason=%s err=%d\r\n", reason, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
rp = (void *)rsp->data;
|
||||||
|
if (rp->status != 0U)
|
||||||
|
{
|
||||||
|
err = -EIO;
|
||||||
|
DBG_ERR("[BLE] TX power set rejected reason=%s status=0x%02x\r\n",
|
||||||
|
reason, rp->status);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current_tx_power_dbm = rp->selected_tx_power;
|
||||||
|
DBG_CORE("[BLE] TX power %d dBm reason=%s\r\n",
|
||||||
|
current_tx_power_dbm, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
net_buf_unref(rsp);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ble_set_conn_tx_power(int8_t dbm, const char *reason)
|
||||||
|
{
|
||||||
|
if (!current_conn_handle_valid || current_tx_power_dbm == dbm)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ble_tx_power_write(BT_HCI_VS_LL_HANDLE_TYPE_CONN,
|
||||||
|
current_conn_handle,
|
||||||
|
dbm,
|
||||||
|
reason ? reason : "unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_tx_power_stress(const char *reason)
|
||||||
|
{
|
||||||
|
(void)ble_set_conn_tx_power(BLE_TX_POWER_STRESS_DBM, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ble_read_conn_rssi(int8_t *rssi)
|
||||||
|
{
|
||||||
|
struct net_buf *buf;
|
||||||
|
struct net_buf *rsp;
|
||||||
|
struct bt_hci_cp_read_rssi *cp;
|
||||||
|
struct bt_hci_rp_read_rssi *rp;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!current_conn_handle_valid || rssi == NULL)
|
||||||
|
{
|
||||||
|
return -ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_alloc(K_NO_WAIT);
|
||||||
|
if (buf == NULL)
|
||||||
|
{
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
cp->handle = sys_cpu_to_le16(current_conn_handle);
|
||||||
|
|
||||||
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_RSSI, buf, &rsp);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
rp = (void *)rsp->data;
|
||||||
|
if (rp->status != 0U)
|
||||||
|
{
|
||||||
|
err = -EIO;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*rssi = rp->rssi;
|
||||||
|
}
|
||||||
|
|
||||||
|
net_buf_unref(rsp);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tx_power_monitor_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
int8_t rssi = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ARG_UNUSED(work);
|
||||||
|
|
||||||
|
if (!current_conn_handle_valid)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ble_read_conn_rssi(&rssi);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
ble_tx_power_stress("rssi-silence");
|
||||||
|
}
|
||||||
|
else if (rssi <= BLE_RSSI_STRESS_DBM)
|
||||||
|
{
|
||||||
|
if (low_rssi_count < UINT8_MAX)
|
||||||
|
{
|
||||||
|
low_rssi_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (low_rssi_count >= BLE_RSSI_STRESS_COUNT)
|
||||||
|
{
|
||||||
|
ble_tx_power_stress("rssi-low");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
low_rssi_count = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_work_schedule(&tx_power_monitor_work,
|
||||||
|
K_MSEC(BLE_RSSI_MONITOR_INTERVAL_MS));
|
||||||
|
}
|
||||||
|
|
||||||
static const char *ble_addr_type_str(uint8_t type)
|
static const char *ble_addr_type_str(uint8_t type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
@@ -357,6 +523,12 @@ static void connected(struct bt_conn *conn, uint8_t err)
|
|||||||
|
|
||||||
current_conn = bt_conn_ref(conn);
|
current_conn = bt_conn_ref(conn);
|
||||||
ble_connection_st = true;
|
ble_connection_st = true;
|
||||||
|
current_conn_handle_valid = (bt_hci_get_conn_handle(conn, ¤t_conn_handle) == 0);
|
||||||
|
current_tx_power_dbm = BLE_TX_POWER_NORMAL_DBM;
|
||||||
|
low_rssi_count = 0U;
|
||||||
|
slow_tx_count = 0U;
|
||||||
|
(void)ble_set_conn_tx_power(BLE_TX_POWER_NORMAL_DBM, "connected");
|
||||||
|
k_work_schedule(&tx_power_monitor_work, K_MSEC(BLE_RSSI_MONITOR_INTERVAL_MS));
|
||||||
|
|
||||||
/* 연결 정보 출력 */
|
/* 연결 정보 출력 */
|
||||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||||
@@ -386,8 +558,13 @@ static void connected(struct bt_conn *conn, uint8_t err)
|
|||||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||||
{
|
{
|
||||||
k_work_cancel_delayable(&conn_param_update_work);
|
k_work_cancel_delayable(&conn_param_update_work);
|
||||||
|
k_work_cancel_delayable(&tx_power_monitor_work);
|
||||||
conn_param_update_attempts = 0U;
|
conn_param_update_attempts = 0U;
|
||||||
last_rx_conn_param_update_ms = 0;
|
last_rx_conn_param_update_ms = 0;
|
||||||
|
current_conn_handle_valid = false;
|
||||||
|
current_tx_power_dbm = BLE_TX_POWER_NORMAL_DBM;
|
||||||
|
low_rssi_count = 0U;
|
||||||
|
slow_tx_count = 0U;
|
||||||
|
|
||||||
if (current_conn)
|
if (current_conn)
|
||||||
{
|
{
|
||||||
@@ -533,6 +710,7 @@ int ble_service_init(ble_data_rx_cb_t rx_cb)
|
|||||||
k_work_init_delayable(&adv_timeout_work, adv_timeout_handler);
|
k_work_init_delayable(&adv_timeout_work, adv_timeout_handler);
|
||||||
k_work_init_delayable(&conn_param_update_work, conn_param_update_handler);
|
k_work_init_delayable(&conn_param_update_work, conn_param_update_handler);
|
||||||
|
|
||||||
|
k_work_init_delayable(&tx_power_monitor_work, tx_power_monitor_handler);
|
||||||
/* Enable BLE stack */
|
/* Enable BLE stack */
|
||||||
err = bt_enable(NULL);
|
err = bt_enable(NULL);
|
||||||
if (err)
|
if (err)
|
||||||
@@ -666,6 +844,7 @@ bool ble_dfu_advertising_is_enabled(void)
|
|||||||
int ble_data_send(const uint8_t *data, uint16_t len)
|
int ble_data_send(const uint8_t *data, uint16_t len)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
int64_t tx_start_ms;
|
||||||
|
|
||||||
if (!current_conn)
|
if (!current_conn)
|
||||||
{
|
{
|
||||||
@@ -689,6 +868,7 @@ int ble_data_send(const uint8_t *data, uint16_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//DBG_CORE("[NUS TX] send len=%u\r\n", len);
|
//DBG_CORE("[NUS TX] send len=%u\r\n", len);
|
||||||
|
tx_start_ms = k_uptime_get();
|
||||||
for (int retry = 0; ; retry++)
|
for (int retry = 0; ; retry++)
|
||||||
{
|
{
|
||||||
err = bt_nus_send(current_conn, data, len);
|
err = bt_nus_send(current_conn, data, len);
|
||||||
@@ -701,6 +881,10 @@ int ble_data_send(const uint8_t *data, uint16_t len)
|
|||||||
* 바로 포기하지 않고 짧게 쉬었다가 다시 시도
|
* 바로 포기하지 않고 짧게 쉬었다가 다시 시도
|
||||||
*/
|
*/
|
||||||
DBG_ERR("[NUS TX] busy, retry=%d\r\n", retry + 1);
|
DBG_ERR("[NUS TX] busy, retry=%d\r\n", retry + 1);
|
||||||
|
if ((retry + 1) >= BLE_TX_BUSY_STRESS_RETRY)
|
||||||
|
{
|
||||||
|
ble_tx_power_stress("tx-queue-busy");
|
||||||
|
}
|
||||||
k_msleep(5);
|
k_msleep(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,11 +896,43 @@ int ble_data_send(const uint8_t *data, uint16_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* bt_nus_send() 성공 후 sent 콜백이 올 때까지 기다림 (다음 패킷을 보내도 되는 시점) */
|
/* bt_nus_send() 성공 후 sent 콜백이 올 때까지 기다림 (다음 패킷을 보내도 되는 시점) */
|
||||||
err = k_sem_take(&nus_tx_done_sem, K_MSEC(1000));
|
err = k_sem_take(&nus_tx_done_sem, K_MSEC(BLE_TX_STUCK_MS));
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
ble_tx_power_stress("tx-complete-stuck");
|
||||||
|
err = k_sem_take(&nus_tx_done_sem, K_MSEC(1000 - BLE_TX_STUCK_MS));
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
DBG_ERR("[NUS TX] completion timeout (err %d)\r\n", err);
|
DBG_ERR("[NUS TX] completion timeout (err %d)\r\n", err);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!err)
|
||||||
|
{
|
||||||
|
int64_t tx_elapsed_ms = k_uptime_get() - tx_start_ms;
|
||||||
|
|
||||||
|
if (tx_elapsed_ms >= BLE_TX_CRITICAL_MS)
|
||||||
|
{
|
||||||
|
ble_tx_power_stress("tx-complete-slow100");
|
||||||
|
slow_tx_count = 0U;
|
||||||
|
}
|
||||||
|
else if (tx_elapsed_ms >= BLE_TX_SLOW_MS)
|
||||||
|
{
|
||||||
|
if (slow_tx_count < UINT8_MAX)
|
||||||
|
{
|
||||||
|
slow_tx_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slow_tx_count >= BLE_TX_SLOW_COUNT)
|
||||||
|
{
|
||||||
|
ble_tx_power_stress("tx-complete-slow50");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slow_tx_count = 0U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
k_mutex_unlock(&nus_tx_lock);
|
k_mutex_unlock(&nus_tx_lock);
|
||||||
return err;
|
return err;
|
||||||
|
|||||||
@@ -27,8 +27,6 @@
|
|||||||
#define BLE_CONN_PARAM_RETRY_INTERVAL_MS 30000
|
#define BLE_CONN_PARAM_RETRY_INTERVAL_MS 30000
|
||||||
#define BLE_CONN_PARAM_MAX_ATTEMPTS 3
|
#define BLE_CONN_PARAM_MAX_ATTEMPTS 3
|
||||||
|
|
||||||
#define BLE_TX_POWER 8 /* +8 dBm */
|
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Callback types
|
* Callback types
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
|
CONFIG_BT_CTLR_TX_PWR_PLUS_4=y
|
||||||
|
CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y
|
||||||
CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
|
CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
|
||||||
|
|||||||
Reference in New Issue
Block a user