From 26c6d035f0dc0a630e22657657f778dbb98b48ba Mon Sep 17 00:00:00 2001 From: jhchun Date: Mon, 15 Jun 2026 15:39:41 +0900 Subject: [PATCH] =?UTF-8?q?EMC=20RS=20=EC=8B=9C=ED=97=98=20=EB=8C=80?= =?UTF-8?q?=EB=B9=84=20supervision=20timeout=20=EB=B0=8F=20=EB=8F=99?= =?UTF-8?q?=EC=A0=81=20TX=20power=20=EC=A0=9C=EC=96=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - supervision timeout을 4초에서 10초로 변경 - RSSI/RSSI silence/HVN 지연/TX queue 적체 기반 TX power boost 추가 - 정상 시 +4 dBm, link stress 시 +8 dBm으로 동적 제어 --- .../ble_app_bladder_patch/main.c | 426 +++++++++++++++++- 1 file changed, 418 insertions(+), 8 deletions(-) diff --git a/project/ble_peripheral/ble_app_bladder_patch/main.c b/project/ble_peripheral/ble_app_bladder_patch/main.c index eedac3d..5d7f33a 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/main.c +++ b/project/ble_peripheral/ble_app_bladder_patch/main.c @@ -142,11 +142,35 @@ #define MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_1_25_MS) /* Min connection interval: 15ms */ #define MAX_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_1_25_MS) /* Max connection interval: 15ms */ #define SLAVE_LATENCY 0 /* Slave latency: 0 (respond every connection event) */ -#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /* Connection supervision timeout: 4s */ +#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(10000, UNIT_10_MS) /* EMC: extend supervision timeout 4s -> 10s to tolerate short RF fades */ #define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /* Wait 5s before first param update request */ #define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /* Subsequent param update interval: 30s */ #define MAX_CONN_PARAMS_UPDATE_COUNT 3 /* Max param update attempts */ +/* EMC BLE link protection: + * - Start each connection at normal +4 dBm. + * - Boost to +8 dBm when RSSI is repeatedly poor, RSSI events stop, HVN complete is slow/stuck, + * or the NUS pending queue backs up. + * - Restore to +4 dBm only after RSSI is stably good and TX is idle. + */ +#define BLE_TX_POWER_NORMAL_DBM 4 /* Normal connected TX power */ +#define BLE_TX_POWER_BOOST_DBM 8 /* Temporary TX power boost during link stress */ +#define BLE_TX_POWER_UNKNOWN_DBM 127 /* Sentinel to force first SoftDevice TX power apply */ +#define BLE_RSSI_BAD_DBM (-80) /* Weak central signal threshold: count toward boost */ +#define BLE_RSSI_GOOD_DBM (-65) /* Good central signal threshold: count toward restore */ +#define BLE_RSSI_BAD_COUNT_LIMIT 3 /* Poor RSSI events needed before RSSI-based boost */ +#define BLE_RSSI_GOOD_COUNT_LIMIT 10 /* Good RSSI events needed before restore */ +#define BLE_RSSI_CHANGE_THRESHOLD_DBM 1 /* Request RSSI event on >=1 dB change */ +#define BLE_RSSI_SKIP_COUNT 0 /* Report RSSI immediately; useful during EMC validation */ + +#define BLE_LINK_STRESS_TICK_MS 200 /* Periodic link-health poll interval */ +#define BLE_LINK_STRESS_RSSI_SILENCE_MS 500 /* No RSSI event for this long -> suspect downlink stress */ +#define BLE_LINK_STRESS_HVN_SLOW_MS 50 /* HVN complete slower than this counts as TX stress */ +#define BLE_LINK_STRESS_HVN_VERY_SLOW_MS 100 /* HVN complete slower than this boosts immediately */ +#define BLE_LINK_STRESS_HVN_STUCK_MS 200 /* No HVN complete after submit -> boost immediately */ +#define BLE_LINK_STRESS_BOOST_COUNT 3 /* Consecutive slow HVN events before boost */ +#define BLE_LINK_STRESS_RECOVERY_MS 10000 /* Clear stress counter after quiet recovery period */ + /*============================================================================== * System Constants *============================================================================*/ @@ -172,6 +196,7 @@ BLE_ADVERTISING_DEF(m_advertising); /* Advertising module instanc APP_TIMER_DEF(m_power_on_delay_timer_id); /* Power button polling timer (5ms single-shot, main_s callback) */ APP_TIMER_DEF(m_power_off_delay_timer_id); /* Power-off delay timer (physical power cut after 3s) */ APP_TIMER_DEF(m_PM_timer_id); /* Peer Manager timer (forced disconnect) */ +APP_TIMER_DEF(m_link_stress_timer_id); /* EMC: BLE link stress monitor (periodic RSSI/HVN watchdog) */ /*============================================================================== * Static Variables @@ -183,6 +208,17 @@ static pm_peer_id_t m_peer_to_be_deleted = PM_PEER_ID_INVALID; static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /* Current BLE connection handle */ static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3; /* NUS max data length (MTU - overhead) */ static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}}; /* UUIDs included in advertising */ +static int8_t m_ble_tx_power_dbm = BLE_TX_POWER_UNKNOWN_DBM; /* Currently applied connected TX power */ +static uint8_t m_rssi_bad_count = 0; /* Consecutive poor RSSI event count */ +static uint8_t m_rssi_good_count = 0; /* Consecutive good RSSI event count */ +static uint8_t m_link_stress_count = 0; /* Consecutive slow-HVN stress count */ +static uint32_t m_last_rssi_tick = 0; /* Last RSSI event tick for silence detection */ +static uint32_t m_last_stress_tick = 0; /* Last stress tick for recovery reset */ +static uint32_t m_hvn_send_tick = 0; /* Last successful notification submit tick */ +static bool m_hvn_send_pending = false; /* True until BLE_GATTS_EVT_HVN_TX_COMPLETE */ +static bool m_rssi_silence_latched = false; /* Prevent repeated RSSI silence logs/boosts */ +static bool m_rssi_bad_latched = false; /* Prevent repeated RSSI_BAD logs/boosts */ +static bool m_hvn_stuck_latched = false; /* Prevent repeated HVN_STUCK logs/boosts */ static uint8_t m_tx_buffer[BLE_NUS_MAX_DATA_LEN] = {0}; /* ASCII text transmission buffer */ static uint16_t m_tx_len = 0; /* Data length to transmit */ @@ -228,7 +264,8 @@ volatile bool ble_connection_st; /* BLE connection state (1=co volatile bool data_tx_in_progress = false; /* Binary TX in progress flag */ /* -- BLE TX async retry state -- */ -#define BLE_TX_PENDING_QUEUE_SIZE 8U +#define BLE_TX_PENDING_QUEUE_SIZE 8U +#define BLE_TX_PENDING_BOOST_THRESHOLD 4U /* EMC: boost TX power if queued packets accumulate */ static uint8_t s_tx_pending_buf[BLE_TX_PENDING_QUEUE_SIZE][BLE_NUS_MAX_DATA_LEN] = {{0}}; /* Pending packets (with CRC) */ static uint16_t s_tx_pending_len[BLE_TX_PENDING_QUEUE_SIZE] = {0}; /* Pending packet lengths */ static volatile uint8_t s_tx_pending_head = 0; /* Next packet to retry */ @@ -394,6 +431,7 @@ static void load_flash_config(void) static void main_s(void * p_context); /* Power button state machine (forward declaration) */ static void t_power_off_timeout_handler(void * p_context); /* Power-off timeout */ static void PM_s(void * p_context); /* Peer Manager timer */ +static void ble_link_stress_tick_handler(void * p_context); /* EMC: BLE RSSI/HVN link stress timer */ /** * @brief Power-off timeout callback @@ -440,6 +478,7 @@ static void PM_s(void * p_context) * - m_power_on_delay_timer: power button polling (5ms single-shot, main_s callback) * - m_power_off_delay_timer: power-off delay (3s single-shot) * - m_PM_timer: Peer Manager disconnect (single-shot) + * - m_link_stress_timer: EMC BLE link monitor (RSSI silence + HVN latency) * - main_timer: main event loop (10ms single-shot, main_loop callback) * - battery_timer: battery monitoring (5s repeating) * - power_timer: power sequence (20ms single-shot, power_loop callback) @@ -452,6 +491,7 @@ static void timers_init(void) APP_ERROR_CHECK(app_timer_create(&m_power_on_delay_timer_id, APP_TIMER_MODE_SINGLE_SHOT, main_s)); APP_ERROR_CHECK(app_timer_create(&m_power_off_delay_timer_id, APP_TIMER_MODE_SINGLE_SHOT, t_power_off_timeout_handler)); APP_ERROR_CHECK(app_timer_create(&m_PM_timer_id, APP_TIMER_MODE_SINGLE_SHOT, PM_s)); + APP_ERROR_CHECK(app_timer_create(&m_link_stress_timer_id, APP_TIMER_MODE_REPEATED, ble_link_stress_tick_handler)); main_timer_init(); battery_timer_init(); @@ -512,7 +552,7 @@ static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event) * @brief Initialize GAP (Generic Access Profile) parameters * * 1) Set device name to SERIAL_NO -> shown during BLE scan - * 2) Configure connection parameters (20~75ms interval, slave latency 0, supervision timeout 4s) + * 2) Configure connection parameters (15ms interval, slave latency 0, EMC supervision timeout 10s) * 3) Set static passkey (when FEATURE_STATIC_PASSKEY enabled) */ static void gap_params_init(void) @@ -564,6 +604,11 @@ extern void maa_async_abort(void); static volatile uint8_t pending_cmd_buf[BLE_NUS_MAX_DATA_LEN] = {0}; static volatile uint8_t pending_cmd_len = 0; +static void ble_link_stress_on_hvn_send(void); +static void ble_link_stress_on_hvn_complete(void); +static void ble_link_stress_boost_now(const char * p_reason, uint32_t elapsed_ms); +static void ble_link_stress_check_pending_depth(void); + static bool ble_tx_pending_has_data(void) { return (s_tx_pending_count > 0); @@ -625,6 +670,7 @@ static bool ble_retry_pending_tx(void) if (err == NRF_SUCCESS) { + ble_link_stress_on_hvn_send(); ble_tx_pending_pop(); freed_slot = true; continue; @@ -1057,6 +1103,359 @@ static void peer_manager_init(void) } #endif +/*============================================================================== + * BLE Link Stress Monitor + Dynamic TX Power Control (EMC) + * + * EMC noise can make the BLE link degrade before the connection actually drops. + * This monitor watches both directions: + * - RSSI events from the central side indicate downlink quality. + * - HVN TX complete latency and NUS pending depth indicate uplink congestion. + * + * Policy: + * 1. Use +4 dBm normally to keep RF output modest. + * 2. Boost to +8 dBm when the link shows repeated or immediate stress. + * 3. Restore to +4 dBm only after RSSI is stably good and TX is fully idle. + *============================================================================*/ + +static void ble_tx_power_set_dynamic(int8_t tx_power_dbm); + +static uint32_t ble_link_stress_elapsed_ms(uint32_t start_tick, uint32_t end_tick) +{ + return (uint32_t)((app_timer_cnt_diff_compute(end_tick, start_tick) * 1000ULL) / APP_TIMER_CLOCK_FREQ); +} + +static bool ble_link_stress_rssi_recent(void) +{ + if (m_last_rssi_tick == 0) + { + return false; + } + + return (app_timer_cnt_diff_compute(app_timer_cnt_get(), m_last_rssi_tick) < + APP_TIMER_TICKS(BLE_LINK_STRESS_RSSI_SILENCE_MS)); +} + +static void ble_link_stress_try_restore(void) +{ + /* Restore only when every stress source is quiet; this prevents power flapping during EMC bursts. */ + if (m_ble_tx_power_dbm == BLE_TX_POWER_BOOST_DBM && + m_link_stress_count == 0 && + m_rssi_good_count >= BLE_RSSI_GOOD_COUNT_LIMIT && + !m_rssi_bad_latched && + !m_rssi_silence_latched && + ble_link_stress_rssi_recent() && + !ble_tx_pending_has_data() && + !m_tx_in_progress && + !m_hvn_send_pending) + { + ble_tx_power_set_dynamic(BLE_TX_POWER_NORMAL_DBM); + } +} + +static void ble_link_stress_boost_now(const char * p_reason, uint32_t elapsed_ms) +{ + m_last_stress_tick = app_timer_cnt_get(); + UNUSED_PARAMETER(p_reason); + + if (elapsed_ms > 0) + { + /* EMC RTT log disabled: DBG_PRINTF("[BLE] stress: %s %lums (boost now)\r\n", p_reason, (unsigned long)elapsed_ms); */ + } + else + { + /* EMC RTT log disabled: DBG_PRINTF("[BLE] stress: %s (boost now)\r\n", p_reason); */ + } + + ble_tx_power_set_dynamic(BLE_TX_POWER_BOOST_DBM); +} + +static void ble_link_stress_check_pending_depth(void) +{ + /* Queue growth means SoftDevice/NUS cannot accept packets fast enough; treat it as uplink stress. */ + if (s_tx_pending_count >= BLE_TX_PENDING_BOOST_THRESHOLD) + { + ble_link_stress_boost_now("TX_QUEUE_DEPTH", 0); + } +} + +static void ble_link_stress_bump(const char * p_reason, uint32_t elapsed_ms) +{ + m_last_stress_tick = app_timer_cnt_get(); + UNUSED_PARAMETER(p_reason); + UNUSED_PARAMETER(elapsed_ms); + + if (m_link_stress_count < BLE_LINK_STRESS_BOOST_COUNT) + { + m_link_stress_count++; + } + + /* EMC RTT log disabled: DBG_PRINTF("[BLE] stress: %s %lums (count=%u/%u)\r\n", + p_reason, (unsigned long)elapsed_ms, m_link_stress_count, BLE_LINK_STRESS_BOOST_COUNT); */ + + if (m_link_stress_count >= BLE_LINK_STRESS_BOOST_COUNT) + { + ble_tx_power_set_dynamic(BLE_TX_POWER_BOOST_DBM); + } +} + +static void ble_link_stress_reset(void) +{ + m_link_stress_count = 0; + m_last_rssi_tick = 0; + m_last_stress_tick = 0; + m_hvn_send_tick = 0; + m_hvn_send_pending = false; + m_rssi_silence_latched = false; + m_hvn_stuck_latched = false; +} + +static void ble_link_stress_on_hvn_send(void) +{ + m_hvn_send_tick = app_timer_cnt_get(); + m_hvn_send_pending = true; + m_hvn_stuck_latched = false; +} + +static void ble_link_stress_on_hvn_complete(void) +{ + uint32_t now_tick; + uint32_t elapsed_ms; + + if (!m_hvn_send_pending) + { + return; + } + + now_tick = app_timer_cnt_get(); + elapsed_ms = ble_link_stress_elapsed_ms(m_hvn_send_tick, now_tick); + m_hvn_send_pending = false; + m_hvn_stuck_latched = false; + + if (elapsed_ms >= BLE_LINK_STRESS_HVN_VERY_SLOW_MS) + { + ble_link_stress_boost_now("HVN_VERY_SLOW", elapsed_ms); + } + else if (elapsed_ms >= BLE_LINK_STRESS_HVN_SLOW_MS) + { + ble_link_stress_bump("HVN_SLOW", elapsed_ms); + } + else + { + m_link_stress_count = 0; + } +} + +static void ble_link_stress_tick_handler(void * p_context) +{ + uint32_t now_tick; + + UNUSED_PARAMETER(p_context); + + if (m_conn_handle == BLE_CONN_HANDLE_INVALID || ble_connection_st != BLE_CONNECTED_ST) + { + return; + } + + now_tick = app_timer_cnt_get(); + + /* If RSSI events disappear, assume the central/downlink side is being disturbed. */ + if (m_last_rssi_tick != 0 && + app_timer_cnt_diff_compute(now_tick, m_last_rssi_tick) >= APP_TIMER_TICKS(BLE_LINK_STRESS_RSSI_SILENCE_MS)) + { + if (!m_rssi_silence_latched) + { + m_rssi_silence_latched = true; + ble_link_stress_boost_now("RSSI_SILENCE", 0); + } + } + + /* If a notification was submitted but TX complete does not arrive, boost immediately. */ + if (m_hvn_send_pending && + app_timer_cnt_diff_compute(now_tick, m_hvn_send_tick) >= APP_TIMER_TICKS(BLE_LINK_STRESS_HVN_STUCK_MS)) + { + if (!m_hvn_stuck_latched) + { + uint32_t elapsed_ms = ble_link_stress_elapsed_ms(m_hvn_send_tick, now_tick); + + m_hvn_stuck_latched = true; + ble_link_stress_boost_now("HVN_STUCK", elapsed_ms); + } + } + else if (m_tx_in_progress && + m_hvn_send_tick != 0 && + app_timer_cnt_diff_compute(now_tick, m_hvn_send_tick) >= APP_TIMER_TICKS(BLE_LINK_STRESS_HVN_STUCK_MS)) + { + if (!m_hvn_stuck_latched) + { + uint32_t elapsed_ms = ble_link_stress_elapsed_ms(m_hvn_send_tick, now_tick); + + m_hvn_stuck_latched = true; + ble_link_stress_boost_now("HVN_STUCK", elapsed_ms); + } + } + + if (m_link_stress_count > 0 && + m_last_stress_tick != 0 && + app_timer_cnt_diff_compute(now_tick, m_last_stress_tick) >= APP_TIMER_TICKS(BLE_LINK_STRESS_RECOVERY_MS)) + { + m_link_stress_count = 0; + /* EMC RTT log disabled: DBG_PRINTF("[BLE] stress: recovered (count=0)\r\n"); */ + ble_link_stress_try_restore(); + } +} + +static void ble_link_stress_start(void) +{ + ret_code_t err_code; + + ble_link_stress_reset(); + m_last_rssi_tick = app_timer_cnt_get(); + + err_code = app_timer_start(m_link_stress_timer_id, APP_TIMER_TICKS(BLE_LINK_STRESS_TICK_MS), NULL); + if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE) + { + APP_ERROR_CHECK(err_code); + } +} + +static void ble_link_stress_stop(void) +{ + (void)app_timer_stop(m_link_stress_timer_id); + ble_link_stress_reset(); +} + +static void ble_tx_power_set_dynamic(int8_t tx_power_dbm) +{ + ret_code_t err_code; + int8_t prev_tx_power_dbm = m_ble_tx_power_dbm; + + if (m_conn_handle == BLE_CONN_HANDLE_INVALID || m_ble_tx_power_dbm == tx_power_dbm) + { + return; + } + + err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, + m_conn_handle, + tx_power_dbm); + if (err_code == NRF_SUCCESS) + { + m_ble_tx_power_dbm = tx_power_dbm; + if (tx_power_dbm == BLE_TX_POWER_BOOST_DBM) + { + m_rssi_good_count = 0; + } + + if (prev_tx_power_dbm == BLE_TX_POWER_UNKNOWN_DBM) + { + /* EMC RTT log disabled: DBG_PRINTF("[BLE] TX power -> %d dBm\r\n", tx_power_dbm); */ + } + else + { + /* EMC RTT log disabled: DBG_PRINTF("[BLE] TX power %d -> %d dBm\r\n", prev_tx_power_dbm, tx_power_dbm); */ + } + } + else if (err_code != NRF_ERROR_INVALID_STATE && err_code != BLE_ERROR_INVALID_CONN_HANDLE) + { + APP_ERROR_CHECK(err_code); + } +} + +static void ble_rssi_state_reset(void) +{ + m_rssi_bad_count = 0; + m_rssi_good_count = 0; + m_rssi_bad_latched = false; +} + +static void ble_rssi_monitor_start(void) +{ + ret_code_t err_code; + + if (m_conn_handle == BLE_CONN_HANDLE_INVALID) + { + return; + } + + ble_rssi_state_reset(); + m_ble_tx_power_dbm = BLE_TX_POWER_UNKNOWN_DBM; + + /* Apply normal power first; stress monitor will boost only when needed. */ + ble_tx_power_set_dynamic(BLE_TX_POWER_NORMAL_DBM); + ble_link_stress_start(); + + err_code = sd_ble_gap_rssi_start(m_conn_handle, + BLE_RSSI_CHANGE_THRESHOLD_DBM, + BLE_RSSI_SKIP_COUNT); + if (err_code == NRF_SUCCESS) + { + /* EMC RTT log disabled: DBG_PRINTF("[BLE] RSSI monitor start (threshold=%d, skip=%d)\r\n", + BLE_RSSI_CHANGE_THRESHOLD_DBM, BLE_RSSI_SKIP_COUNT); */ + } + else if (err_code != NRF_ERROR_INVALID_STATE) + { + APP_ERROR_CHECK(err_code); + } + else + { + /* EMC RTT log disabled: DBG_PRINTF("[BLE] RSSI monitor already active\r\n"); */ + } +} + +static void ble_rssi_monitor_stop(void) +{ + if (m_conn_handle != BLE_CONN_HANDLE_INVALID) + { + (void)sd_ble_gap_rssi_stop(m_conn_handle); + } + + ble_link_stress_stop(); + ble_rssi_state_reset(); + m_ble_tx_power_dbm = BLE_TX_POWER_UNKNOWN_DBM; +} + +static void ble_rssi_update(int8_t rssi_dbm) +{ + m_last_rssi_tick = app_timer_cnt_get(); + + if (rssi_dbm <= BLE_RSSI_BAD_DBM) + { + if (m_rssi_bad_count < BLE_RSSI_BAD_COUNT_LIMIT) + { + m_rssi_bad_count++; + } + m_rssi_good_count = 0; + + if (m_rssi_bad_count >= BLE_RSSI_BAD_COUNT_LIMIT && !m_rssi_bad_latched) + { + m_rssi_bad_latched = true; + /* EMC RTT log disabled: DBG_PRINTF("[BLE] stress: RSSI_BAD %d dBm (count=%u/%u, boost now)\r\n", + rssi_dbm, m_rssi_bad_count, BLE_RSSI_BAD_COUNT_LIMIT); */ + ble_tx_power_set_dynamic(BLE_TX_POWER_BOOST_DBM); + } + } + else if (rssi_dbm >= BLE_RSSI_GOOD_DBM) + { + m_rssi_silence_latched = false; + + if (m_rssi_good_count < BLE_RSSI_GOOD_COUNT_LIMIT) + { + m_rssi_good_count++; + } + m_rssi_bad_count = 0; + m_rssi_bad_latched = false; + + if (m_rssi_good_count >= BLE_RSSI_GOOD_COUNT_LIMIT) + { + ble_link_stress_try_restore(); + } + } + else + { + m_rssi_bad_count = 0; + m_rssi_good_count = 0; + } +} + /*============================================================================== * BLE Event Handler * Handles all BLE events from the SoftDevice. @@ -1067,13 +1466,14 @@ static void peer_manager_init(void) * * Key events handled: * - DISCONNECTED: connection lost -> device sleep, state reset - * - CONNECTED: connection established -> assign QWR handle, set TX power +8dBm + * - CONNECTED: connection established -> assign QWR handle, start EMC link monitor * - PHY_UPDATE_REQUEST: keep link on 1M PHY + * - RSSI_CHANGED: RSSI input for dynamic TX power boost/restore * - TIMEOUT: connection/GATT timeout -> force disconnect * - SEC_PARAMS_REQUEST: security parameter request (reject if security unused) * - PASSKEY_DISPLAY: display passkey (debug log) * - AUTH_KEY_REQUEST: respond with static passkey - * - HVN_TX_COMPLETE: TX complete -> clear transmission flag + * - HVN_TX_COMPLETE: TX complete -> clear transmission flag and measure HVN latency */ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) { @@ -1109,6 +1509,8 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) DBG_PRINTF("[BLE] Disconnected (reason 0x%02X)%s\r\n", disc_reason, unintended_disc ? " [UNINTENDED]" : ""); + ble_rssi_monitor_stop(); /* EMC: stop RSSI/HVN stress timer before invalidating handle */ + ble_connection_st = 0; pending_cmd_len = 0; // Clear pending command buffer m_conn_handle = BLE_CONN_HANDLE_INVALID; @@ -1152,7 +1554,7 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle); APP_ERROR_CHECK(err_code); - sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_conn_handle, 4); + ble_rssi_monitor_start(); /* EMC: apply +4 dBm normal power and start dynamic boost monitor */ led_set_state(LED_STATE_OFF); /* Connection complete -> LED OFF */ @@ -1170,6 +1572,10 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) } break; + case BLE_GAP_EVT_RSSI_CHANGED: + ble_rssi_update(p_ble_evt->evt.gap_evt.params.rssi_changed.rssi); + break; + case BLE_GATTC_EVT_TIMEOUT: case BLE_GATTS_EVT_TIMEOUT: DBG_PRINTF("[BLE] GATT Timeout -> disconnect\r\n"); @@ -1222,6 +1628,7 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) #endif case BLE_GATTS_EVT_HVN_TX_COMPLETE: + ble_link_stress_on_hvn_complete(); /* EMC: HVN latency feeds dynamic TX power control */ m_tx_in_progress = false; m_tx_complete_pending = false; /* Notify waiting functions of TX completion */ break; @@ -1424,11 +1831,11 @@ void data_tx_handler(char const *p_data_to_send) if (err_code == NRF_SUCCESS) { - // OK + ble_link_stress_on_hvn_send(); /* EMC: start HVN latency stopwatch */ } else if (err_code == NRF_ERROR_RESOURCES) { - // Retry later + /* Retry later; pending queue depth will trigger boost if congestion persists. */ } else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND) { @@ -1607,6 +2014,7 @@ uint32_t dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length) return NRF_ERROR_NO_MEM; } + ble_link_stress_check_pending_depth(); return NRF_ERROR_RESOURCES; } @@ -1615,6 +2023,7 @@ uint32_t dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length) if (err_code == NRF_SUCCESS) { + ble_link_stress_on_hvn_send(); /* EMC: start HVN latency stopwatch */ return NRF_SUCCESS; } else if (err_code == NRF_ERROR_RESOURCES) @@ -1625,6 +2034,7 @@ uint32_t dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length) return NRF_ERROR_NO_MEM; } + ble_link_stress_check_pending_depth(); return NRF_ERROR_RESOURCES; } else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND)