EMC RS 시험 대비 supervision timeout 및 동적 TX power 제어 추가
- supervision timeout을 4초에서 10초로 변경 - RSSI/RSSI silence/HVN 지연/TX queue 적체 기반 TX power boost 추가 - 정상 시 +4 dBm, link stress 시 +8 dBm으로 동적 제어
This commit is contained in:
@@ -142,11 +142,35 @@
|
|||||||
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_1_25_MS) /* Min connection interval: 15ms */
|
#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 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 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 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 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 */
|
#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
|
* 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_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_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_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
|
* 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_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 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 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 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 */
|
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 */
|
volatile bool data_tx_in_progress = false; /* Binary TX in progress flag */
|
||||||
|
|
||||||
/* -- BLE TX async retry state -- */
|
/* -- 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 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 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 */
|
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 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 t_power_off_timeout_handler(void * p_context); /* Power-off timeout */
|
||||||
static void PM_s(void * p_context); /* Peer Manager timer */
|
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
|
* @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_on_delay_timer: power button polling (5ms single-shot, main_s callback)
|
||||||
* - m_power_off_delay_timer: power-off delay (3s single-shot)
|
* - m_power_off_delay_timer: power-off delay (3s single-shot)
|
||||||
* - m_PM_timer: Peer Manager disconnect (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)
|
* - main_timer: main event loop (10ms single-shot, main_loop callback)
|
||||||
* - battery_timer: battery monitoring (5s repeating)
|
* - battery_timer: battery monitoring (5s repeating)
|
||||||
* - power_timer: power sequence (20ms single-shot, power_loop callback)
|
* - 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_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_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_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();
|
main_timer_init();
|
||||||
battery_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
|
* @brief Initialize GAP (Generic Access Profile) parameters
|
||||||
*
|
*
|
||||||
* 1) Set device name to SERIAL_NO -> shown during BLE scan
|
* 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)
|
* 3) Set static passkey (when FEATURE_STATIC_PASSKEY enabled)
|
||||||
*/
|
*/
|
||||||
static void gap_params_init(void)
|
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_buf[BLE_NUS_MAX_DATA_LEN] = {0};
|
||||||
static volatile uint8_t pending_cmd_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)
|
static bool ble_tx_pending_has_data(void)
|
||||||
{
|
{
|
||||||
return (s_tx_pending_count > 0);
|
return (s_tx_pending_count > 0);
|
||||||
@@ -625,6 +670,7 @@ static bool ble_retry_pending_tx(void)
|
|||||||
|
|
||||||
if (err == NRF_SUCCESS)
|
if (err == NRF_SUCCESS)
|
||||||
{
|
{
|
||||||
|
ble_link_stress_on_hvn_send();
|
||||||
ble_tx_pending_pop();
|
ble_tx_pending_pop();
|
||||||
freed_slot = true;
|
freed_slot = true;
|
||||||
continue;
|
continue;
|
||||||
@@ -1057,6 +1103,359 @@ static void peer_manager_init(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
* BLE Event Handler
|
||||||
* Handles all BLE events from the SoftDevice.
|
* Handles all BLE events from the SoftDevice.
|
||||||
@@ -1067,13 +1466,14 @@ static void peer_manager_init(void)
|
|||||||
*
|
*
|
||||||
* Key events handled:
|
* Key events handled:
|
||||||
* - DISCONNECTED: connection lost -> device sleep, state reset
|
* - 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
|
* - PHY_UPDATE_REQUEST: keep link on 1M PHY
|
||||||
|
* - RSSI_CHANGED: RSSI input for dynamic TX power boost/restore
|
||||||
* - TIMEOUT: connection/GATT timeout -> force disconnect
|
* - TIMEOUT: connection/GATT timeout -> force disconnect
|
||||||
* - SEC_PARAMS_REQUEST: security parameter request (reject if security unused)
|
* - SEC_PARAMS_REQUEST: security parameter request (reject if security unused)
|
||||||
* - PASSKEY_DISPLAY: display passkey (debug log)
|
* - PASSKEY_DISPLAY: display passkey (debug log)
|
||||||
* - AUTH_KEY_REQUEST: respond with static passkey
|
* - 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)
|
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",
|
DBG_PRINTF("[BLE] Disconnected (reason 0x%02X)%s\r\n",
|
||||||
disc_reason, unintended_disc ? " [UNINTENDED]" : "");
|
disc_reason, unintended_disc ? " [UNINTENDED]" : "");
|
||||||
|
|
||||||
|
ble_rssi_monitor_stop(); /* EMC: stop RSSI/HVN stress timer before invalidating handle */
|
||||||
|
|
||||||
ble_connection_st = 0;
|
ble_connection_st = 0;
|
||||||
pending_cmd_len = 0; // Clear pending command buffer
|
pending_cmd_len = 0; // Clear pending command buffer
|
||||||
m_conn_handle = BLE_CONN_HANDLE_INVALID;
|
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);
|
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
|
||||||
APP_ERROR_CHECK(err_code);
|
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 */
|
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;
|
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_GATTC_EVT_TIMEOUT:
|
||||||
case BLE_GATTS_EVT_TIMEOUT:
|
case BLE_GATTS_EVT_TIMEOUT:
|
||||||
DBG_PRINTF("[BLE] GATT Timeout -> disconnect\r\n");
|
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
|
#endif
|
||||||
|
|
||||||
case BLE_GATTS_EVT_HVN_TX_COMPLETE:
|
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_in_progress = false;
|
||||||
m_tx_complete_pending = false; /* Notify waiting functions of TX completion */
|
m_tx_complete_pending = false; /* Notify waiting functions of TX completion */
|
||||||
break;
|
break;
|
||||||
@@ -1424,11 +1831,11 @@ void data_tx_handler(char const *p_data_to_send)
|
|||||||
|
|
||||||
if (err_code == NRF_SUCCESS)
|
if (err_code == NRF_SUCCESS)
|
||||||
{
|
{
|
||||||
// OK
|
ble_link_stress_on_hvn_send(); /* EMC: start HVN latency stopwatch */
|
||||||
}
|
}
|
||||||
else if (err_code == NRF_ERROR_RESOURCES)
|
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)
|
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;
|
return NRF_ERROR_NO_MEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ble_link_stress_check_pending_depth();
|
||||||
return NRF_ERROR_RESOURCES;
|
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)
|
if (err_code == NRF_SUCCESS)
|
||||||
{
|
{
|
||||||
|
ble_link_stress_on_hvn_send(); /* EMC: start HVN latency stopwatch */
|
||||||
return NRF_SUCCESS;
|
return NRF_SUCCESS;
|
||||||
}
|
}
|
||||||
else if (err_code == NRF_ERROR_RESOURCES)
|
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;
|
return NRF_ERROR_NO_MEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ble_link_stress_check_pending_depth();
|
||||||
return NRF_ERROR_RESOURCES;
|
return NRF_ERROR_RESOURCES;
|
||||||
}
|
}
|
||||||
else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND)
|
else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND)
|
||||||
|
|||||||
Reference in New Issue
Block a user