ESP32 BLE 스택을 OTA 시 해제하고 복원하기
BLEIoTEmbeddedOTA
ESP32-S3에서 HTTPS OTA는 TLS 핸드셰이크만으로 약 40KB 힙을 소모한다. BLE 스택(NimBLE)이 43~53KB를 점유하고 있으므로, OTA 전에 BLE를 완전히 해제하면 충분한 메모리를 확보할 수 있다.
구조
OTA 메모리 매니저에 BLE를 모듈로 등록한다. OTA가 시작되면 매니저가 등록된 모듈을 순서대로 해제한다.
ota_memory_module_t ble_module = {
.name = "BLE",
.get_memory_usage = ble_get_memory_usage, // 약 43KB 반환
.deinit = ble_deinit,
.reinit = ble_reinit,
.can_recover = true
};
ota_memory_register_module(&ble_module);
can_recover = true이므로 OTA 완료(성공/실패 모두) 후 reinit이 호출된다.
해제 순서
BLE 스택 해제에는 엄격한 순서 제약이 있다.
esp_err_t DeinitBleComs(void) {
// 1. 광고 중지 + 연결 해제
ble_gap_adv_stop();
ble_gap_conn_cancel();
// 2. NimBLE 호스트 루프 종료
nimble_port_stop();
// 3. 호스트 태스크 종료 대기 (최대 1초)
int wait_count = 0;
while (g_ble_state.host_task_handle != NULL && wait_count < 20) {
vTaskDelay(pdMS_TO_TICKS(50));
wait_count++;
}
if (g_ble_state.host_task_handle != NULL) {
vTaskDelete(g_ble_state.host_task_handle); // 강제 종료
}
// 4. NimBLE 포트 해제 (메모리 풀 반환)
nimble_port_deinit();
// 5. BT 컨트롤러 해제
esp_bt_controller_disable();
esp_bt_controller_deinit();
esp_bt_mem_release(ESP_BT_MODE_BLE);
return ESP_OK;
}
3번(태스크 종료 대기)을 건너뛰면 4번에서 크래시가 발생한다. nimble_port_run()이 아직 실행 중인 상태에서 메모리 풀을 해제하기 때문이다. 강제 종료(vTaskDelete)는 정상 종료 실패 시 fallback이다.
복원
저장해둔 설정으로 역순 초기화한다.
esp_err_t ReinitBleComs(void) {
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
esp_bt_controller_init(&bt_cfg);
esp_bt_controller_enable(ESP_BT_MODE_BLE);
// 최초 초기화와 동일한 함수 재호출
InitBleComs(g_ble_state.device_name, g_ble_state.profile);
return ESP_OK;
}
g_ble_state에 device_name과 GATT profile 포인터를 보관해두므로 복원 시 별도 인자가 필요 없다.
메모리 내역
| 항목 | 크기 |
|---|---|
| NimBLE 호스트 태스크 스택 | 8KB |
| NimBLE 버퍼/구조체 | 약 15KB |
| BT 컨트롤러 | 약 20KB |
| 합계 | 약 43KB |
esp_get_free_heap_size()로 해제 전후를 비교해 실제 회수량을 로그로 확인한다.
장단점
장점:
- 추가 하드웨어 없이 약 43KB 힙 확보
- OTA 성공률이 높아진다. HTTPS OTA에서 메모리 부족으로 TLS 핸드셰이크 실패하는 문제 해결
- 모듈 등록 패턴이므로 BLE 외 다른 모듈(태스크 매니저 등)도 같은 방식으로 추가 가능
단점:
- OTA 중 BLE 통신 불가. 모바일 앱에서 OTA 진행 상태를 BLE로 받을 수 없다
- 복원 시 기존 bonding 정보가 유지되지만, 모바일 앱은 재연결이 필요하다
- 해제/복원 과정에 약 500ms 소요
대안 검토
BLE 유지하면서 OTA:
- WiFi만으로 OTA를 수행하고 BLE는 유지하는 방식
- 메모리가 충분한 칩(PSRAM 탑재)에서는 가능하지만, 내부 SRAM만 사용하는 구성에서는 힙 부족 발생
- BLE로 OTA 진행률을 전송할 수 있다는 장점이 있으나, 이 프로젝트에서는 PSRAM 없이 동작해야 하므로 채택하지 않았다
HTTP(비암호화) OTA:
- TLS 없이 HTTP로 OTA하면 약 40KB를 절약할 수 있어 BLE 해제가 불필요
- 보안 문제로 프로덕션에서는 사용하지 않는다. 개발 단계에서는 HTTP OTA + BLE 유지 조합으로 디버깅 편의성을 확보했다
NimBLE 버퍼 축소:
CONFIG_BT_NIMBLE_ACL_BUF_COUNT,CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT등을 줄여 상주 메모리를 낮추는 방식- 이미 최소 수준으로 설정했으나(
ACL_BUF_COUNT=8,MAX_CONNECTIONS=1), 그래도 OTA에 충분하지 않았다