#!/usr/bin/env python3 """센서/포맷 자가점검 — Pi 4 SHT30(2번) 모듈. 1) CRC-8 / fixed2 / 본문 포맷이 펌웨어·서버 규약과 일치하는지 단위 점검 2) 실제(또는 시뮬레이션) SHT30 측정 1회 → 변환값 출력 하드웨어 없이도 1)은 항상 통과해야 한다(서명 호환성의 핵심 보증). """ import sys import wire from sht30_driver import crc8, parse_frame, SHT30, SMBUS_AVAILABLE # 포맷/CRC 자가점검은 비밀값(config_local.py) 없이도 가능해야 한다. # config.py 는 config_local.py 가 없으면 import 시 fail-closed 로 중단하므로, # 개발 PC 점검을 위해 기본값으로 폴백한다(서버 연결은 test_connection.py 담당). try: from config import I2C_BUS, I2C_ADDR, FORCE_SIMULATION except RuntimeError: I2C_BUS, I2C_ADDR, FORCE_SIMULATION = 1, 0x44, False print(" [info] config_local.py 없음 → I2C 기본값(bus=1, 0x44)으로 점검") def check(name, got, want): ok = got == want print(f" [{'OK ' if ok else 'FAIL'}] {name}: got={got!r} want={want!r}") return ok print("=" * 50) print(" SHT30 자가점검 (2번 모듈)") print("=" * 50) passed = True # ── 1) CRC-8 점검 (Sensirion 표준 벡터 0xBEEF → 0x92) ── print("\n[1] CRC-8 (poly 0x31, init 0xFF):") passed &= check("crc8(0xBEEF)", crc8(b"\xBE\xEF"), 0x92) # ── 2) fixed2 점검 (round-half-up, reference.py/jb_fixed2 동일) ── print("\n[2] fixed2 (소수 2자리 round-half-up):") passed &= check("24.0", wire.fixed2(24.0), "24.00") passed &= check("48.5", wire.fixed2(48.5), "48.50") passed &= check("-3.245", wire.fixed2(-3.245), "-3.25") passed &= check("0.0", wire.fixed2(0.0), "0.00") # ── 3) 프레임 파싱 점검 (CRC 포함 합성 프레임) ── print("\n[3] parse_frame (합성 프레임 변환):") raw_t, raw_rh = 0x6400, 0x8000 # 임의 raw 값 frame = bytes([ raw_t >> 8, raw_t & 0xFF, crc8(bytes([raw_t >> 8, raw_t & 0xFF])), raw_rh >> 8, raw_rh & 0xFF, crc8(bytes([raw_rh >> 8, raw_rh & 0xFF])), ]) t, rh = parse_frame(frame) exp_t = -45.0 + 175.0 * (raw_t / 65535.0) exp_rh = 100.0 * (raw_rh / 65535.0) passed &= check("temp_c", round(t, 4), round(exp_t, 4)) passed &= check("rh", round(rh, 4), round(exp_rh, 4)) # ── 4) 본문 포맷 점검 (필드 순서/타입) ── print("\n[4] 본문 포맷 (필드 순서 고정):") body = wire.sht30_event_body("dev", "loc", 2, "s2", "periodic", 1700000000, 24.0, 48.0, "normal", "vX") expected = ('{"device_id":"dev","device_location":"loc","sensor_id":2,' '"sensor_name":"s2","event_type":"periodic","timestamp":1700000000,' '"metric_type":"sht30","temperature_c":24.00,"humidity_percent":48.00,' '"metric_status":"normal","app_version":"vX"}') passed &= check("body", body, expected) print(f" 서명(key=test): {wire.sign_raw('test', body)[:16]}...") # ── 5) 실제/시뮬레이션 측정 1회 ── print("\n[5] 측정 1회:") sim = FORCE_SIMULATION or not SMBUS_AVAILABLE print(f" 모드: {'시뮬레이션' if sim else f'실제 I2C(bus={I2C_BUS},0x{I2C_ADDR:02X})'}") try: with SHT30(bus_num=I2C_BUS, addr=I2C_ADDR, simulate=sim) as s: t, rh = s.measure() print(f" 측정값: T={t:.2f}C RH={rh:.2f}%") except Exception as e: print(f" 측정 실패: {e}") if not sim: print(" (i2cdetect -y 1 로 0x44 응답 확인 / 배선·풀업·I2C 활성화 점검)") print("\n" + "=" * 50) print(f" 결과: {'전체 통과 ✅' if passed else '실패 항목 있음 ❌'}") print("=" * 50) sys.exit(0 if passed else 1)