/* ============================================================================= * main.c - SHT30 온습도 보드 펌웨어 진입점 (BOARD_SHT30) * * 부팅 순서 (RPi sht30_monitor.py 기동 대응): * bsp_init() : HAL/클럭(HSE 8MHz -> 168MHz)/공통 GPIO/LED * applog_init() : USART3 디버그 로그(PD8/PD9, 115200) — PA2 는 ETH_MDIO 전용 * watchdog_init() : IWDG 활성화 (이후 헬스 태스크가 refresh) * sht30_init() : I2C 주변장치 + 센서 초기화 * net_init() : ETH MAC + LAN8720 PHY(RMII) + LwIP * tls_init() : mbedTLS 엔트로피/DRBG 시드, 루트 CA 파싱 * app_sht30_start() : 측정 태스크 + 헬스 태스크 생성 * vTaskStartScheduler() : FreeRTOS 스케줄러 기동(반환하지 않음) * * 폐쇄망(air-gapped): 모든 의존성(HAL/CMSIS/FreeRTOS/lwip/mbedtls)은 third_party * 에 벤더링되어 있으며 런타임에 외부 네트워크를 사용하지 않는다(SNTP/HTTPS는 * 사내 망 내부 서버 대상). * ===========================================================================*/ #include #include "FreeRTOS.h" #include "task.h" #include "app_config.h" /* board_config.h + secrets.h 포함 */ #include "bsp.h" #include "applog.h" #include "watchdog.h" #include "sht30.h" #include "net.h" #include "tls.h" /* 이 펌웨어는 SHT30 보드 전용이다(CMake sht30_fw 타깃이 -DBOARD_SHT30 정의). */ #if !defined(BOARD_SHT30) #error "board_sht30/main.c 는 -DBOARD_SHT30 로 빌드해야 합니다." #endif /* app_sht30.c 에서 정의 (전용 헤더 없이 모듈 내부 함수로 연결) */ extern void app_sht30_start(void); int main(void) { /* 1) 클럭/HAL/공통 주변장치. 가장 먼저 수행해야 이후 초기화가 유효하다. */ bsp_init(); /* 2) 디버그 로그 UART. 이후 단계의 진단 메시지를 위해 일찍 켠다. */ applog_init(); LOGI("=== SHT30 board boot ==="); LOGI("device_id=%s sensor_id=%d ver=%s", BOARD_DEVICE_ID, BOARD_SENSOR_ID, APP_VERSION); LOGI("location=%s name=%s", BOARD_DEVICE_LOCATION, BOARD_SENSOR_NAME); /* 3) 독립 워치독(IWDG). 활성화 즉시 카운트다운이 시작되므로, 스케줄러 기동 * 후 헬스 태스크가 첫 refresh 를 하기 전까지 타임아웃되지 않도록 * APP_WATCHDOG_TIMEOUT_MS 는 충분히 크게(20s) 설정되어 있다. */ watchdog_init(APP_WATCHDOG_TIMEOUT_MS); /* 4) SHT30 센서(I2C). 실패해도 부팅은 계속한다: 측정 태스크가 매 주기 * 재시도하므로(센서 일시 미연결 대비) 여기서 멈추지 않는다. */ if (sht30_init() != 0) { LOGE("sht30_init failed (will retry in measure loop)"); } else { LOGI("sht30 init ok (i2c addr=0x%02X)", APP_SHT30_I2C_ADDR); } /* 5) 네트워크 스택 초기화 (LwIP tcpip 스레드 포함). 링크 업 대기는 * 측정 태스크에서 수행한다. */ net_init(); LOGI("net init ok"); /* 6) TLS 전역 초기화 (엔트로피/DRBG, 임베드 루트 CA 파싱). 한 번만 수행. */ if (tls_init() != 0) { /* TLS 가 없으면 서버 보고가 불가능하므로 치명적 오류로 처리. * (워치독이 결국 리셋하여 재기동을 시도) */ bsp_fatal("tls_init failed"); } LOGI("tls init ok"); /* 7) 애플리케이션 태스크(측정 + 헬스) 생성. */ app_sht30_start(); LOGI("tasks created, starting scheduler"); /* 8) 스케줄러 기동. 정상적으로는 반환하지 않는다. */ vTaskStartScheduler(); /* 여기에 도달했다면 힙 부족으로 스케줄러가 기동하지 못한 것이다. */ bsp_fatal("scheduler returned (insufficient heap)"); for (;;) { /* unreachable */ } }