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 제외.
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 를 사용한다.
상태:
- ✅ 코드 반영됨:
applog.c가 USART3 + GPIOD(PD8/PD9, AF7)로 초기화,app_config.h주석 정정.- ⬜
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 를 어디서 만들지가 보드 설계의 핵심이다. 두 가지 표준 구성:
- PHY 가 50MHz 생성 → MCU 로 공급 (권장, 디스커버리 보드 방식)
- LAN8720 에 25MHz 크리스털을 달고, PHY 내부에서 50MHz 를 만들어
REFCLKO→ MCUPA1으로 공급. - 보드에 따라 MCU
MCO1(PA8)로 25MHz 를 PHY 에 주고 PHY 가 50MHz 를 되돌리는 변형도 있음.
- LAN8720 에 25MHz 크리스털을 달고, PHY 내부에서 50MHz 를 만들어
- 외부 50MHz 오실레이터 → MCU PA1 + PHY 동시 공급
- 50MHz 캔 오실레이터 1개로 두 칩에 동시 분배. 가장 단순하고 안정적.
TODO(hw):보드 설계에 맞춰 1) 또는 2) 중 하나를 확정하고 BOM/스템핑을 결정한다. 펌웨어 측 RCCRMII클럭 선택(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.php의METRIC_*상수가 판정한다(펌웨어는 원값만 보고).
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)뿐이다.