2번 모듈 신규(raspberry_pi4_sht30/): SHT30 온습도 → cafe24, raw-body X-Signature HMAC, 폐쇄망 오프라인 설치(vendor/install), systemd, CRC/포맷 자가점검. firmware sht30_fw wire 포맷과 byte-parity 검증. 1번 모듈 통합(raspberry_pi4/): 누수+카메라를 동일 서버로. 레거시 sha256(key+json) body-sig → X-Signature HMAC 전환, 사진 canonical=device_id|sensor_id|log_id|timestamp. 서버 통합(php/): api/sensor_data.php 누수 이벤트 수신, api/photo_upload.php 신규(HMAC·finfo·서버생성 파일명·nonce 멱등·sensor 결속), photo.php 인증 사진서빙, dashboard.php 누수 패널+사진, sms_send.php 누수 SMS, config.php 태그/요약. DB: sql/migration_add_leak.sql(가산·멱등), schema_sht30.sql 통합 스키마. 보안 하드닝(외부 펜테스트 대비, 확정 CRITICAL/HIGH 0): cron_heartbeat api_key fail-closed, setup_mfa 부트스트랩 토큰 post-enrollment 잠금, var/ PHP 실행차단, HSTS. 문서/테스트: SYSTEM_OVERVIEW.md, security_hardening_test.php 갱신. 참고: 사용자 요청으로 세션 외 기존 미커밋 변경도 함께 포함. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
156 lines
6.6 KiB
Markdown
156 lines
6.6 KiB
Markdown
# 온습도(SHT30) 센서 모듈 — Raspberry Pi 4 Model B 전용 (2번 모듈)
|
||
|
||
**Sensirion SHT30**(한진데이터 P4422-3, I2C) 온습도 센서를 Raspberry Pi 4 Model B 의
|
||
I2C 버스로 읽어, 주기적으로 cafe24 서버(`php/api/sensor_data.php`)에 보고하는 모듈입니다.
|
||
서버가 측정값을 저장하고, **고온/저온/고습/저습 임계 판정과 SMS, 대시보드 표시**를 모두 수행합니다.
|
||
|
||
> 이 폴더는 STM32F407 펌웨어(`sht30_fw`)의 동작을 **Raspberry Pi 4 + Python 으로 이식**한
|
||
> "2번 모듈"입니다. wire 포맷(본문/서명)·CRC·변환식은 펌웨어와 **바이트 단위로 동일**합니다.
|
||
> "1번 모듈"(누수+카메라)은 `../raspberry_pi4/` 폴더입니다. 전체 구성은 루트의
|
||
> [`SYSTEM_OVERVIEW.md`](../SYSTEM_OVERVIEW.md)를 보세요.
|
||
|
||
---
|
||
|
||
## 1. 구성 파일
|
||
|
||
| 파일 | 역할 |
|
||
|---|---|
|
||
| `sht30_sensor.py` | 메인 — 주기 측정 → 본문 생성/서명 → POST(재시도), startup→periodic |
|
||
| `sht30_driver.py` | SHT30 I2C 측정 + CRC-8 검증 + 물리값 변환 (smbus2, 시뮬레이션 폴백) |
|
||
| `wire.py` | 본문 JSON + raw-body HMAC 서명 (펌웨어 `jsonbody.c`/`sig.c`와 바이트 동일) |
|
||
| `config.py` | 비밀 아닌 기본 설정(센서 id/주기/범위/ I2C). `config_local` 에서 비밀값 로드 |
|
||
| `config_local.example.py` | 운영·비밀값 템플릿 → `config_local.py` 로 복사해 사용(미커밋) |
|
||
| `requirements.txt` | Python 의존성(`requests`, `smbus2`, 핀 고정) |
|
||
| `install.sh` | Pi 4 설치(폐쇄망 오프라인 + 온라인 폴백, I2C 활성화 포함) |
|
||
| `vendor.sh` | 폐쇄망용 의존성 사전 다운로드(빌드 머신에서 실행) |
|
||
| `test_connection.py` | 서버 연결/서명 POST 점검 |
|
||
| `test_sensor.py` | CRC/포맷 자가점검 + 측정 1회(하드웨어 없이도 포맷 점검 통과) |
|
||
| `systemd/sht30-sensor.service` | 부팅 자동 실행 서비스 유닛 |
|
||
|
||
---
|
||
|
||
## 2. 하드웨어 (Pi 4 Model B 기준)
|
||
|
||
- **Raspberry Pi 4 Model B** (BCM2711) + Raspberry Pi OS **Bookworm 64-bit**
|
||
- **SHT30** 온습도 센서 모듈 (I2C, 한진데이터 P4422-3)
|
||
- I2C 풀업 저항 **4.7kΩ × 2** (모듈에 내장된 경우 생략)
|
||
- 전원: **USB-C 5V/3A**, 네트워크: Pi 4 **Gigabit Ethernet**(유선) 또는 WiFi
|
||
|
||
### 2.1 핀 배치 (I2C 버스 1)
|
||
|
||
| 신호 | Pi 4 핀 | 비고 |
|
||
|---|---|---|
|
||
| SHT30 VCC | 3.3V (물리 1) | **5V 금지** |
|
||
| SHT30 GND | GND (물리 6/9/14/…) | |
|
||
| SHT30 SDA | GPIO2 / SDA1 (물리 3) | + 4.7kΩ 풀업 → 3.3V |
|
||
| SHT30 SCL | GPIO3 / SCL1 (물리 5) | + 4.7kΩ 풀업 → 3.3V |
|
||
| SHT30 ADDR | GND → 주소 **0x44** | VDD 면 0x45 (`config.py` `I2C_ADDR`) |
|
||
|
||
---
|
||
|
||
## 3. 서버 호환성 (반드시 확인)
|
||
|
||
이 모듈은 **현재 repo 의 `php/` 서버(=cafe24 SHT30 대시보드 서버)**로 보고합니다.
|
||
서버는 **raw-body HMAC 서명**을 요구합니다.
|
||
|
||
```
|
||
X-Signature = lowercase_hex( HMAC-SHA256( key=API_KEY, msg=요청본문 바이트 ) )
|
||
```
|
||
|
||
- `config_local.py` 의 `API_KEY` 는 서버 `config.local.php` 의 `API_KEY` 와 **정확히 동일**해야 합니다.
|
||
- 서버는 `timestamp` 신선도(±600초)로 재전송을 막으므로 Pi 시각이 **NTP 로 동기**되어 있어야 합니다(Raspberry Pi OS 기본 활성).
|
||
- `sensor_id=2` 로 보고하며, 서버 대시보드(`dashboard.php`)는 기본 차트로 sensor 2 를 표시합니다.
|
||
- 임계 경보(고온/저온/고습/저습)·SMS·복구 판정은 **모두 서버**가 수행합니다. 이 모듈은 측정값만 보고합니다.
|
||
|
||
> 서버 측 스키마/배포는 [`../sql/schema_sht30.sql`](../sql/schema_sht30.sql),
|
||
> [`../docs/INSTALL_PI_SERVER.md`](../docs/INSTALL_PI_SERVER.md),
|
||
> [`../docs/SHT30_SENSOR2_SETUP.md`](../docs/SHT30_SENSOR2_SETUP.md) 참고.
|
||
|
||
---
|
||
|
||
## 4. 설치
|
||
|
||
### 4.1 설정값 준비
|
||
|
||
```bash
|
||
cp config_local.example.py config_local.py
|
||
# config_local.py 를 열어 API_URL / API_KEY / DEVICE_ID / DEVICE_LOCATION 채움
|
||
```
|
||
|
||
`config_local.py` 가 없으면 실행 시 **명확한 오류로 즉시 중단**됩니다(fail closed).
|
||
`config_local.py` 와 `API_KEY` 는 **절대 커밋하지 마세요**(`.gitignore` 처리됨).
|
||
|
||
### 4.2 온라인 설치 (인터넷 가능 환경)
|
||
|
||
```bash
|
||
chmod +x install.sh
|
||
./install.sh
|
||
sudo reboot # I2C 를 새로 켠 경우 1회 재부팅
|
||
```
|
||
|
||
### 4.3 폐쇄망(air-gapped) 오프라인 설치
|
||
|
||
인터넷이 되는 **동일 아키텍처(aarch64, 가급적 동일 Pi 4 + Bookworm 64-bit)** 머신에서
|
||
의존성을 먼저 받아 번들을 만든 뒤, 타깃 Pi 4 로 옮겨 설치합니다.
|
||
|
||
```bash
|
||
# (빌드 머신, 인터넷 O) wheelhouse/ + debs/ 생성
|
||
./vendor.sh
|
||
|
||
# wheelhouse/ 와 debs/ 를 타깃 Pi 4 의 이 폴더로 복사한 뒤:
|
||
# (타깃 Pi 4, 인터넷 X)
|
||
./install.sh # debs/ · wheelhouse/ 가 있으면 자동으로 오프라인 설치
|
||
```
|
||
|
||
> `smbus2` 는 순수 파이썬(arch 무관)이라 x86 빌드 머신에서 받아도 됩니다.
|
||
> `requests` 등 다른 wheel/deb 는 **aarch64** 용을 받아야 합니다(타깃 arch 에서 `vendor.sh` 권장).
|
||
|
||
---
|
||
|
||
## 5. 실행 / 점검
|
||
|
||
```bash
|
||
# 센서가 보이는지 (0x44)
|
||
i2cdetect -y 1
|
||
|
||
# 포맷/CRC 자가점검 + 측정 1회 (하드웨어 없으면 시뮬레이션)
|
||
source venv/bin/activate
|
||
python3 test_sensor.py
|
||
|
||
# 서버 연결/서명 점검
|
||
python3 test_connection.py
|
||
|
||
# 실제 실행
|
||
python3 sht30_sensor.py
|
||
```
|
||
|
||
`smbus2` 미설치 또는 I2C 미활성 환경(개발 PC 등)에서는 자동으로 **시뮬레이션 모드**로 동작합니다.
|
||
|
||
---
|
||
|
||
## 6. 부팅 자동 실행 (systemd)
|
||
|
||
```bash
|
||
# 폴더를 /home/pi/raspberry_pi4_sht30 에 배치했다고 가정 (다르면 .service 의 경로 수정)
|
||
sudo cp systemd/sht30-sensor.service /etc/systemd/system/
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable sht30-sensor
|
||
sudo systemctl start sht30-sensor
|
||
sudo systemctl status sht30-sensor
|
||
journalctl -u sht30-sensor -f
|
||
```
|
||
|
||
`SupplementaryGroups=i2c` 가 `/dev/i2c-1` 접근에 필요합니다.
|
||
|
||
---
|
||
|
||
## 7. 보안 메모
|
||
|
||
- 비밀값(`API_KEY`)·서버 URL·기기 정보는 `config_local.py`(미커밋)에만 둡니다.
|
||
커밋되는 파일에는 placeholder 만 존재합니다.
|
||
- 보고 본문은 `HMAC-SHA256(API_KEY, raw_body)` 서명(`X-Signature` 헤더)으로 위변조를 방지합니다
|
||
(길이확장 공격에 면역). 레거시 body-field sha256 폴백은 서버에서 기본 비활성입니다.
|
||
- 서명하는 바이트와 전송하는 바이트는 **항상 동일**합니다(`wire.py` 가 본문을 직접 만들어
|
||
그 바이트에 서명 후 raw POST). 펌웨어 `sig.c` / `reference.py` / 서버 `verify_signature_raw`
|
||
와 바이트 단위로 일치합니다.
|
||
- `config_local.py` 노출 시 즉시 `API_KEY` 를 교체(서버 동시 변경)하세요.
|