diff --git a/boards/nrf52840dk_nrf52840.conf b/boards/nrf52840dk_nrf52840.conf index d3f2f09..6c8b3b2 100644 --- a/boards/nrf52840dk_nrf52840.conf +++ b/boards/nrf52840dk_nrf52840.conf @@ -1,2 +1,3 @@ CONFIG_BT_CTLR_TX_PWR_PLUS_8=y CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y +CONFIG_BT_CTLR_CONN_RSSI=y diff --git a/src/ble/ble_service.c b/src/ble/ble_service.c index d40bcb8..0f16406 100644 --- a/src/ble/ble_service.c +++ b/src/ble/ble_service.c @@ -50,6 +50,7 @@ static int64_t last_rx_conn_param_update_ms; #define BLE_TX_STUCK_MS 200 #define BLE_TX_SLOW_COUNT 3 #define BLE_TX_BUSY_STRESS_RETRY 4 +#define BLE_TX_RECOVER_HOLD_MS 10000 // 마지막 트리거 이후 이 시간 동안 트리거 없는 경우 TX power 복귀 extern int bt_hci_get_conn_handle(const struct bt_conn *conn, uint16_t *conn_handle); @@ -58,6 +59,7 @@ 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 int64_t last_stress_ms; static struct k_work_delayable tx_power_monitor_work; #if IS_ENABLED(CONFIG_BT_SMP) @@ -137,9 +139,64 @@ static int ble_set_conn_tx_power(int8_t dbm, const char *reason) static void ble_tx_power_stress(const char *reason) { + last_stress_ms = k_uptime_get(); // stress 발생 시각 기록 (복귀 판정 기준) (void)ble_set_conn_tx_power(BLE_TX_POWER_STRESS_DBM, reason); } +// 마지막 stress 이후 충분히 조용하면(BLE_TX_RECOVER_HOLD_MS) NORMAL(4)로 복귀 +static void ble_tx_power_try_recover(void) +{ + if ((current_tx_power_dbm != BLE_TX_POWER_NORMAL_DBM) && + ((k_uptime_get() - last_stress_ms) >= BLE_TX_RECOVER_HOLD_MS)) + { + (void)ble_set_conn_tx_power(BLE_TX_POWER_NORMAL_DBM, "link-recovered"); + } +} + +// 컨트롤러에 설정된 현재 연결 TX power를 조회 (실제 적용값 확인용) +static int ble_read_conn_tx_power(int8_t *dbm) +{ + struct net_buf *buf; + struct net_buf *rsp; + struct bt_hci_cp_vs_read_tx_power_level *cp; + struct bt_hci_rp_vs_read_tx_power_level *rp; + int err; + + if (!current_conn_handle_valid || dbm == 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_type = BT_HCI_VS_LL_HANDLE_TYPE_CONN; + cp->handle = sys_cpu_to_le16(current_conn_handle); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_TX_POWER_LEVEL, buf, &rsp); + if (err) + { + return err; + } + + rp = (void *)rsp->data; + if (rp->status != 0U) + { + err = -EIO; + } + else + { + *dbm = rp->tx_power_level; + } + + net_buf_unref(rsp); + return err; +} + static int ble_read_conn_rssi(int8_t *rssi) { struct net_buf *buf; @@ -221,8 +278,10 @@ static void tx_power_monitor_handler(struct k_work *work) low_rssi_count = 0U; } - k_work_schedule(&tx_power_monitor_work, - K_MSEC(BLE_RSSI_MONITOR_INTERVAL_MS)); + // 전송이 없어도 주기적으로 복귀 판정 (마지막 stress 후 충분히 조용하면 NORMAL) + ble_tx_power_try_recover(); + + k_work_schedule(&tx_power_monitor_work, K_MSEC(BLE_RSSI_MONITOR_INTERVAL_MS)); } static void ble_log_local_identity(void) @@ -523,7 +582,7 @@ static const struct bt_data sd_dfu[] = BT_DATA_BYTES(BT_DATA_UUID128_SOME, SMP_BT_SVC_UUID_VAL), }; -/* Connection callbacks */ +/* BLE TX Power Control */ static void connected(struct bt_conn *conn, uint8_t err) { if (err) @@ -539,7 +598,25 @@ static void connected(struct bt_conn *conn, uint8_t err) 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"); + last_stress_ms = k_uptime_get(); + + // 컨트롤러 기본 출력이 최대(+8)이므로, 연결 시 NORMAL(4)로 강제 적용 + if (current_conn_handle_valid) + { + (void)ble_tx_power_write(BT_HCI_VS_LL_HANDLE_TYPE_CONN, current_conn_handle, BLE_TX_POWER_NORMAL_DBM, "connected"); + } + + int8_t cur_txp = 0; + int txp_ret = ble_read_conn_tx_power(&cur_txp); + if (txp_ret == 0) + { + DBG_CORE("[BLE] TX power (current) = %d dBm\r\n", cur_txp); + } + else + { + DBG_CORE("[BLE] TX power read fail ret=%d\r\n", txp_ret); + } + if (BLE_RSSI_MONITOR_ENABLED) { k_work_schedule(&tx_power_monitor_work, K_MSEC(BLE_RSSI_MONITOR_INTERVAL_MS)); @@ -579,6 +656,7 @@ static void disconnected(struct bt_conn *conn, uint8_t reason) current_tx_power_dbm = BLE_TX_POWER_NORMAL_DBM; low_rssi_count = 0U; slow_tx_count = 0U; + last_stress_ms = 0; if (current_conn) { @@ -792,6 +870,9 @@ int ble_advertising_start(void) return err; } + // 컨트롤러 기본 출력이 최대(+8)이므로, 광고는 항상 NORMAL(4)로 고정 (stress 무관) + (void)ble_tx_power_write(BT_HCI_VS_LL_HANDLE_TYPE_ADV, 0, BLE_TX_POWER_NORMAL_DBM, "adv"); + k_work_cancel_delayable(&adv_timeout_work); if (!advertising_unlimited) // DFU 광고 포함, 미연결 10분 시 전원 OFF { @@ -950,6 +1031,9 @@ int ble_data_send(const uint8_t *data, uint16_t len) else { slow_tx_count = 0U; + + // 정상 전송 완료 → 마지막 stress 이후 충분히 조용했으면 복귀 + ble_tx_power_try_recover(); } }