Showing Posts From
Nrf52
- 16 Dec, 2025
nRF52 BLE 연결이 끊긴다? 연결 간격을 조정해봤나
nRF52 BLE 연결이 끊긴다? 연결 간격을 조정해봤나 아침부터 시작된 악몽 출근했다. 메일함에 빨간 글씨. "양산 일정 2주 남았습니다. BLE 안정성 이슈 해결 바랍니다."시제품 20대 중 3대가 랜덤하게 연결 끊김. 재현도 안 돼. 가장 싫은 타입의 버그다. nRF52832 쓰는 IoT 센서 프로젝트. 스마트폰 앱이랑 BLE로 통신한다. 센서 데이터 1초마다 보내는 간단한 구조인데, 왜 끊기는지 모르겠다. HW팀은 "펌웨어 문제 아니냐"고 한다. 앱팀은 "안드로이드는 문제없다"고 한다. 나만 혼자 샌드백이다. 스펙 문서는 거짓말을 안 한다 일단 nRF52 SDK 문서부터 다시 읽었다. BLE Connection Parameters. 읽어도 읽어도 어렵다.Connection Interval: 7.5ms ~ 4000ms Slave Latency: 0 ~ 499 Supervision Timeout: 100ms ~ 32000ms이게 뭔 소리인지 알아? 나도 처음엔 몰랐다. Connection Interval은 통신 주기다. 짧을수록 빠르게 데이터 주고받지만 전력 소모가 크다. Slave Latency는 센서가 몇 번까지 응답 안 해도 괜찮은지. 전력 아끼려고 쓴다. Supervision Timeout은 "이 시간 동안 응답 없으면 연결 끊는다"는 뜻. 우리 코드는 이랬다. #define MIN_CONN_INTERVAL MSEC_TO_UNITS(100, UNIT_1_25_MS) #define MAX_CONN_INTERVAL MSEC_TO_UNITS(200, UNIT_1_25_MS) #define SLAVE_LATENCY 0 #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)100~200ms 간격으로 통신. Latency 0. Timeout 4초. 보기엔 문제없어 보인다. 그런데 스펙 문서 깊숙이 읽다가 발견했다. "Supervision Timeout must be larger than (1 + Slave Latency) × Connection Interval × 2" 계산해보니 우리는 딱 맞춰놨다. 여유가 없다. 200ms × 2 = 400ms < 4000ms. 수식상 문제없지만, 실전은 다르다.오실로스코프는 진실을 말한다 이론만 보면 안 된다. 실제 신호를 봐야 한다. Logic Analyzer 연결했다. nRF52 DK에 프로브 꽂고, GPIO 토글로 타이밍 찍어뒀다. 연결 이벤트마다 핀 하이/로우. 원시적이지만 확실하다. 돌렸다. 30분 관찰. 정상일 때: 200ms 간격으로 깔끔하게 이벤트 발생. 끊기기 직전: 간격이 들쑥날쑥. 150ms, 180ms, 250ms... 왜 이러지? 로그 뒤져보니 원인 발견. 센서 읽는 I2C 통신이 가끔 지연된다. 온도센서 응답 느릴 때 있다. 그 사이 BLE 이벤트 놓친다. RTOS Priority도 문제였다. BLE 스택이랑 센서 태스크 우선순위가 같았다. 센서 읽다가 BLE 이벤트 늦게 처리하면, Central(스마트폰)은 "쟤 죽은 거 아니야?" 판단한다. Supervision Timeout 4초. 여유 있어 보이지만, 실전에선 불안하다. RF 간섭 있으면? 패킷 재전송 있으면? 누적되면 4초 금방이다. 삽질의 시작 첫 번째 시도: Timeout 늘리기. #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(6000, UNIT_10_MS)4초 → 6초. 결과: 여전히 끊긴다. 근본 해결 아니다. 두 번째 시도: Connection Interval 늘리기. #define MIN_CONN_INTERVAL MSEC_TO_UNITS(200, UNIT_1_25_MS) #define MAX_CONN_INTERVAL MSEC_TO_UNITS(400, UNIT_1_25_MS)생각: 주기 늘리면 센서 읽을 시간 여유 생긴다. 결과: 연결은 안정됐는데, 데이터 전송 느려져서 앱팀이 불만. "왜 이렇게 느려졌냐." 세 번째 시도: Slave Latency 추가. #define SLAVE_LATENCY 3센서가 3번까지 응답 안 해도 괜찮다는 뜻. 바쁠 때 건너뛸 수 있다. 결과: 더 불안정해졌다. 왜? 로그 보니, Latency 쓰면 Central이 "언제 응답 올지 모른다"고 타이밍 예측 못 한다. 안드로이드 BLE 스택이 짜증 낸다. 이게 스펙엔 없는 현실이다.답은 의외로 간단했다 삽질 1주일. 동기한테 하소연했다. 걔는 웨어러블 기기 펌웨어 한다. nRF52 경험 많다. "야, Connection Interval 협상 제대로 했어?" 협상? 뭔 소리야. 알고 보니 BLE는 연결 후 Parameter를 재협상한다. Central이 제안 → Peripheral(우리 센서)이 수락/거절. 우리 코드는 초기값만 설정하고, 협상 핸들링을 안 했다. Central이 "30ms 간격으로 하자" 제안하면, 그냥 수락한다. 30ms? 우리 센서는 I2C 읽는 데만 50ms 걸리는데? 코드 수정했다. void on_ble_evt(ble_evt_t const * p_ble_evt) { switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { ble_gap_conn_params_t const * p_req; p_req = &p_ble_evt->evt.gap_evt.params.conn_param_update_request; // 최소 Interval 체크 if (p_req->max_conn_interval < MIN_ACCEPTABLE_INTERVAL) { // 거절하고 우리 값 제안 ble_gap_conn_params_t our_params = { .min_conn_interval = MIN_CONN_INTERVAL, .max_conn_interval = MAX_CONN_INTERVAL, .slave_latency = 0, .conn_sup_timeout = CONN_SUP_TIMEOUT }; sd_ble_gap_conn_param_update(p_ble_evt->evt.gap_evt.conn_handle, &our_params); } else { // 수락 sd_ble_gap_conn_params_reply(p_ble_evt->evt.gap_evt.conn_handle, p_req, NULL); } break; } } }Central이 너무 짧은 간격 요청하면 거절하고, 우리가 감당 가능한 값 제안한다. 추가로 BLE 이벤트 핸들러 우선순위 올렸다. // FreeRTOS Task Priority #define BLE_TASK_PRIORITY (configMAX_PRIORITIES - 1) // 최우선 #define SENSOR_TASK_PRIORITY (tskIDLE_PRIORITY + 2) // 낮춤센서 읽다가 BLE 이벤트 놓치는 일 없도록. 테스트는 잔인하다 수정 후 24시간 Stress Test. 시제품 20대 전부 켜두고, 앱 연결 → 끊기 → 재연결 반복. 사무실 한쪽에 휴대폰 20개 놓고 스크립트 돌렸다. 밤새 돌렸다. 아침에 출근해서 확인. 로그 확인: 연결 끊김 0건. 재연결 시간: 평균 1.2초. 이전엔 3~5초 걸렸다. 데이터 누락: 없음. HW팀 불러서 보여줬다. "이제 됩니다." 걔네는 그냥 "아 그래?" 하더라. 1주일 고생한 건 관심 없다. 앱팀한테도 보고. "속도는 괜찮냐?" "이전이랑 똑같네요." 그래, 그럼 됐다. 스펙엔 없는 현실 BLE 스펙 문서는 1000페이지 넘는다. 다 읽어도 실전은 다르다.Connection Interval은 협상된다. 초기값은 제안일 뿐. Central(폰)마다 행동이 다르다. 삼성은 이렇고, 애플은 저렇고. RF 환경에 따라 신뢰성이 천차만별. 사무실이랑 공장이 다르다. Supervision Timeout은 여유 있게. "이론상 충분"은 실전에서 불충분.코드로만 해결 안 된다. 파형 봐야 하고, 로그 쌓아야 하고, 24시간 돌려야 안다. 양산 일정은 2주 남았다. 아직 해야 할 테스트 많다. -30도, +85도 온도 챔버 테스트. EMI 테스트. 인증 준비. 끝나면 한잔해야겠다.오늘도 스펙 문서와 현실 사이 어딘가에서 싸운다.