# 설계: 누수 노드 다중센서 통합 (누수 + 온습도 + 미세먼지) · 상시 카메라 조명 **날짜:** 2026-07-19 **브랜치:** feat/pi-c-sensor3-leak-node **상태:** 설계 확정, 구현 진행 **대체:** [`2026-07-18-smoke-node-pilot-design.md`](./2026-07-18-smoke-node-pilot-design.md) (연기 개조/불꽃/CO 경로 폐기) **참조 계획:** [`../plans/2026-07-18-smoke-node-pilot.md`](../plans/2026-07-18-smoke-node-pilot.md) (연기 시범 → PMS7003 metric 통합으로 전환) ## 목표 기존 누수 노드(Pi-A = sensor_id 1, Pi-C = sensor_id 3)를 **한 노드에 3종 센서**를 얹은 통합 노드로 확장한다: 1. **누수** — 기존 leak GPIO(GPIO17, active-high). 변경 없음. 2. **온습도(SHT30)** — I2C metric(`metric_type=sht30`). 이미 통합됨(2026-07-15 설계). 3. **미세먼지(PMS7003)** — UART metric(`metric_type=pms7003`). **이번 신규.** 연기(화재)의 **대리 지표(smoke proxy)** — 입자 유입 시 PM 농도가 급등한다. 세 센서 모두 **그 노드의 같은 sensor_id(1 또는 3)로 보고**하고, `metric_type` 문자열로만 구분한다. SHT30 동일-id 통합 방식을 그대로 미러(새 id·재번호 없음). 추가로, 누수 확인 카메라(NoIR)용 **상시점등 백색 LED**를 배선한다(스위칭·코드 없음). ## 핵심 결정 (확정) 1. **동일 sensor_id · metric_type 구분.** `leak_sensor_metric`(sensor_id + 측정시각 키)이 온습도·미세먼지를 한 테이블에서 `metric_type`으로 구분 → 한 sensor_id가 누수 상태 + 온습도 측정 + 미세먼지 측정을 동시에 가진다. PM 행은 `pm1_0/pm2_5/pm10`만 채우고 `temperature_c/humidity_percent`는 NULL(반대도 동일) — additive 컬럼. 2. **노드: 별도 서비스.** 불안정한 누수 코드는 건드리지 않는다. PMS7003 는 SHT30 처럼 **독립 systemd 서비스**(`pms7003-sensor`)로 얹는다. config 의 `SENSOR_ID`만 노드별(1/3). 3. **event_type:** 첫 성공 판독 = `startup`, 이후 `periodic`(SHT30 와 동일). PMS 는 누수/알람 이벤트를 절대 방출하지 않는다 — 임계 판정·SMS 는 전적으로 서버 몫. 4. **Pi metric_status = 물리 타당성만**(`normal` | `out_of_range`). 서버가 운영 임계를 재평가 하므로 Pi 의 status 를 경보 근거로 신뢰하지 않는다. 5. **경보/수신자:** 전역 `METRIC_PM25_HIGH`(75)·`METRIC_PM10_HIGH`(150) 초과 시 서버가 판정 → `[미세먼지경보]` SMS. 수신자는 기존 sensor_id 1·3 그룹 그대로(추가 매핑 불필요). 6. **카메라 조명: 상시점등.** 스위칭·GPIO 제어 없음. 백색 LED 모듈을 Pi 5V 에 직결해 항상 켠다. ## 데이터 흐름 / 페이로드 ``` Pi-A (한 대) Pi-C: 동일, sensor_id=3 ├ leak-sensor.service → POST(sensor_id=1) leak_alert/recovery/periodic (누수) ├ sht30-sensor.service → POST(sensor_id=1) startup/periodic (metric_type=sht30, 온·습도) └ pms7003-sensor.service → POST(sensor_id=1) startup/periodic (metric_type=pms7003, PM) 서버 sensor_data.php: sensor_id=1 metric 저장(metric_type 구분) + 임계 재평가 → high_pm25/high_pm10 breach → [미세먼지경보] SMS (SENSOR_RECIPIENTS[1]) → is_leak 보존(비-누수 이벤트는 누수 상태를 덮지 않음) 대시보드: sensor_id=1 → 누수 칩 + 온습도 칩 + 미세먼지 칩 ``` **PMS7003 보고 바디 (Pi 가 이 바이트 순서 그대로 만들어 HMAC 서명; KEY 이름 서버와 일치):** ```json {"device_id":"..","device_location":"..","sensor_id":1,"sensor_name":"..","event_type":"periodic", "timestamp":,"metric_type":"pms7003","pm1_0":,"pm2_5":,"pm10":, "metric_status":"normal","app_version":".."} ``` - `pm1_0/pm2_5/pm10` = **맨 정수 µg/m³**(대기환산), 범위 0..1000. - HTTP User-Agent: `PMS7003Sensor/2.0` (WAF 우회용 커스텀 UA). ## 서버 (이미 반영됨 — 참고) - 스키마 `leak_sensor_metric`: `pm1_0/pm2_5/pm10 SMALLINT UNSIGNED NULL` additive. - `config.php`: `METRIC_PM25_HIGH`(75) · `METRIC_PM10_HIGH`(150) · `METRIC_PM_HYSTERESIS`(10), 쿨다운은 `METRIC_ALERT_COOLDOWN_SEC` 재사용. - 새 breach kind `high_pm25` · `high_pm10` → 둘 다 `SMS_TAG_HIGH_PM = '[미세먼지경보]'` 매핑. ## 하드웨어 배선 ### PMS7003 (UART, 5V) — TXD 만 읽으면 됨 | 신호 | PMS7003 | Pi 4 | 비고 | |---|---|---|---| | 전원 | VCC | **5V (물리 pin2)** | 레이저 팬 구동, 5V 필수 | | 접지 | GND | **GND (물리 pin6)** | | | 데이터 | TXD | **RXD = GPIO15 (물리 pin10)** | PMS → Pi 단방향(읽기) | - **UART 활성화 필수:** `enable_uart=1` + 직렬 로그인 콘솔 분리(포트 점유 해제). `install.sh`가 자동 처리, 첫 활성화면 재부팅 필요(migrate.sh 가 안내). 포트 = `/dev/serial0`. - **버스 무충돌:** UART(GPIO14/15)는 누수 GPIO17·SHT30 I2C(GPIO2/3)와 겹치지 않는다 → 세 센서 병행 가능. ### 카메라 조명 (상시점등 백색 LED) | 신호 | LED 모듈 | Pi 4 | 비고 | |---|---|---|---| | 전원 | VCC | **5V** | | | 접지 | GND | **GND** | | - **상시 켜짐 · 스위칭/코드 없음.** 저전력 모듈이라 발열 무해, 상시점등해도 문제 없음. - **NoIR + 백색광 주의:** NoIR 카메라 + 백색 LED 는 화면 색이 **분홍빛**으로 나온다(누수 여부 식별에는 지장 없음). 색을 정상으로 원하면 **일반 카메라(IR-cut 필터 내장)**를 쓴다. - 자세한 배선/주의는 `raspberry_pi4/WIRING.html` "Camera Lighting (Always-On)" 절 참조. ## 배포 (기존 절차 재사용) - 소스 트리: PMS7003 Pi 코드는 `raspberry_pi4_pms7003/`(SHT30 소스 미러 — `pms7003_sensor.py`/`pms7003_driver.py`/`install.sh`/`vendor.sh`/`systemd/pms7003-sensor.service`). - 기존 하드닝 노드(SSH off) 대상 **USB 원샷 마이그레이션**: `tools/migration_leak_add_pms7003/` → 생성물 `deploy/07_migration_leak_add_pms7003/`. `migrate.sh`가 노드의 기존 `config_local.py` 에서 API_URL/API_KEY/위치/sensor_id 를 자동 추출해 같은 id 로 통합(시크릿 미포함 번들). - 폐쇄망: 빌드 PC 에서 `cd pms7003 && bash vendor.sh` → `debs/`·`wheelhouse/` 동봉. - 서버: `config.local.php`의 `METRIC_SENSOR_IDS` 에 1·3 포함 확인(온습도와 동일 목록). ## 비목표 (YAGNI) - Pi-B(sensor_id 2, 온습도 전용) 변경 없음. - 연기 개조/불꽃/CO/릴레이 감지 경로 — 전면 폐기(smoke proxy = PM 농도로 대체). - PM 기반 자동 카메라 캡처 트리거 — 별개 이슈(이번 범위 밖). - 카메라 조명 밝기 제어/스위칭 — 상시점등이라 불필요.