SHT30 온습도 모니터링 시스템 전체 소스(서버 PHP, STM32 펌웨어, SQL, 테스트). 전체 코드리뷰에서 도출된 보안 하드닝 10건 반영: - 요청 서명 HMAC-SHA256 전환(펌웨어 sig.c/서버 config.php/호스트 패리티 동시) - 재전송 방어 + 기본 API_KEY fail-closed + 디바이스 문자열 정제(api/sensor_data.php) - 오프라인 SMS 중복 발송 경합 제거(cron_heartbeat.php, 원자적 선점) - CSV 수식 주입 방지(monthly_report.php), 감사로그 회전 락(retention_cleanup.php) - 브루트포스 카운터 원자화(login.php), 예시 TOTP 비밀키 무효화, 마이그레이션 멱등화 _backup/(하드코딩 실 비밀값 포함)·config.local.php·런타임 상태는 .gitignore 제외.
89 lines
3.8 KiB
C
89 lines
3.8 KiB
C
/* =============================================================================
|
|
* 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 <stdint.h>
|
|
|
|
#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 */
|
|
}
|
|
}
|