Compare commits

..

9 Commits

Author SHA1 Message Date
jhChun 3ec6afec36 MUX 전환 후 ADC S/H 커패시터 안정화를 위한 dummy read 2026-04-07 14:17:06 +09:00
jhChun 6cb927e831 DEBUG 빌드 해제
- 에러 처리 시 먹통 현상 = DEBUG 빌드
2026-04-03 16:43:29 +09:00
jhChun 47a31c1eb4 SAADC 타이밍 충돌로 인한 FW RESET 방지: 배터리 저전압 측정 주기 및 init 실패시 처리 변경
- 배터리 저전압 측정 주기 5초 -> 60초로 변경(충돌 확률 감소)
- SAADC init 실패 시 APP_ERROR_CHECK -> 측정 스킵 처리

***** 현재 에러 처리는 APP_ERROR_CHECK로 되어있는데, 에러 발생 시 APP_ERROR_CHECK에 걸리면 HardFault 무한 루프에 빠져서 기기가 먹통이 됨. 런타임 중 에러가 발생해도 안전하게 복구되도록 에러 처리 전환 필요함. *****
2026-04-02 11:27:28 +09:00
jhChun 5ee39f834e Slave Latency 4 -> 0 변경 2026-04-02 11:17:06 +09:00
jhChun 5128c44912 DR_ADC_ECHO_SAMPLES_MAX 100 -> 119 2026-04-01 16:22:42 +09:00
jhChun 8fdc153ed5 블루투스 스캐닝 LED 1초 간격 점멸 -> 0.5초 간격 점멸 2026-03-31 17:58:49 +09:00
jhChun d8ef9bcb41 dev 모드 BLE 연결 수정
- dev 모드인 경우 보안 x
2026-03-31 17:58:33 +09:00
jhChun 007e0b5683 Ver VBTFW0102 2026-03-31 14:27:14 +09:00
jhChun d63a9cfa3c BLE 보안 실패 시 자동 재페어링 및 버그 3건 수정
- 앱과 기기 사이 키가 불일치하는 경우 새로운 키 생성 요청(재페어링 허용)
- main.c: PM_EVT_CONN_SEC_CONFIG_REQ 중복 핸들러 제거
- main.c: DEV 모드에서 pm_peers_delete() 이중 호출 방지
- main.c: 활성 연결 중 advertising 재시작 방지(m_conn_handle 체크)
- ble_quick_security: 보안 실패 시 bond 삭제 + 자동 재페어링 시도
- ble_quick_security: allow_repairing = true (항상 재페어링 허용)
2026-03-31 14:17:35 +09:00
8 changed files with 61 additions and 25 deletions
+24 -2
View File
@@ -95,6 +95,14 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
{ {
ret_code_t err_code; ret_code_t err_code;
// DEV 모드: 보안 실패 이벤트는 SDK 핸들러에 전달하지 않음 (disconnect 방지)
if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) {
DBG_PRINTF("Security failed: error=%d\r\n",
p_evt->params.conn_sec_failed.error);
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
return;
}
// Call standard handlers (required) // Call standard handlers (required)
pm_handler_on_pm_evt(p_evt); pm_handler_on_pm_evt(p_evt);
pm_handler_flash_clean(p_evt); pm_handler_flash_clean(p_evt);
@@ -113,6 +121,12 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
DBG_PRINTF("Security failed: error=%d\r\n", DBG_PRINTF("Security failed: error=%d\r\n",
p_evt->params.conn_sec_failed.error); p_evt->params.conn_sec_failed.error);
if (m_state.dev_mode) {
// DEV 모드: 보안 실패 무시 — 연결 유지
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
break;
}
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) { if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) {
// Key missing: 재페어링 시도, 실패 시 disconnect로 폴백 // Key missing: 재페어링 시도, 실패 시 disconnect로 폴백
err_code = pm_conn_secure(p_evt->conn_handle, true); err_code = pm_conn_secure(p_evt->conn_handle, true);
@@ -126,15 +140,23 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
pm_handler_disconnect_on_sec_failure(p_evt); pm_handler_disconnect_on_sec_failure(p_evt);
} }
} else { } else {
// 기타 보안 실패 → disconnect // 기타 보안 실패 → bond 삭제 후 재페어링 시도
pm_peer_id_t peer_id;
if (pm_peer_id_get(p_evt->conn_handle, &peer_id) == NRF_SUCCESS
&& peer_id != PM_PEER_ID_INVALID) {
pm_peer_delete(peer_id);
}
err_code = pm_conn_secure(p_evt->conn_handle, true);
if (err_code != NRF_SUCCESS) {
pm_handler_disconnect_on_sec_failure(p_evt); pm_handler_disconnect_on_sec_failure(p_evt);
} }
}
break; break;
case PM_EVT_CONN_SEC_CONFIG_REQ: case PM_EVT_CONN_SEC_CONFIG_REQ:
{ {
pm_conn_sec_config_t config = { pm_conn_sec_config_t config = {
.allow_repairing = m_state.dev_mode .allow_repairing = true
}; };
pm_conn_sec_config_reply(p_evt->conn_handle, &config); pm_conn_sec_config_reply(p_evt->conn_handle, &config);
} }
+3
View File
@@ -795,6 +795,9 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us,
/* 채널 선택(MUX) : 1.3ms */ /* 채널 선택(MUX) : 1.3ms */
dr_piezo_select_channel(piezo_ch); dr_piezo_select_channel(piezo_ch);
/* MUX 전환 후 S/H 커패시터 안정화를 위한 dummy read */
(void)spim_read_raw();
/* 채널당 반복 측정 횟수만큼 */ /* 채널당 반복 측정 횟수만큼 */
for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++) for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++)
{ {
+1 -1
View File
@@ -73,7 +73,7 @@
*============================================================================*/ *============================================================================*/
#define DR_ADC_SCLK_MHZ 8.6f /**< SPI bit-bang SCLK frequency */ #define DR_ADC_SCLK_MHZ 8.6f /**< SPI bit-bang SCLK frequency */
#define DR_ADC_CLOCKS_PER_SAMPLE 16 /**< ADC121S051: 16 SCLK per sample */ #define DR_ADC_CLOCKS_PER_SAMPLE 16 /**< ADC121S051: 16 SCLK per sample */
#define DR_ADC_ECHO_SAMPLES_MAX 100 /**< Maximum samples */ #define DR_ADC_ECHO_SAMPLES_MAX 119 /**< Maximum samples */
#define DR_ADC_ECHO_SAMPLES_DEFAULT 100 /**< Default samples */ #define DR_ADC_ECHO_SAMPLES_DEFAULT 100 /**< Default samples */
#define DR_ADC_SAMPLE_INTERVAL_US 1.86f /**< 16 / 8.6MHz = 1.86us per sample */ #define DR_ADC_SAMPLE_INTERVAL_US 1.86f /**< 16 / 8.6MHz = 1.86us per sample */
#define DR_ADC_SOUND_SPEED_MM_US 1.54f /**< Sound speed in tissue (mm/us) */ #define DR_ADC_SOUND_SPEED_MM_US 1.54f /**< Sound speed in tissue (mm/us) */
@@ -67,7 +67,7 @@ static nrf_saadc_value_t adc_buf;
APP_TIMER_DEF(m_battery_loop_timer_id); APP_TIMER_DEF(m_battery_loop_timer_id);
/* 배터리 측정 주기: 5초 (밀리초 단위) */ /* 배터리 측정 주기: 5초 (밀리초 단위) */
#define BATTERY_LOOP_INTERVAL 5000 #define BATTERY_LOOP_INTERVAL 60000
/* 저전압 체크 플래그 — battery_loop에서 true로 설정, 핸들러에서 소비 */ /* 저전압 체크 플래그 — battery_loop에서 true로 설정, 핸들러에서 소비 */
bool low_battery_check = false; bool low_battery_check = false;
@@ -217,7 +217,9 @@ static void battery_configure(void)
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; // 10 -> 12bit saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; // 10 -> 12bit
saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X; saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X;
ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, battery_event_handler); ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, battery_event_handler);
APP_ERROR_CHECK(err_code); if (err_code != NRF_SUCCESS) {
return; /* SAADC 사용 중 → 이번 측정 스킵, 다음 주기에 재시도 */
}
/* AIN2 채널 설정: 싱글엔드 입력, 1/6 gain, burst + TACQ 20μs */ /* AIN2 채널 설정: 싱글엔드 입력, 1/6 gain, burst + TACQ 20μs */
nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2); nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
@@ -49,7 +49,7 @@ static const led_pattern_t m_patterns[LED_STATE_COUNT] =
[LED_STATE_OFF] = { 0, 0, COLOR_NONE, false }, [LED_STATE_OFF] = { 0, 0, COLOR_NONE, false },
[LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → 유지 */ [LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → 유지 */
[LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → OFF */ [LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → OFF */
[LED_STATE_ADVERTISING] = { 1000, 1000, COLOR_GREEN, true }, /* 초록 점멸 1초 */ [LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, /* 초록 점멸 1초 */
[LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* 초록 1초 on / 3초 off */ [LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* 초록 1초 on / 3초 off */
[LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* 주황 점멸 1초 */ [LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* 주황 점멸 1초 */
[LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* 초록 3초 on / 1초 off */ [LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* 초록 3초 on / 1초 off */
@@ -146,7 +146,7 @@
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* 최소 연결 간격: 30ms */ #define MIN_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* 최소 연결 간격: 30ms */
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* 최대 연결 간격: 30ms */ #define MAX_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* 최대 연결 간격: 30ms */
#define SLAVE_LATENCY 4 /* 슬레이브 지연: 0 (매 연결 이벤트마다 응답) */ #define SLAVE_LATENCY 0 /* 슬레이브 지연: 0 (매 연결 이벤트마다 응답) */
#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /* 연결 감독 타임아웃: 4초 */ #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /* 연결 감독 타임아웃: 4초 */
#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /* 첫 파라미터 갱신 요청까지 5초 대기 */ #define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /* 첫 파라미터 갱신 요청까지 5초 대기 */
#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /* 이후 갱신 요청 간격: 30초 */ #define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /* 이후 갱신 요청 간격: 30초 */
@@ -944,20 +944,16 @@ static void pm_evt_handler(pm_evt_t const * p_evt)
DBG_PRINTF("[PM] Sec failed\r\n"); DBG_PRINTF("[PM] Sec failed\r\n");
break; break;
/* 모든 본딩 삭제 완료 → 광고 재시작 */ /* 모든 본딩 삭제 완료 → 활성 연결이 없을 때만 광고 재시작 */
case PM_EVT_PEERS_DELETE_SUCCEEDED: case PM_EVT_PEERS_DELETE_SUCCEEDED:
DBG_PRINTF("[PM] Bonds erased\r\n"); DBG_PRINTF("[PM] Bonds erased\r\n");
if (m_conn_handle == BLE_CONN_HANDLE_INVALID) {
advertising_start(false); advertising_start(false);
break;
/* 보안 설정 요청 → 재페어링 허용 */
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
} }
break; break;
/* PM_EVT_CONN_SEC_CONFIG_REQ → ble_quick_security에서 처리 */
/* 피어 데이터(본딩 정보) 갱신 성공 → 피어 BLE 주소 저장 */ /* 피어 데이터(본딩 정보) 갱신 성공 → 피어 BLE 주소 저장 */
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED: case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
{ {
@@ -1020,14 +1016,17 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
#endif #endif
#if FEATURE_SECURE_CONNECTION #if FEATURE_SECURE_CONNECTION
if (!ble_security_is_dev_mode()) {
pm_handler_secure_on_connection(p_ble_evt); pm_handler_secure_on_connection(p_ble_evt);
}
#endif #endif
switch (p_ble_evt->header.evt_id) switch (p_ble_evt->header.evt_id)
{ {
/* BLE 연결이 끊어지는 경우 */ /* BLE 연결이 끊어지는 경우 */
case BLE_GAP_EVT_DISCONNECTED: case BLE_GAP_EVT_DISCONNECTED:
DBG_PRINTF("[BLE] Disconnected\r\n"); DBG_PRINTF("[BLE] Disconnected (reason 0x%02X)\r\n",
p_ble_evt->evt.gap_evt.params.disconnected.reason);
ble_connection_st = 0; ble_connection_st = 0;
m_conn_handle = BLE_CONN_HANDLE_INVALID; m_conn_handle = BLE_CONN_HANDLE_INVALID;
m_tx_in_progress = false; m_tx_in_progress = false;
@@ -1106,6 +1105,14 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
NULL, NULL); NULL, NULL);
APP_ERROR_CHECK(err_code); APP_ERROR_CHECK(err_code);
#else
if (ble_security_is_dev_mode()) {
err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
NULL, NULL);
APP_ERROR_CHECK(err_code);
DBG_PRINTF("DEV: Rejected security request\r\n");
}
#endif #endif
break; break;
@@ -1641,7 +1648,8 @@ static void main_s(void * p_context)
battery_timer_start(); battery_timer_start();
#if FEATURE_SECURE_CONNECTION #if FEATURE_SECURE_CONNECTION
advertising_start(erase_bonds || bond_data_delete); /* DEV 모드: ble_security_quick_init()에서 이미 bond 삭제됨 → 중복 방지 */
advertising_start(!ble_security_is_dev_mode() && (erase_bonds || bond_data_delete));
#else #else
advertising_start(); advertising_start();
#endif #endif
@@ -30,8 +30,9 @@
* *
* Firmware Version Update History * Firmware Version Update History
* - VBTFW0101 : reb+red ( ), 260330 jhChun * - VBTFW0101 : reb+red ( ), 260330 jhChun
* - VBTFW0102 : LED (msl) 260331 jhChun
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
#define FIRMWARE_VERSION "VBTFW0101" #define FIRMWARE_VERSION "VBTFW0102"
/*============================================================================== /*==============================================================================
* *
File diff suppressed because one or more lines are too long