# Roomfit MCU Compatibility Matrix

**Created**: 2026-04-01  
**Last Updated**: 2026-04-01

이 문서는 Roomfit 앱과 MCU 펌웨어 사이의 버전 혼선 지점을 정리한 compatibility matrix입니다.  
목표는 "문서 스펙", "체크인된 MCU 소스", "앱 현재 구현", "실제 리스크"를 한 번에 비교할 수 있게 만드는 것입니다.

---

## 1. 사용 목적

이 문서는 아래 상황에서 기준 문서로 사용합니다.

- 앱에서 특정 command/response가 왜 예외 처리되는지 확인할 때
- `docs/reference/mcu-protocol.md`와 `docs/reference/mcu-source/`가 서로 다를 때
- 실기기에서 들어온 packet이 문서와 맞지 않을 때
- 새 펌웨어 버전을 도입하기 전에 regression checklist를 만들 때

---

## 2. 진실원천 우선순위

버전 관리가 섞여 있는 현재 상태에서는 아래 우선순위를 적용합니다.

1. **실기기 packet capture**
2. **배포된 펌웨어 binary/tag**
3. **repo에 체크인된 MCU 소스**
4. **서술형 reference 문서**

즉, 문서와 MCU 소스가 충돌하면 MCU 소스를 우선 참고하고, MCU 소스와 실기기 capture가 충돌하면 실기기를 우선합니다.

---

## 3. 현재 호환성 요약

| 항목 | reference 문서 | 체크인 MCU 소스 | 앱 현재 처리 | 상태 |
|---|---|---|---|---|
| `REPORT(0x41)` | `mcu-protocol.md`는 기본 report를 10-byte data로 설명 | `Return_Report_Task_v3()`는 12-byte data(time, posL, posR, loadL, loadR, voltage) 사용 | `ReportResponse`는 12-byte v3를 기본 처리하고 short payload도 허용 | 호환 처리됨, 문서 outdated |
| `GET_SYSTEM_INFO / BLE_CONNECT(0x06)` | `0x06` 단일 응답 또는 multi-packet snapshot 가능하다고 설명 | `BLE_CONNECT` 처리에서 `0x67/0x05/0x65` snapshot push | `SystemInfoResponse`는 future-only로 유지, 실제 snapshot은 `WeightResponse/VoltageResponse/WeightPowerResponse`로 처리 | 호환 처리됨 |
| `SET_WEIGHT(0x61)` | 요청 `0x61`, 응답 snapshot으로 설명 | `protocol.c`는 `SET_WEIGHT` 응답도 `0x61`로 반환 | `WeightResponse`가 `0x61/0x66/0x67` 모두 처리 | 호환 처리됨 |
| `WEIGHTPLUS / WEIGHTMINUS (0x66/0x67)` | 응답을 `0x66` snapshot 중심으로 설명 | 일부 경로는 `0x66`, 일부 초기 snapshot은 `0x67` 사용 | `WeightResponse`가 두 코드 모두 파싱 | 호환 처리됨 |
| `MODE_CHANGE(0x68)` | 문서 일부는 v1 4-byte 확장 응답을 현재 미지원으로 설명 | `protocol.c`는 `[leftMode, rightMode, error, error]` 4-byte 반환 | `WeightModeResponse`가 4-byte 확장 응답을 파싱하고 `errorCode` 반영 | 앱 구현이 문서보다 최신 |
| `ECC_LEVEL(0x69)` | 문서 일부는 v2 포맷 기준 서술 | `protocol.c`는 `[left*2, right*2, error, 0]` 4-byte 반환 | `EccLevelResponse`가 4-byte 확장 응답 파싱 | 앱 구현이 문서보다 최신 |
| `FORCED_CALIBRATION(0x84)` | `0x84` 응답으로 설명 | 체크인 소스 일부는 `RUN_FORCEDCALIB` 뒤 `AUTOWEIGHT_ACTIVE(0x81)` 반환 | `DeviceLink`가 `ForceCalibrateCommand` 직후 legacy `0x81`을 `ForceCalibrateResponse`로 승격 | 호환 shim 추가됨 |
| `AUTOWEIGHT_ACTIVE/STATUS`, `RELAX_ZONE`, `FAIL_SAFE_ERROR` | 문서상 존재 | 소스에도 존재 | parser만 있던 상태에서 `DeviceState` `automation/safety`로 state 승격 완료 | 관측 가능해짐 |
| `SET_RANGE / SET_RANGE_DIGIT / SET_RANGE_INIT` | one-shot ACK 응답 | 소스도 1-byte ACK 사용 | parser는 있음. `DeviceState`에는 누적하지 않음 | 기능상 문제 없음, UI feedback 필요 시 별도 facade 권장 |
| `DEBUG_REPORT(0xF5)` | 문서와 소스 모두 debug 확장 언급 | 7-byte basic, extended variant 공존 | `DebugReportResponse`가 basic/extended 둘 다 파싱 | 호환 처리됨 |
| `SET_TIME(0x08)` | v2+ 가정 | 체크인 v1 소스에는 handler 정의 없음 | command는 있으나 deployed FW에서 미지원 가능성 큼 | 고위험 speculative |
| `TARGET_WEIGHT(0x70)` | v2+ 가정 | 체크인 v1 소스에는 handler 정의 없음 | command는 있으나 deployed FW에서 미지원 가능성 큼 | 고위험 speculative |
| `SET_RELAX_ZONE(0x83)` request | 문서는 v2+ 확장으로 서술 | 체크인 v1 소스에는 request handler 불명확 | command는 있으나 현재 UI 노출 없음 | 검증 필요 |

---

## 4. 핵심 mismatch 상세

### 4.1 `REPORT(0x41)` 포맷

- 문서 서술:
  - `docs/reference/mcu-protocol.md`
  - 기본 report data를 `time + posL + posR + forceL + forceR`로 설명
- 체크인 MCU 소스:
  - `docs/reference/mcu-source/Core/Src/protocol.c`
  - `Return_Report_Task_v3()`는 `voltage`까지 포함한 12-byte data 반환
- 앱 처리:
  - `packages/roomfit_protocol/lib/src/responses/response.dart`
  - `ReportResponse`는 v3 12-byte를 기본으로 사용
  - short payload fallback도 허용

정리:

- 현재 앱은 소스 기준으로 맞춰져 있음
- reference 문서가 구버전 설명을 유지하고 있음

### 4.2 `FORCED_CALIBRATION(0x84)` legacy ACK

- 문서 서술:
  - `0x84` 요청 뒤 `0x84` 응답
- 체크인 MCU 소스:
  - `docs/reference/mcu-source/Core/Src/protocol.c`
  - `RUN_FORCEDCALIB` 처리 후 `Return_1byte(AUTOWEIGHT_ACTIVE, ReStart_ForcedCalib);`
- 앱 처리:
  - `packages/roomfit_protocol/lib/src/device_link.dart`
  - `ForceCalibrateCommand` 직후 들어온 `0x81`을 legacy force-calibration ACK로 해석

정리:

- 이 예외는 버전 혼선의 대표 사례
- 지금은 앱에서 compatibility shim으로 흡수
- 장기적으로는 FW tag별 behavior를 명시해야 함

### 4.3 `MODE_CHANGE(0x68)` / `ECC_LEVEL(0x69)` 확장 응답

- 문서 일부는 여전히 "현재 앱 미지원"처럼 읽힘
- 실제 앱 parser는 이미 4-byte 확장 응답을 처리함
- 따라서 문서가 구현보다 뒤처져 있음

정리:

- 현재 앱 구현이 문서보다 최신
- 문서 갱신 시 "legacy 4-byte도 지원"으로 수정 필요

---

## 5. 앱 현재 호환 전략

앱은 지금 아래 원칙으로 MCU 버전 혼선을 흡수합니다.

- 가능한 한 **response code alias**를 허용한다.
  - 예: `WeightResponse`가 `0x61/0x66/0x67` 모두 처리
- payload 길이가 달라질 수 있는 packet은 **짧은 payload fallback**을 둔다.
  - 예: `ReportResponse`
- 특정 command 직후 들어오는 legacy ACK는 **context-aware compatibility shim**으로 해석한다.
  - 예: `ForceCalibrateCommand` 직후의 `0x81`
- parser만 끝내지 않고, 운영상 중요한 신호는 `DeviceState`에 승격해 UI에서 관찰 가능하게 만든다.
  - `autoWeight`, `relaxZone`, `forceCalibrating`, `failSafeError`

---

## 6. 남은 리스크

### 6.1 실기기 미검증 command

아래 command는 코드에는 있으나, 체크인 MCU 소스 기준으로는 deployed FW 지원 여부가 불명확합니다.

- `SetTimeCommand(0x08)`
- `SetTargetWeightCommand(0x70)`
- `SetRelaxZoneCommand(0x83)` request path

이들은 실기기 packet capture 전까지 "지원 예정 command"로 취급하는 것이 안전합니다.

### 6.2 one-shot ACK의 UI 부재

아래 응답은 파싱되지만 persistent state로 유지하지 않습니다.

- `SET_RANGE(0x62)`
- `SET_RANGE_DIGIT(0x72)`
- `SET_RANGE_INIT(0x73)`

현재는 packet log로는 보이지만, UI에서 성공/실패 badge를 유지하려면 feature facade가 별도 status state를 가져야 합니다.

### 6.3 문서 drift

현재 `mcu-protocol.md`는 구현/소스보다 뒤처진 항목이 남아 있습니다.

- `REPORT(0x41)` v3 12-byte
- `MODE_CHANGE(0x68)` legacy 4-byte support
- `ECC_LEVEL(0x69)` legacy 4-byte support
- `FORCED_CALIBRATION(0x84)` legacy `0x81` ACK

---

## 7. 운영 권장안

### 7.1 펌웨어 버전별 protocol snapshot 유지

최소한 아래 단위로 protocol snapshot을 분리하는 것이 좋습니다.

- `rf_v1.0.0_250507`
- `rf_v1.0.3_debug_report`
- `rf_v1.0.4_hil`
- `rf_v3_report_default`

### 7.2 새 펌웨어 반입 시 체크리스트

1. 실기기에서 raw packet capture 확보
2. `CommandCodes`와 실제 code 비교
3. response payload 길이 비교
4. parser/unit conversion 비교
5. `DeviceState` 승격 필요 여부 판단
6. `packages/roomfit_protocol/test/`와 `flutter test`에 regression 추가

### 7.3 문서 유지 규칙

새 mismatch를 찾으면 반드시 세 곳 중 최소 두 곳을 같이 갱신합니다.

- 이 문서
- `docs/reference/mcu-protocol.md`
- 대응 테스트

문서만 고치고 테스트를 안 늘리거나, 테스트만 고치고 문서를 안 남기는 방식은 다시 drift를 만듭니다.

