BLE 연결 파라미터 기존과 동일하게 변경

This commit is contained in:
2026-06-19 18:12:58 +09:00
parent f68b3296ee
commit c4cc1205d6
3 changed files with 160 additions and 19 deletions
+149 -12
View File
@@ -4,8 +4,8 @@
*
* Original VesiScan-Basic BLE implementation ported to Zephyr/NCS:
* - NUS (Nordic UART Service) for data exchange
* - Advertising: 40ms interval, 3-min timeout
* - Connection: 30ms interval, 0 latency, 4s supervision timeout
* - Advertising: 40ms interval, 10-min timeout
* - Connection: 15ms interval, 0 latency, 10s supervision timeout
* - TX power +8 dBm, 2M PHY preferred
* - Dev mode: no security / Production mode: bonding + passkey (TODO)
******************************************************************************/
@@ -35,6 +35,9 @@ LOG_MODULE_REGISTER(ble_svc, LOG_LEVEL_INF);
static struct bt_conn *current_conn;
static ble_data_rx_cb_t rx_callback;
static bool dfu_advertising_mode;
static bool advertising_unlimited;
static uint8_t conn_param_update_attempts;
static int64_t last_rx_conn_param_update_ms;
#if IS_ENABLED(CONFIG_BT_SMP)
#define BLE_REQUIRED_SECURITY_LEVEL BT_SECURITY_L4
@@ -60,6 +63,8 @@ static struct k_sem nus_tx_done_sem;
/* BLE API는 ISR/콜백에서 직접 호출하면 안전하지 않을 수 있으므로 k_work 사용 */
static struct k_work adv_restart_work;
static struct k_work_delayable adv_timeout_work;
static struct k_work_delayable conn_param_update_work;
static const bt_addr_le_t fixed_identity_addr = {
.type = BT_ADDR_LE_RANDOM,
.a = {
@@ -271,6 +276,85 @@ static void adv_restart_handler(struct k_work *work)
led_set_state(LED_STATE_ADVERTISING);
}
static void adv_timeout_handler(struct k_work *work)
{
ARG_UNUSED(work);
if (!ble_connection_st && !advertising_unlimited && !dfu_advertising_mode)
{
DBG_PRINTF("[BLE] Advertising timeout\r\n");
(void)ble_advertising_stop();
}
}
static bool ble_conn_param_is_preferred(uint16_t interval, uint16_t latency,
uint16_t timeout)
{
return (interval == BLE_MIN_CONN_INTERVAL) &&
(latency == BLE_SLAVE_LATENCY) &&
(timeout == BLE_CONN_SUP_TIMEOUT);
}
static int ble_request_preferred_conn_params(bool count_attempt)
{
struct bt_le_conn_param conn_param =
{
.interval_min = BLE_MIN_CONN_INTERVAL,
.interval_max = BLE_MAX_CONN_INTERVAL,
.latency = BLE_SLAVE_LATENCY,
.timeout = BLE_CONN_SUP_TIMEOUT,
};
int err;
if (current_conn == NULL)
{
return -ENOTCONN;
}
if (count_attempt)
{
conn_param_update_attempts++;
}
DBG_PRINTF("[BLE] Request params: interval=%u latency=%u timeout=%u attempt=%u\r\n",
BLE_MIN_CONN_INTERVAL,
BLE_SLAVE_LATENCY,
BLE_CONN_SUP_TIMEOUT,
conn_param_update_attempts);
err = bt_conn_le_param_update(current_conn, &conn_param);
if (err)
{
DBG_ERR("[BLE] Param update failed err=%d -> disconnect\r\n", err);
(void)bt_conn_disconnect(current_conn, BT_HCI_ERR_UNACCEPT_CONN_PARAM);
return err;
}
return 0;
}
static void conn_param_update_handler(struct k_work *work)
{
ARG_UNUSED(work);
if (current_conn == NULL)
{
return;
}
if (conn_param_update_attempts >= BLE_CONN_PARAM_MAX_ATTEMPTS)
{
return;
}
if (ble_request_preferred_conn_params(true) == 0 &&
conn_param_update_attempts < BLE_CONN_PARAM_MAX_ATTEMPTS)
{
k_work_schedule(&conn_param_update_work,
K_MSEC(BLE_CONN_PARAM_RETRY_INTERVAL_MS));
}
}
/*==============================================================================
* Advertising data
*============================================================================*/
@@ -312,15 +396,10 @@ static void connected(struct bt_conn *conn, uint8_t err)
bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
DBG_CORE("[BLE] Peer: %s\r\n", addr_str);
/* Connection parameters (30ms interval) */
struct bt_le_conn_param conn_param =
{
.interval_min = BLE_MIN_CONN_INTERVAL,
.interval_max = BLE_MAX_CONN_INTERVAL,
.latency = BLE_SLAVE_LATENCY,
.timeout = BLE_CONN_SUP_TIMEOUT,
};
bt_conn_le_param_update(conn, &conn_param);
conn_param_update_attempts = 0U;
last_rx_conn_param_update_ms = 0;
k_work_schedule(&conn_param_update_work,
K_MSEC(BLE_CONN_PARAM_FIRST_DELAY_MS));
#if IS_ENABLED(CONFIG_BT_SMP)
err = bt_conn_set_security(conn, BLE_REQUIRED_SECURITY_LEVEL);
@@ -339,6 +418,10 @@ static void connected(struct bt_conn *conn, uint8_t err)
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
k_work_cancel_delayable(&conn_param_update_work);
conn_param_update_attempts = 0U;
last_rx_conn_param_update_ms = 0;
if (current_conn)
{
bt_conn_unref(current_conn);
@@ -346,17 +429,48 @@ static void disconnected(struct bt_conn *conn, uint8_t reason)
}
ble_connection_st = false;
advertising_unlimited = true;
DBG_CORE("[BLE] Disconnected (reason 0x%02x)\r\n", reason);
/* 워크큐에서 advertising 재시작 */
k_work_submit(&adv_restart_work);
}
static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
{
ARG_UNUSED(conn);
if (!ble_conn_param_is_preferred(param->interval_min,
param->latency,
param->timeout) ||
(param->interval_max != BLE_MAX_CONN_INTERVAL))
{
DBG_PRINTF("[BLE] Override requested params: min=%u max=%u latency=%u timeout=%u\r\n",
param->interval_min,
param->interval_max,
param->latency,
param->timeout);
param->interval_min = BLE_MIN_CONN_INTERVAL;
param->interval_max = BLE_MAX_CONN_INTERVAL;
param->latency = BLE_SLAVE_LATENCY;
param->timeout = BLE_CONN_SUP_TIMEOUT;
}
return true;
}
static void le_param_updated(struct bt_conn *conn, uint16_t interval,
uint16_t latency, uint16_t timeout)
{
ARG_UNUSED(conn);
DBG_PRINTF("[BLE] Params: interval=%d latency=%d timeout=%d\r\n",
interval, latency, timeout);
if (ble_conn_param_is_preferred(interval, latency, timeout))
{
k_work_cancel_delayable(&conn_param_update_work);
}
}
#if IS_ENABLED(CONFIG_BT_SMP)
@@ -379,6 +493,7 @@ BT_CONN_CB_DEFINE(conn_callbacks) =
{
.connected = connected,
.disconnected = disconnected,
.le_param_req = le_param_req,
.le_param_updated = le_param_updated,
#if IS_ENABLED(CONFIG_BT_SMP)
.security_changed = security_changed,
@@ -401,6 +516,19 @@ static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len
#endif
DBG_CORE("[NUS RX] len=%u\r\n", len);
if (current_conn == conn)
{
int64_t now_ms = k_uptime_get();
if ((last_rx_conn_param_update_ms == 0) ||
((now_ms - last_rx_conn_param_update_ms) >= BLE_CONN_PARAM_RETRY_INTERVAL_MS))
{
last_rx_conn_param_update_ms = now_ms;
(void)ble_request_preferred_conn_params(false);
}
}
if (rx_callback)
{
rx_callback(data, len);
@@ -433,6 +561,8 @@ int ble_service_init(ble_data_rx_cb_t rx_cb)
k_sem_init(&nus_tx_done_sem, 0, 1);
k_work_init(&adv_restart_work, adv_restart_handler);
k_work_init_delayable(&adv_timeout_work, adv_timeout_handler);
k_work_init_delayable(&conn_param_update_work, conn_param_update_handler);
err = ble_configure_fixed_identity();
if (err)
@@ -490,7 +620,7 @@ int ble_advertising_start(void)
struct bt_le_adv_param adv_param = BT_LE_ADV_PARAM_INIT(
BT_LE_ADV_OPT_CONN,
APP_ADV_INTERVAL, /* min interval */
APP_ADV_INTERVAL + 16, /* max interval (slight window) */
APP_ADV_INTERVAL, /* max interval */
NULL /* undirected */
);
@@ -501,6 +631,12 @@ int ble_advertising_start(void)
return err;
}
k_work_cancel_delayable(&adv_timeout_work);
if (!advertising_unlimited && !dfu_advertising_mode)
{
k_work_schedule(&adv_timeout_work, K_MSEC(APP_ADV_DURATION * 10));
}
DBG_PRINTF("[BLE] Advertising started (%s)\r\n", dfu_advertising_mode ? "SMP" : "NUS");
return 0;
}
@@ -514,6 +650,7 @@ int ble_advertising_stop(void)
return err;
}
k_work_cancel_delayable(&adv_timeout_work);
DBG_PRINTF("[BLE] Advertising stopped\r\n");
return 0;
}