POSA_LEAKSMS/firmware/docs/HARDWARE.md
유창욱 90f121e14c chore: import codebase with security hardening
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 제외.
2026-06-20 09:37:40 +09:00

12 KiB

HARDWARE.md — STM32F407VGT6 핀맵 & 배선 (SHT30 단일 보드)

대상 MCU: STM32F407VGT6 (LQFP-100), Cortex-M4F @168MHz. 권장 기준 보드: STM32F4-DISCOVERY 계열 또는 동등한 커스텀 PCB. 핀은 LQFP-100 패키지 기준.

이 문서는 SHT30 온습도 보드의 핀 배치, 외부 배선, 전원, 클럭원, 그리고 반드시 해소해야 하는 핀 충돌(USART2 ↔ LAN8720 MDIO, 둘 다 PA2) 을 다룬다.

사이트/하드웨어 결정이 필요한 항목은 TODO(hw): 로 표시했다. 코드는 합리적 기본값으로 컴파일된다.


0. ⚠️ PA2 핀 충돌 (USART2 TX vs. LAN8720 MDIO) — 코드에서 해소 완료

문제(배경). 초기에는 디버그 로그 UART 가 USART2 (PA2=TX / PA3=RX) 로 설계되었으나(ST-Link VCP 호환), LAN8720 RMII 연결에서 MDIO 신호 역시 PA2 (ETH_MDIO, AF11) 를 사용한다. PA2 를 두 기능이 동시에 점유할 수 없고, RMII 동작에 MDIO(PHY 레지스터 설정/링크 폴링)가 필수이므로 PA2 는 반드시 ETH_MDIO 여야 한다.

해소(코드 반영됨). 로그 UART 를 USART3 (PD8=TX / PD9=RX, AF7) 로 이동했다. 반영 위치:

  • common/applog.c: HAL_UART_MspInit/applog_init 가 USART3 + GPIOD(PD8/PD9, AF7)로 초기화.
  • common/app_config.h: APP_LOG_UART_BAUD 주석을 USART3 PD8/PD9 로 정정. PA2 는 ETH_MDIO 전용이며, 코드에는 USART2 사용처가 더 이상 없다.

권장 해소안 (택일)

방안 로그 UART 핀 AF ST-Link VCP 권장도 비고
A. USART3 → PD8(TX)/PD9(RX) PD8/PD9 AF7 별도 USB-UART 어댑터 필요 ★ 권장 RMII/I2C 와 충돌 없음. PD 포트는 본 설계에서 비어 있음
B. USART1 → PA9(TX)/PA10(RX) PA9/PA10 AF7 별도 어댑터 차선 PA9/PA10 미사용. 단, PA9 는 USB-OTG VBUS 와 보드에 따라 충돌 가능 — 디스커버리 보드 확인
C. USART2 유지 + RMII 포기 PA2/PA3 VCP 사용 ✗ 불가 이더넷이 필수이므로 채택 불가

채택: 방안 A — 로그 UART = USART3 (PD8 TX / PD9 RX, AF7, 115200 8N1). USART3 의 다른 핀쌍(PB10/PB11, PC10/PC11)은 RMII(PB11=TXEN)·SHT30 I2C 와 겹치므로 PD8/PD9 를 사용한다.

상태:

  1. 코드 반영됨: applog.c 가 USART3 + GPIOD(PD8/PD9, AF7)로 초기화, app_config.h 주석 정정.
  2. TODO(hw): ST-Link VCP(PA2/PA3)를 못 쓰므로 운영 로그는 외부 3.3V USB-UART 어댑터를 PD8/PD9 에 연결. 본 문서의 모든 표는 방안 A(USART3 PD8/PD9) 기준이다.

1. LAN8720 Ethernet PHY (RMII)

STM32 내장 ETH MAC + 외부 LAN8720 PHY, RMII 모드. 모든 RMII 핀은 대체기능 AF11 (ETH).

STM32 핀 신호 (RMII) AF LAN8720 핀 방향(MCU 기준) 비고
PA1 ETH_REF_CLK AF11 nINT/REFCLKO 입력 50MHz 기준 클럭 입력 (§1.1)
PA2 ETH_MDIO AF11 MDIO 양방향 ⚠️ §0 충돌 핀 — USART2 금지
PC1 ETH_MDC AF11 MDC 출력 관리 클럭
PA7 ETH_CRS_DV AF11 CRS_DV 입력 캐리어 감지/데이터 유효
PC4 ETH_RXD0 AF11 RXD0 입력 수신 데이터 0
PC5 ETH_RXD1 AF11 RXD1 입력 수신 데이터 1
PB11 ETH_TX_EN AF11 TXEN 출력 송신 인에이블
PB12 ETH_TXD0 AF11 TXD0 출력 송신 데이터 0
PB13 ETH_TXD1 AF11 TXD1 출력 송신 데이터 1

추가 배선(RMII 표준):

LAN8720 핀 연결 비고
MDIO 풀업 1.5kΩ → 3.3V 권장
nRST MCU GPIO 또는 RC 리셋 TODO(hw): 소프트 리셋 핀 배정 시 GPIO 1개 추가(예 PE0). 미배정 시 전원-온 RC 리셋
25MHz XTAL / OSC §1.1 클럭 구성에 따라
RXER (선택) 미사용 가능 RMII 에서 생략 가능

RMII 는 RXD2/RXD3/TXD2/TXD3/COL/CRS/RX_CLK/TX_CLK 가 없다(MII 대비 핀 절감). 위 9개 + MDC/MDIO 가 전부다.

1.1 RMII 50MHz REF_CLK 소싱 (중요)

RMII 는 MAC 과 PHY 가 공통 50MHz REF_CLK 를 공유해야 한다. PA1(ETH_REF_CLK)은 MCU 입력이며, 이 50MHz 를 어디서 만들지가 보드 설계의 핵심이다. 두 가지 표준 구성:

  1. PHY 가 50MHz 생성 → MCU 로 공급 (권장, 디스커버리 보드 방식)
    • LAN8720 에 25MHz 크리스털을 달고, PHY 내부에서 50MHz 를 만들어 REFCLKO → MCU PA1 으로 공급.
    • 보드에 따라 MCU MCO1(PA8) 로 25MHz 를 PHY 에 주고 PHY 가 50MHz 를 되돌리는 변형도 있음.
  2. 외부 50MHz 오실레이터 → MCU PA1 + PHY 동시 공급
    • 50MHz 캔 오실레이터 1개로 두 칩에 동시 분배. 가장 단순하고 안정적.

TODO(hw): 보드 설계에 맞춰 1) 또는 2) 중 하나를 확정하고 BOM/스템핑을 결정한다. 펌웨어 측 RCC RMII 클럭 선택(SYSCFG->PMC.MII_RMII_SEL=RMII)은 common/net.c/ethernetif.c 초기화에서 처리한다(코드 기본값 = RMII).


2. SHT30 온습도 센서 (-DBOARD_SHT30, sensor_id=2)

Sensirion SHT30 (한진데이터 P4422-3 모듈), I2C. RPi sht30_monitor.py 와 동일 규약.

2.1 I2C 배선

STM32 핀 신호 AF I2C 비고
PB6 I2C1_SCL AF4 I2C1 오픈드레인, 외부 풀업 필요
PB7 I2C1_SDA AF4 I2C1 오픈드레인, 외부 풀업 필요
  • 7-bit 주소: 0x44 (APP_SHT30_I2C_ADDR). HAL 호출 시 0x44<<1.
  • 명령: 0x2C06 (high-repeatability, clock-stretch off) → ~20ms 대기 → 6바이트 read.
  • 풀업: SCL/SDA 각각 4.7kΩ → 3.3V (모듈에 내장 풀업이 있으면 생략). I2C 속도 100kHz(Standard) 권장.
  • plausibility 범위(벗어나면 metric_status="out_of_range"): T [-40, 125]°C, RH [0, 100]% (common/app_config.h). 운영 경보 임계(고온/저온/고습/저습 → SMS)는 서버 php/config.phpMETRIC_* 상수가 판정한다(펌웨어는 원값만 보고).
  SHT30 모듈                STM32
  ┌────────┐
  │  VDD ──┼──────────────  3.3V ──┬──[4.7kΩ]──┐   ┌──[4.7kΩ]── 3.3V
  │  SCL ──┼──────────────  PB6 ───┘            │   │
  │  SDA ──┼──────────────  PB7 ────────────────┼───┘
  │  GND ──┼──────────────  GND                 │
  │  ADDR ─┼── GND  (주소 0x44; VDD 면 0x45)     │
  └────────┘

TODO(hw): ADDR 핀 결선으로 0x44(ADDR→GND) / 0x45(ADDR→VDD) 가 결정된다. 본 설계는 0x44 고정.

2.2 SHT30 보드 핀 요약

용도
PB6 / PB7 I2C1 SCL / SDA (0x44)
(RMII 9핀) §1 표 그대로 — 단 PB11/PB12/PB13 가 RMII 에 쓰임에 주의
PD8/PD9 로그 UART (USART3, §0 방안 A)
상태 LED §3

주의: I2C1 은 PB6/PB7 를 쓰고 RMII 는 PB11/PB12/PB13 을 쓴다 — PB 포트 내부에서 충돌 없음.


3. 공통 주변장치 — 상태 LED, 클럭원, 워치독/RTC, 전원

3.1 상태 LED (common/bsp.h bsp_led_*)

용도 비고
PD12 상태 LED (녹색) TODO(hw): 디스커버리 보드 온보드 LED(PD12~PD15) 사용 가정. 커스텀 PCB 면 임의 GPIO 1개로 변경

권장 점멸 패턴(운영 가시성): 부팅=점등 / 정상 보고=느린 토글 / 망 단절·TLS 실패=빠른 점멸 / bsp_fatal()=고속 점멸.

3.2 시스템 클럭 / 오실레이터

항목 비고
HSE 8MHz 크리스털 SystemClock_Config() 가 PLL 로 168MHz 생성(common/bsp.h 주석)
SYSCLK 168MHz Cortex-M4F
RMII REF_CLK 50MHz §1.1 (HSE 와 별개 경로)

TODO(hw): 보드에 HSE 8MHz 크리스털 + 부하 커패시터 실장 확인. 없으면 SystemClock_Config 의 HSE 값 정정 필요.

3.3 RTC 클럭원 (SNTP → RTC 시간 보존)

항목 권장 비고
RTC 클럭원 LSE 32.768kHz 크리스털 TLS 인증서 유효기간 검증 + timestamp 필드용. 정전 시 VBAT 로 시간 보존
대안 LSI(~32kHz, 내장) 정밀도 낮음. SNTP 로 주기 보정하면 허용. VBAT 백업 불가
VBAT 코인셀(CR2032) 또는 VBAT→3.3V LSE + 백업 도메인 유지용. 미실장 시 매 부팅 SNTP 재동기 필요

TODO(hw): LSE 크리스털 + VBAT 백업 실장 여부 확정. 미실장이면 common/timesync.c 는 매 부팅 SNTP 동기에 의존(이식 계획 R4: SNTP 실패 시 경보).

3.4 워치독 (IWDG)

항목 비고
IWDG 클럭원 LSI (~32kHz, 내장) IWDG 는 항상 LSI 구동(별도 핀 없음)
타임아웃 APP_WATCHDOG_TIMEOUT_MS = 20000 (20s) 이 시간 내 watchdog_refresh() 없으면 MCU 리셋(common/watchdog.h)
BOR 활성 권장 브라운아웃 리셋(이식 계획 Phase 7) — TODO(hw): 옵션 바이트로 BOR 레벨 설정

3.5 전원

레일 용도 비고
3.3V MCU VDD/VDDA, LAN8720, SHT30, 풀업 LAN8720 RMII I/O 는 3.3V. PHY 전류 여유(~수십 mA) 확보
VDDA ADC/PLL 기준 0.1µF + 1µF 디커플링, 페라이트 비드 권장
VBAT RTC 백업(§3.3) 코인셀/점퍼
디커플링 각 VDD 핀당 0.1µF + 벌크 4.7µF 표준 STM32 권장

TODO(hw): PoE/외부 12V→3.3V 등 실제 급전 방식은 설치 환경에 따라 확정. RPi 가 쓰던 5V USB 어댑터 재사용 가능(5V→3.3V 레귤레이터 추가).


4. 최종 권장 핀맵 (전체 통합)

§0 방안 A(로그 UART = USART3 PD8/PD9) 채택을 가정한 최종 권장 핀맵. 충돌 해소 완료 상태.

STM32 핀 SHT30 보드 기능 AF 충돌 여부
PA1 ETH_REF_CLK (RMII 50MHz in) AF11 OK
PA2 ETH_MDIO (USART2 금지) AF11 §0 해소
PA7 ETH_CRS_DV AF11 OK
PB6 I2C1_SCL AF4 OK
PB7 I2C1_SDA AF4 OK
PB11 ETH_TX_EN AF11 OK
PB12 ETH_TXD0 AF11 OK
PB13 ETH_TXD1 AF11 OK
PC1 ETH_MDC AF11 OK
PC4 ETH_RXD0 AF11 OK
PC5 ETH_RXD1 AF11 OK
PD8 USART3_TX (로그) AF7 OK (방안 A)
PD9 USART3_RX (로그) AF7 OK (방안 A)
PD12 상태 LED OK
OSC_IN/OUT HSE 8MHz OK
PC14/PC15 LSE 32.768kHz (RTC, 선택) §3.3
VBAT RTC 백업 전원(선택) §3.3

미사용/주의: PA2 는 절대 USART2_TX 로 설정하지 말 것(이더넷 MDIO 전용). PA3 도 USART2_RX 로 쓰지 않는다.


5. 핀 충돌 점검표 (요약)

잠재 충돌 상태 해소
PA2: USART2_TX ↔ ETH_MDIO 해소(코드) 로그 UART 를 USART3(PD8/PD9)로 이동(applog.c). PA2=ETH_MDIO 고정
USART3 PB10/PB11 ↔ ETH_TX_EN(PB11) 회피됨 USART3 를 PD8/PD9 핀쌍으로 사용
USART3 PC10/PC11 ↔ ETH RXD(PC4/PC5) 회피됨 동일(PD8/PD9 사용)
로그 UART(PD8/PD9) ↔ 상태 LED(PD12) 없음 같은 D 포트, 다른 핀
I2C1(PB6/PB7) ↔ RMII(PB11~13) 없음 PB 포트 내 핀 분리
RMII 클럭 게이팅(bsp.c) 무해 bsp.c 가 공통 클럭으로 GPIOA/B/C/D 를 켬. ETH MspInit 도 GPIOA/B/C 를 자체 enable(중복 무해)

결론: 코드 기준으로 남은 핀 충돌은 없다. PA2 충돌은 로그 UART 를 USART3 PD8/PD9 로 옮겨 해소했고 (코드 반영 완료), 나머지는 모두 포트 내 핀 분리로 충돌이 없다. 남은 항목은 §0의 외부 USB-UART 어댑터 배선 과 §3 의 클럭원/전원 TODO(hw) 뿐이다.