실제 배포는 Raspberry Pi 4(raspberry_pi4_sht30)인데 다수 문서가 STM32를 "현재 단일보드"로 서술하던 드리프트를 교정(README/SYSTEM_OVERVIEW/ DEPLOYMENT_PLAN은 이미 Pi 기준). - SHT30_SENSOR2_SETUP.md: Pi 기본 전면 재작성, STM32는 마이그레이션 대안 - README.md / wiring_diagram.md / INSTALL_PI_SERVER.md: Pi 기본으로 재구성 - ops_checks.php / security_evidence.php: /etc/sht30-sensor.env → config_local.py - api/sensor_data.php: 헤더 예시 device_id rpi4-sht30-01 - examples/*.html: device_id 교정 + 구식 env 예제 폐기 배너 - 전압 경고 정정: "5V=소자 손상"은 틀림(모듈 2.4~5.5V). Pi에서 3.3V 쓰는 이유는 Pi GPIO 3.3V 전용(5V 시 SDA/SCL 5V → Pi 손상)
258 lines
12 KiB
Markdown
258 lines
12 KiB
Markdown
# STM32 펌웨어 및 Cafe24 서버 설치 설명서 v2606
|
||
|
||
이 문서는 서버실 온습도(SHT30) 모니터링 시스템 v2606을 실제 운영 환경에 배포할 때 STM32F407VGT6 펌웨어와 Cafe24 PHP/MySQL 서버에 각각 무엇을 설치하고 설정해야 하는지 정리한 설치용 문서입니다.
|
||
|
||
> **참고:** 이 문서는 **STM32 펌웨어 중심**의 설치 문서입니다. Raspberry Pi 4 모듈(누수+카메라 / SHT30) 배포자는 [docs/DEPLOYMENT_PLAN_RPI4x3.md](./DEPLOYMENT_PLAN_RPI4x3.md)와 [SYSTEM_OVERVIEW.md](../SYSTEM_OVERVIEW.md)를 먼저 참고하세요. 서버 설치 절차(§3~§6)는 공통입니다.
|
||
|
||
## 1. 설치 대상
|
||
|
||
| 구분 | 설치 위치 | 역할 |
|
||
|---|---|---|
|
||
| Cafe24 서버 | `public_html/raspi_leck_detecter/` | 센서 API 수신, DB 저장, 임계 판정, SMS 발송, 대시보드/보고서 제공 |
|
||
| Cafe24 MySQL | Cafe24 DB | 센서 로그, 온습도 측정값, SMS 로그, 장비 상태 저장 |
|
||
| STM32F407VGT6 (`sht30_fw`) | 펌웨어 플래시 | SHT30 I2C 측정, 5분 주기 보고, raw-body 서명 |
|
||
| 서버 cron | Cafe24 cron 또는 외부 스케줄러 | `cron_heartbeat.php` 주기 실행(장비 오프라인 감지) |
|
||
|
||
## 2. 설치 전 준비물
|
||
|
||
- STM32F407VGT6 보드 (STM32F4-DISCOVERY 계열 또는 동등 커스텀 PCB)
|
||
- SHT30 온습도 모듈 (한진데이터 P4422-3, I2C)
|
||
- LAN8720 Ethernet PHY 모듈 (RMII) + LAN 케이블
|
||
- I2C 풀업 저항 4.7kΩ × 2 (모듈 내장 풀업 시 생략)
|
||
- 외부 3.3V USB-UART 어댑터 (USART3 PD8/PD9 콘솔 로그용)
|
||
- Cafe24 PHP 호스팅
|
||
- Cafe24 MySQL DB
|
||
- Cafe24 SMS 서비스 계정 및 secure key
|
||
- 운영 담당자 SMS 수신 번호
|
||
- HTTPS 접속 가능한 도메인 또는 호스팅 경로 + Cafe24 루트 CA
|
||
|
||
배선은 [wiring_diagram.md](./wiring_diagram.md)를 먼저 적용합니다. SHT30 I2C(PB6 SCL / PB7 SDA, 주소 0x44, 3.3V/GND, 4.7kΩ 풀업)와 LAN8720 RMII 결선은 [firmware/docs/HARDWARE.md](../firmware/docs/HARDWARE.md)를 따릅니다.
|
||
|
||
## 3. 서버에 설치할 파일
|
||
|
||
Cafe24 웹 루트 아래에 **repo 의 `php/` 트리 전체를 그대로 업로드**합니다. 개별 파일을 선별해 올리면 누수 사진 파이프라인(`api/photo_upload.php`, `photo.php`, `var/photos/`)이 빠지기 쉽습니다.
|
||
|
||
```text
|
||
public_html/raspi_leck_detecter/ ← php/ 트리 전체 업로드
|
||
.htaccess
|
||
blocked.php
|
||
config.php
|
||
config.local.php ← 서버에서 example 복사 후 작성(저장소에 없음)
|
||
config.local.example.php
|
||
ops_checks.php
|
||
sms_send.php
|
||
cron_heartbeat.php
|
||
dashboard.php
|
||
photo.php ← 누수 사진 조회(관리자 인증 필요)
|
||
setup_mfa.php
|
||
admin_security.php
|
||
setup_wizard.php
|
||
security_evidence.php
|
||
monthly_report.php
|
||
login.php
|
||
retention_cleanup.php
|
||
setup_hash.php
|
||
test_mobile.php
|
||
api/
|
||
sensor_data.php
|
||
photo_upload.php ← 누수 사진 업로드 API
|
||
var/
|
||
.gitkeep
|
||
.htaccess ← var/ 웹 직접 접근 차단(403)
|
||
index.html
|
||
photos/ ← 누수 사진 저장 폴더(.htaccess/index.html 포함,
|
||
.htaccess 없으면 첫 업로드 시 자동 생성 시도)
|
||
index.html
|
||
```
|
||
|
||
운영 서버에는 `config.local.php`가 반드시 필요합니다. `config.local.example.php`를 복사해서 만들고 실제 운영 값으로 교체합니다.
|
||
|
||
```php
|
||
<?php
|
||
return [
|
||
'DB_HOST' => 'localhost',
|
||
'DB_PORT' => 3306,
|
||
'DB_NAME' => 'your_db_name',
|
||
'DB_USER' => 'your_db_user',
|
||
'DB_PASS' => 'your_db_password',
|
||
|
||
'API_KEY' => 'replace-with-long-random-secret',
|
||
|
||
'SMS_USER_ID' => 'your-cafe24-sms-user-id',
|
||
'SMS_SECURE' => 'your-cafe24-sms-secure-key',
|
||
'SMS_SENDER' => '01000000000',
|
||
'SMS_RECIPIENTS' => [
|
||
'01000000000',
|
||
],
|
||
|
||
'ADMIN_USER' => 'admin',
|
||
'ADMIN_PASSWORD_HASH' => '$2y$10$replace.with.password_hash.output',
|
||
'ADMIN_TOTP_SECRET' => 'replace-with-base32-secret',
|
||
'MFA_SETUP_TOKEN' => 'replace-with-temporary-random-token',
|
||
|
||
// 온습도 임계 override (기본값은 config.php 의 METRIC_*)
|
||
'METRIC_TEMP_HIGH_C' => 30,
|
||
'METRIC_TEMP_LOW_C' => 10,
|
||
'METRIC_RH_HIGH' => 70,
|
||
'METRIC_RH_LOW' => 20,
|
||
|
||
'SMS_LOG_RETENTION_DAYS' => 365,
|
||
'SENSOR_LOG_RETENTION_DAYS' => 365,
|
||
'SENSOR_METRIC_RETENTION_DAYS' => 365,
|
||
'ADMIN_AUDIT_RETENTION_DAYS' => 365,
|
||
];
|
||
```
|
||
|
||
`API_KEY`는 STM32 펌웨어 `firmware/common/secrets.h`의 `APP_API_KEY`와 반드시 바이트 단위로 같아야 합니다(raw-body 서명 `X-Signature = lowercase_hex(HMAC-SHA256(키=API_KEY, 메시지=요청본문 바이트))`).
|
||
|
||
`ADMIN_TOTP_SECRET`은 Google Authenticator, Microsoft Authenticator, Authy 등 TOTP 인증 앱에 등록한 Base32 비밀키입니다. 이 값이 없으면 관리자 로그인을 허용하지 않습니다. 담당자 변경 시 관리자 비밀번호와 TOTP 비밀키를 모두 교체하고 변경 기록을 운영 인수인계 자료에 남깁니다.
|
||
|
||
최초 등록은 `MFA_SETUP_TOKEN`을 임시로 설정한 뒤 다음 주소에서 진행합니다.
|
||
|
||
```text
|
||
https://your-domain.example/raspi_leck_detecter/setup_mfa.php?token=임시토큰
|
||
```
|
||
|
||
`setup_mfa.php`에서 생성된 수동 입력 키를 Google Authenticator에 등록하고 6자리 코드를 검증한 뒤, 화면에 표시되는 `ADMIN_TOTP_SECRET` 줄을 `config.local.php`에 반영합니다. 등록 완료 후 `MFA_SETUP_TOKEN`은 빈 값으로 바꾸거나 삭제합니다.
|
||
|
||
## 4. 서버 DB 설치
|
||
|
||
신규 설치라면 Cafe24 phpMyAdmin 또는 MySQL 콘솔에서 다음 파일을 실행합니다.
|
||
|
||
```sql
|
||
SOURCE /path/to/sql/schema_sht30.sql;
|
||
```
|
||
|
||
phpMyAdmin에서는 [schema_sht30.sql](../sql/schema_sht30.sql) 내용을 복사해서 SQL 실행 창에 붙여넣습니다. 생성 테이블은 총 7개 — `leak_sensor_log`, `leak_sensor_status`, `leak_sensor_metric`, `leak_sms_log`, `leak_ingest_nonce`(재전송 방어), `leak_incident`, `leak_photo`(누수 사고/사진) — 이며, 신규 설치는 이 파일 하나로 완결됩니다(누수 컬럼/테이블 포함). 모든 테이블은 `leak_` 접두사를 사용해 같은 DB 의 다른 테이블과 격리됩니다.
|
||
|
||
## 5. 서버 권한 및 관리자 설정
|
||
|
||
상태 파일 폴더는 PHP가 쓸 수 있어야 합니다.
|
||
|
||
```text
|
||
php/var/
|
||
```
|
||
|
||
관리자 비밀번호 해시는 서버 CLI에서 생성합니다.
|
||
|
||
```bash
|
||
php setup_hash.php "새관리자비밀번호"
|
||
```
|
||
|
||
출력된 해시를 `config.local.php`의 `ADMIN_PASSWORD_HASH`에 넣습니다. 관리자 인증 앱 비밀키도 `config.local.php`의 `ADMIN_TOTP_SECRET`에 설정합니다.
|
||
|
||
서버 설정 후 브라우저에서 아래 화면을 확인합니다.
|
||
|
||
```text
|
||
https://your-domain.example/raspi_leck_detecter/setup_wizard.php
|
||
```
|
||
|
||
점검 화면에서 확인할 항목은 다음과 같습니다.
|
||
|
||
- DB 연결
|
||
- 필수 테이블 존재 여부(`leak_sensor_log`, `leak_sensor_status`, `leak_sensor_metric`, `leak_sms_log`)
|
||
- API 키 기본값 교체 여부
|
||
- 관리자 해시 설정 여부
|
||
- 관리자 TOTP 설정 여부
|
||
- SMS 수신자 설정 여부
|
||
- 상태 파일 폴더 쓰기 권한
|
||
- 테스트 SMS 발송
|
||
|
||
## 6. 서버 cron 설정
|
||
|
||
장비 오프라인 감지를 위해 `cron_heartbeat.php`를 주기 실행합니다. Cafe24 cron 또는 외부 스케줄러에서 다음 URL을 1분 간격으로 호출합니다.
|
||
|
||
```text
|
||
https://your-domain.example/raspi_leck_detecter/cron_heartbeat.php?key=<CRON_SECRET>
|
||
```
|
||
|
||
HTTP 호출에는 `?key=<CRON_SECRET>` 파라미터가 **필수**입니다(누락 시 403). `CRON_SECRET`은 `API_KEY`에서 파생되며 다음으로 계산합니다: `php -r "echo hash_hmac('sha256','cron','<API_KEY>');"`
|
||
|
||
서버 CLI cron을 사용할 수 있다면 다음처럼 구성할 수 있습니다(CLI 실행에는 key가 필요 없습니다).
|
||
|
||
```cron
|
||
* * * * * php /home/hosting_user/public_html/raspi_leck_detecter/cron_heartbeat.php
|
||
```
|
||
|
||
호스팅 환경마다 실제 절대 경로가 다르므로 Cafe24 파일 관리자 또는 `phpinfo()`로 경로를 확인합니다. 마지막 수신 후 `HEARTBEAT_TIMEOUT_SEC`(기본 1200초=20분)을 초과하면 오프라인으로 판정합니다.
|
||
|
||
## 7. STM32 펌웨어 빌드 (폐쇄망)
|
||
|
||
폐쇄망 빌드 머신에서 펌웨어를 빌드합니다. 의존성 벤더링과 오프라인 빌드/플래시 절차는 [firmware/docs/BUILD_OFFLINE.md](../firmware/docs/BUILD_OFFLINE.md)를 따릅니다.
|
||
|
||
```bash
|
||
cp firmware/common/secrets.h.example firmware/common/secrets.h
|
||
# secrets.h 의 APP_API_KEY 를 서버 config.local.php 의 API_KEY 와 바이트 단위로 동일하게 채운다.
|
||
```
|
||
|
||
빌드 전 확인할 설정값:
|
||
|
||
| 위치 | 설정 |
|
||
|---|---|
|
||
| `firmware/common/secrets.h` | `APP_API_KEY` = 서버 `API_KEY` |
|
||
| `firmware/common/app_config.h` | `APP_API_HOST`, `APP_API_PATH`(`/raspi_leck_detecter/api/sensor_data.php`), 네트워크(DHCP/static) |
|
||
| `firmware/common/app_config.h` | `APP_SHT30_REPORT_INTERVAL_SEC`(기본 300초), `APP_SHT30_I2C_ADDR`(0x44) |
|
||
| `firmware/certs/server_ca.c` | 자리표시자 CA → 실제 Cafe24 루트 CA |
|
||
|
||
`sht30_fw` 타깃을 빌드해 `sht30_fw.bin`을 생성하고 보드에 플래시합니다.
|
||
|
||
## 8. STM32 펌웨어 동작 확인
|
||
|
||
USART3(PD8 TX / PD9 RX, 115200 8N1)에 외부 3.3V USB-UART 어댑터를 연결해 콘솔 로그를 확인합니다.
|
||
|
||
첫 부팅 후 확인할 항목:
|
||
|
||
- I2C1 초기화 및 SHT30 주소 0x44 응답
|
||
- 첫 측정값(온도/습도)
|
||
- 네트워크(DHCP/static) → SNTP 시간 동기 → TLS 핸드셰이크
|
||
- 서버 200 응답 및 `startup` 보고
|
||
|
||
상태 LED(PD12) 패턴: 부팅=점등 / 정상 보고=느린 토글 / 망 단절·TLS 실패=빠른 점멸.
|
||
|
||
## 9. 설치 후 최종 확인
|
||
|
||
서버에서 확인합니다.
|
||
|
||
```text
|
||
https://your-domain.example/raspi_leck_detecter/setup_wizard.php
|
||
https://your-domain.example/raspi_leck_detecter/dashboard.php
|
||
https://your-domain.example/raspi_leck_detecter/monthly_report.php
|
||
https://your-domain.example/raspi_leck_detecter/security_evidence.php
|
||
```
|
||
|
||
정상 설치 기준은 다음과 같습니다.
|
||
|
||
- `setup_wizard.php`의 필수 점검 항목이 통과
|
||
- `dashboard.php`에 최신 온도/습도와 센서 상태(`sensor_id=2`)가 표시
|
||
- `security_evidence.php`에서 운영 점검 결과와 보안통제 매트릭스 조회 가능
|
||
- STM32 콘솔 로그에 서버 200 응답 기록 확인
|
||
- `leak_sensor_metric`에 주기 측정값(periodic) 저장
|
||
- 임계 초과 테스트 시 `metric_status` 경보 기록 및 SMS 수신
|
||
- 월간 보고서에서 임계 경보/복귀/오프라인 내역 조회 가능
|
||
|
||
## 10. 문제 해결
|
||
|
||
| 증상 | 확인 지점 |
|
||
|---|---|
|
||
| API 403 | 서버 `API_KEY`와 펌웨어 `APP_API_KEY` 일치 여부, raw-body 서명 |
|
||
| API 500 | Cafe24 PHP 오류 로그, DB 접속 정보, 테이블 생성 여부 |
|
||
| 로그인 불가 | `ADMIN_PASSWORD_HASH`, `ADMIN_TOTP_SECRET`, 인증 앱 시간 동기화 |
|
||
| SMS 미발송 | Cafe24 SMS 계정, secure key, 발신번호 등록, 잔액, `leak_sms_log` |
|
||
| 온습도 미표시 | I2C 배선(PB6/PB7, 0x44), 4.7kΩ 풀업, `leak_sensor_metric` 테이블, 펌웨어 측정 로그 |
|
||
| 임계 경보 미발송 | `config.php`/`config.local.php`의 `METRIC_*`, `METRIC_ALERT_COOLDOWN_SEC`(1800초) |
|
||
| TLS 실패 | `firmware/certs/server_ca.c` 실제 CA 반영, SNTP 시간 동기(인증서 유효기간 검증) |
|
||
| 오프라인 알림 과다/지연 | `HEARTBEAT_CHECK_INTERVAL_SEC`, `HEARTBEAT_TIMEOUT_SEC`(기본 1200초), cron 주기 |
|
||
|
||
## 11. 운영자가 보관해야 할 값
|
||
|
||
다음 값은 코드 저장소에 넣지 말고 운영자만 별도로 보관합니다.
|
||
|
||
- Cafe24 DB 이름, 계정, 비밀번호
|
||
- `API_KEY` / 펌웨어 `APP_API_KEY`
|
||
- Cafe24 SMS user id
|
||
- Cafe24 SMS secure key
|
||
- SMS 발신번호
|
||
- SMS 수신자 번호
|
||
- 관리자 계정과 원문 비밀번호, `ADMIN_TOTP_SECRET`
|
||
- 서버 업로드 경로
|
||
- 장비 ID(`rpi4-sht30-01`, 배포=Raspberry Pi 4)와 설치 위치
|