- tools/make_deploy_bundles.py: PMS7003·Zero2W 디스플레이 노드 번들 생성 반영(폐쇄망 동봉) - tools/harden_pi.sh: 하드닝 스크립트 갱신 - docs/superpowers: 노드 센서 통합·스모크 파일럿·대시보드 다크 설계/계획 문서 - docs/claude-config: IoT 납품·검증 스킬/룰 참조본 - review/: 프로젝트 리뷰·UI v2 목업
110 lines
6.6 KiB
Markdown
110 lines
6.6 KiB
Markdown
# 설계: 누수 노드 다중센서 통합 (누수 + 온습도 + 미세먼지) · 상시 카메라 조명
|
|
|
|
**날짜:** 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":<int>,"metric_type":"pms7003","pm1_0":<int>,"pm2_5":<int>,"pm10":<int>,
|
|
"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 기반 자동 카메라 캡처 트리거 — 별개 이슈(이번 범위 밖).
|
|
- 카메라 조명 밝기 제어/스위칭 — 상시점등이라 불필요.
|