Files
HECO2/Protocol/HOMENET/260607_PCDashBoard_API_및_프로토콜.md
T
jeon 5a96a696b1 chore: HERV 통합 저장소 초기 커밋
- 펌웨어(program), C# 대시보드(TestProgram), 시뮬레이터(Simulator),
  프로토콜/문서(Protocol, doc) 전체를 단일 저장소로 통합
- program 폴더의 별도 git 저장소를 제거하고 통합 저장소에 흡수
- 빌드 산출물(program/build, bin/obj, *.o/.elf/.bin/.hex 등) .gitignore 처리
- 사내 Synology NAS Git 원격 연결 예정

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 21:44:23 +09:00

263 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PCDashBoard ↔ ERV 통신 — API 함수 & 프로토콜 상세 (2026.06.07)
PC 대시보드(ErvDashboard) ↔ ERV 메인보드 통신을 **485 프로토콜을 그대로 사용하되 `IErvApi` 함수로 감싼**
구조(인프로세스 API 파사드)로 정리한 문서. 선로 통신은 변함없이 485 프레임이며, UI 코드는 바이트/프레임을
다루지 않고 의미 있는 API 함수만 호출한다.
---
## 0. 구조 개요
```
[MainWindow.xaml.cs (UI)]
│ _api.SetPower(true) / SetFan(2) / RequestStatus() ...
[IErvApi] ←─ 인터페이스 (TestProgram/PCDashBoard/Api/IErvApi.cs)
[SerialErvApi] ←─ 구현 (Api/SerialErvApi.cs)
│ CtrlFrame 으로 485 프레임 빌드 / FrameParser 로 STATUS 파싱
[SerialChannel] ── RS485 / USB-Serial ── [ERV 메인보드]
```
- 송신 : `IErvApi.SetXxx()``CtrlFrame.Xxx()``0xAA … CRC16` 프레임 생성 → 시리얼 전송
- 수신 : 시리얼 바이트 → `FrameParser``STATUS(0x81)` 디코드 → `StatusReceived(StatusRecord)` 이벤트
- UI 매핑 : `StatusRecord``DashboardState``StatusMapper.Apply()`(UI 측)가 담당 → API 는 UI 비종속
### 파일 구성
| 파일 | 역할 |
|---|---|
| `PCDashBoard/Api/IErvApi.cs` | 통신 API 인터페이스(함수/이벤트 정의) |
| `PCDashBoard/Api/SerialErvApi.cs` | RS485 구현(SerialChannel + FrameParser + CtrlFrame 내장) |
| `PCDashBoard/Api/StatusMapper.cs` | `StatusRecord → DashboardState` 매핑 |
| `PCDashBoard/Protocol/SerialChannel.cs` | 시리얼 포트 송수신(byte) |
| `ErvProtocol/CtrlFrame.cs` | PC→ERV 제어 프레임 빌더 |
| `ErvProtocol/StatusDecoder.cs` | ERV→PC STATUS 디코더 |
| `ErvProtocol/FrameParser.cs` | 0xAA/LEN/CRC 프레임 분리 |
| `ErvProtocol/Enums.cs` | RunMode/SubModeType/HystPreset/AirQuality/AutoState |
---
## 1. API 함수 (IErvApi)
### 1.1 연결
| 함수 / 속성 | 설명 |
|---|---|
| `bool Connect(string port, int baud)` | 시리얼 연결 (대시보드 기본 115200) |
| `void Disconnect()` | 연결 해제 |
| `bool IsConnected { get; }` | 연결 여부 |
| `static string[] SerialErvApi.GetAvailablePorts()` | 사용 가능한 COM 포트 목록 |
| `event Action<bool> ConnectionChanged` | 연결 상태 변경 통지 |
### 1.2 수신
| 함수 / 이벤트 | 설명 |
|---|---|
| `event Action<StatusRecord> StatusReceived` | STATUS(0x81) 디코드 완료 시 발생 |
| `event Action<string> Log` | 프레임 hex / 이벤트 로그 |
| `void RequestStatus()` | STATUS 1회 즉시 요청 (CMD 0x0A) |
| `bool GetRoomStatus(int room, out bool damperSa, out bool damperEa, out AirQuality airQuality, out int led)` | 최근 STATUS 기준 각실(room 1~4) 급기·배기 댐퍼/공기질/LED 조회. 수신 이력 없거나 room 범위 밖이면 false |
### 1.3 제어 함수 ↔ 프로토콜 CMD 매핑
| API 함수 | CMD | PAYLOAD | 비고 |
|---|---|---|---|
| `SetPower(bool on)` | 0x01 | `[onoff]` | 전원 ON/OFF |
| `SetRunMode(RunMode mode)` | 0x02 | `[mode]` | 환기/자동/공청/바이패스 |
| `SetFan(int speed)` | 0x03 | `[speed]` | 0~4, 자동모드 시 무시 |
| `SetSubMode(SubModeType type, bool on)` | 0x04 | `[type][onoff]` | 스마트수면/쾌적조리/안심회복 |
| `SetHood(bool on)` | 0x05 | `[onoff]` | 연동후드 |
| `SetHystPreset(HystPreset preset)` | 0x06 | `[preset]` | ECO/NORMAL/TURBO |
| `SetHystDeadband(int preset, int pm25, int pm10, int voc, int co2)` | 0x07 | `[preset][pm25(2)][pm10(2)][voc(2)][co2(2)]` | 프리셋별 히스(하강) 값 |
| `SetDiffuserDamper(int room, int type, bool open)` | 0x08 | `[room][type][onoff]` | room 1~4, type 0=급기(SA)/1=배기(EA) |
| `SetDiffuserLed(int room, int dim)` | 0x09 | `[room][dim]` | dim 0~9 |
| `RequestStatus()` | 0x0A | (없음) | 상태 요청 |
| `SetReset(bool on)` | 0x0B | `[onoff]` | ERV 리셋 토글 |
| `SetVsp(int group, int index, int sa, int ea)` | 0x0C | `[group][index][sa(2)][ea(2)]` | 풍량 VSP 1엔트리 |
| `SetHystThreshold(int preset, int pollutant, int l1, int l2, int l3, int l4)` | 0x0D | `[preset][pollutant][L1(2)][L2(2)][L3(2)][L4(2)]` | 오염단계 임계 |
| `SetReserve(int hours)` | 0x0E | `[hours]` | (꺼짐)예약 0~8시간(0=해제) |
- `room` : 1=거실, 2=침실1, 3=침실2, 4=침실3
- `type`(댐퍼) : 0=급기(SA) / 1=배기(EA)
- `group` : 0=환기(Vent) / 1=바이패스(Bypass) / 2=공청(AirClean)
- `pollutant` : 0=CO2 / 1=PM2.5 / 2=PM10 / 3=VOC
- 멀티바이트(2) 값은 **빅엔디안**
### 1.4 데모
| 함수 | 설명 |
|---|---|
| `void InjectDemoStatus(int tick)` | 합성 STATUS 를 수신 경로로 주입(시리얼 없이 UI 테스트) |
### 1.5 enum 정의 (ErvProtocol/Enums.cs)
```
RunMode : Off=0, Vent=1, Auto=2, AirClean=3, Bypass=4
AutoState : Distribute=0(분산), Focus=1(집중)
HystPreset : Eco=0, Normal=1, Turbo=2
AirQuality : VeryBad=1, Bad=2, Normal=3, Good=4
SubModeType : SmartSleep=1, ComfortCook=2, ReliefRecover=3
```
---
## 2. 사용법
### 2.1 초기화 / 이벤트 등록
```csharp
readonly DashboardState _state = new();
readonly IErvApi _api = new SerialErvApi();
// 로그 / 연결상태 / STATUS 수신
_api.Log += Log;
_api.ConnectionChanged += b => Dispatcher.BeginInvoke(() => OnConnectionChanged(b));
_api.StatusReceived += rec => Dispatcher.BeginInvoke(() =>
{
StatusMapper.Apply(rec, _state); // StatusRecord → UI 모델
LogStatusSnapshot();
});
```
> `SerialErvApi` 이벤트는 시리얼 수신(백그라운드) 스레드에서 발생하므로, UI 갱신은
> `Dispatcher.BeginInvoke` 로 마샬링한다.
### 2.2 연결 / 상태 요청
```csharp
var ports = SerialErvApi.GetAvailablePorts(); // COM 목록
_api.Connect("COM3", 115200); // 연결
_api.RequestStatus(); // 최초 STATUS 요청
...
_api.Disconnect();
```
### 2.3 제어 송신 (예)
```csharp
_api.SetPower(true); // 전원 ON
_api.SetRunMode(RunMode.Auto); // 자동
_api.SetFan(2); // 풍량 2단
_api.SetSubMode(SubModeType.ComfortCook, true); // 쾌적조리 ON
_api.SetHood(true); // 연동후드 ON
_api.SetReserve(3); // 3시간 후 꺼짐 예약
_api.SetDiffuserDamper(1, 0, true); // 거실 급기(SA) 댐퍼 열림
_api.SetDiffuserDamper(1, 1, false); // 거실 배기(EA) 댐퍼 닫힘
_api.SetDiffuserLed(2, 5); // 침실1 LED 디밍 5
// 풍량 VSP (환기2단 SA/EA)
_api.SetVsp(group:0, index:2, sa:63, ea:61);
// 히스테리시스
_api.SetHystPreset(HystPreset.Normal);
_api.SetHystDeadband(preset:1, pm25:2, pm10:5, voc:5, co2:50);
_api.SetHystThreshold(preset:1, pollutant:0 /*CO2*/, l1:700, l2:1000, l3:1300, l4:1600);
```
### 2.4 수신 처리
`StatusReceived``StatusMapper.Apply(rec, state)``DashboardState`(전원/모드/풍량/각실 센서·
댐퍼·LED·부하점수·온습도/VSP/히스테리시스/임계표) 전체가 갱신된다.
각실 일부 항목만 즉시 조회할 때는 `GetRoomStatus` 사용(최근 수신 STATUS 기준) :
```csharp
if (_api.GetRoomStatus(1, out bool sa, out bool ea, out AirQuality aq, out int led))
Console.WriteLine($"거실: 급기={(sa ? "" : "")} 배기={(ea ? "" : "")} 공기질={aq} LED={led}");
```
---
## 3. 프로토콜 상세
### 3.1 물리계층
- RS-485 (또는 USB-Serial), **115200 bps, 8 Data, None Parity, 1 Stop (N81)**
### 3.2 공통 프레임 (Rev2.0 — 244byte 고정)
```
+------+------+----------------+--------+--------+
| STX | CMD | DATA[240] | CRC_L | CRC_H |
+------+------+----------------+--------+--------+
0xAA 1B 240B 고정 16-bit CRC(LE)
```
| 필드 | 크기 | 설명 |
|---|---|---|
| STX | 1 | 고정 `0xAA` |
| CMD | 1 | 명령/응답 코드 |
| DATA | 240 | **고정 240byte.** 제어는 앞쪽 인자 + 나머지 `0` 패딩, ERV→PC 는 STATUS/ACK 데이터 |
| CRC | 2 | **CRC-16/MODBUS**(poly 0xA001, init 0xFFFF), **CMD~DATA(241byte)** 범위, **리틀엔디안** |
- **LEN 필드 폐기.** 모든 프레임은 항상 `1+1+240+2 = 244byte` 고정. 파서는 정해진 바이트 수만 읽고 CRC 검증.
- 멀티바이트 수치는 **빅엔디안**(CRC만 리틀엔디안)
- 프레임 동기 : STX(0xAA) 탐색 + 고정길이 수신 + CRC 검증. 60ms 이상 바이트 공백 시 파서 리셋.
### 3.3 명령 코드
**PC → ERV (제어)** : 1.3 표 참조 (0x01~0x0E)
**ERV → PC (상태/응답)**
| CMD | 이름 | PAYLOAD | 설명 |
|---|---|---|---|
| 0x81 | STATUS | DATA 240 byte (3.5) | 전체 상태 스냅샷 (주기 송신 + REQ_STATUS 응답) |
| 0x82 | ACK | DATA `[echoCmd][result]` + 0패딩 | 제어 수신 응답 result 0=OK / 1=ERR |
### 3.4 값 정의
- 운전모드 : 0 OFF / 1 환기 / 2 자동 / 3 공청 / 4 바이패스
- 부가모드 type : 1 스마트수면 / 2 쾌적조리 / 3 안심회복 (STATUS subMode 는 비트맵 bit0/1/2)
- 공기질 : 1 매우나쁨(빨강) / 2 나쁨(주황) / 3 보통(초록) / 4 좋음(파랑)
- 자동상태 : 0 분산 / 1 집중
- 히스 프리셋 : 0 ECO / 1 NORMAL / 2 TURBO
### 3.5 STATUS(0x81) PAYLOAD 레이아웃 — **총 240 byte**
| 블록 | offset | 크기 | 내용 |
|---|---|---|---|
| 글로벌 | 0~16 | 17 | power, runMode, autoState, fanMode, subMode, hood, hystPreset, hystPM25(7,2), hystPM10(9,2), hystVOC(11,2), hystCO2(13,2), errorCode(15,2) |
| 각실 ×4 | 17~72 | 14×4=56 | 거실→침실1→2→3, 아래 표 |
| 리셋 | 73 | 1 | reset 토글 0/1 |
| VSP | 74~109 | 4×9=36 | 환기1~4, 바이패스, 공청1~4 의 SA(2)+EA(2) |
| 히스 프리셋표 | 110~133 | 8×3=24 | ECO/NORMAL/TURBO 의 PM2.5(2)/PM10(2)/VOC(2)/CO2(2) |
| 오염단계 임계표 | 134~229 | 32×3=96 | 프리셋×[CO2,PM2.5,PM10,VOC] 각 L1~L4 u16 |
| 각실 온습도 ×4 | 230~237 | 2×4=8 | 실별 Temp(1)+Humi(1) |
| (꺼짐)예약 | 238~239 | 2 | 잔여 초 u16 (0=예약없음) |
**각실 블록(14 byte) 상세** (상대 offset)
| +off | 크기 | 필드 |
|---|---|---|
| +0 | 1 | damper 비트맵 (bit0 급기 열림 / bit1 배기 열림) |
| +1 | 2 | pm25 |
| +3 | 2 | pm10 |
| +5 | 2 | voc |
| +7 | 2 | co2 |
| +9 | 1 | airQuality (3.4) |
| +10 | 1 | ledDim (0~9) |
| +11 | 2 | loadScore (부하점수) |
| +13 | 1 | finalVolume (최종풍량) |
**오염단계 임계표(프리셋당 32 byte)** : `CO2[L1..L4]` `PM2.5[L1..L4]` `PM10[L1..L4]` `VOC[L1..L4]` 순, 각 u16(빅엔디안).
> 계산식 : 17 + 56 + 1 + 36 + 24 + 96 + 8 + 2 = **240**
### 3.6 동작 시나리오
1. 연결 후 `RequestStatus()(0x0A)` 송신 → ERV 가 `STATUS(0x81)` 응답.
2. ERV 는 약 500ms~1s 주기로 `STATUS` 자동 송신 → 대시보드 실시간 갱신.
3. PC 제어 시 해당 `CTRL_*` 송신 → ERV 가 `ACK(0x82)` + 다음 STATUS 에 반영.
4. PC 는 STATUS 수신 시마다 로그(시각 + 전체 상태) 적재.
---
## 4. 비고
- 본 문서의 API(IErvApi) 도입은 **전송 방식(485 프로토콜)을 바꾸지 않는다.** 호출부를 함수로 캡슐화한 것.
- `IErvApi` 는 인터페이스이므로, 향후 다른 전송(예: HTTP) 구현(`HttpErvApi`)을 추가해 설정으로 교체 가능.
- 펌웨어(UART1/HOMENET_485) 세부 필드는 상호 합의하여 조정한다. (원본 초안: `TestProgram/PC_ERV_Protocol.md`)