Matter Virtual Device가 있다면, 디바이스에서 자동 생성

디바이스가 없다면?

 

 

1. chip-tool 설치 

git clone https://github.com/project-chip/connectedhomeip.git
cd connectedhomeip
source scripts/activate.sh
gn gen out/debug
ninja -C out/debug

 

 

2. QR 코드 생성 명령어

./out/debug/chip-tool payload generate \
    --version 1 \
    --vendor-id 0xFFF1 \
    --product-id 0x8000 \
    --discriminator 3840 \
    --setup-pin-code 20202021

 

3. 출력 예시

SetupQRCode: MT:00000CQM00D0X6X730
ManualPairingCode: 34970112332
 

1) Zxing 라이브러리 이용하여 QR 코드 인식 시도함
> NU310 기기가 고정 초점 카메라여서
포커스를 못잡아서 QR코드 인식이 안 됨

2) 다른 라이브러리 찾아봄 : ML Kit
프레임 기반, 포커싱이 완벽하지 않아도 테스트 구조 인식으로 성공률 높음
> NU310기기는 CameraX와 호환이 안 됨

3) ML Kit + Camera2 기반 구현

 

🚫 안 되는 방식 정리

방법 이유
Zxing (JourneyApps) Nu310은 고정 초점 카메라 → Zxing은 포커스 필수 → 인식 실패
ML Kit + CameraX Nu310은 CameraX 미지원 → 프리뷰/분석 자체 불가능
ML Kit + Camera2 구조는 가능하나, 직접 구현해야 하고 복잡 + Fragment 안에서 Hilt/Navi와 충돌 가능성 높음

 

 

✅ 현실적인 선택지

💡 ML Kit + Camera2 API 직접 구현 (권장)

➡️ 이미 ML Kit가 포커싱이 약해도 QR 구조 인식이 가능하므로,
➡️ Camera2 API로 프레임만 확보해서 ML Kit에 넘기면 됩니다.
➡️ 고정초점이어도 ML Kit는 인식 성공률이 높음

 

✅ 실현 가능한 조합

구성요소 설명
카메라 Camera2 API 사용 (기기 호환성 보장)
분석기 ML Kit: BarcodeScanning
프리뷰 TextureView 사용
권한 처리 CAMERA 권한 직접 처리
Fragment Fragment에서 동작, Hilt 없어도 됨
 

 

✅ 전체 흐름 요약

  1. Fragment에 TextureView 배치
  2. Camera2로 프리뷰 띄움
  3. ImageReader로 프레임 수신
  4. InputImage.fromMediaImage()로 ML Kit에 전달
  5. QR 코드 인식되면 처리 → 멈춤

 

✅ 다음 단계

이미 이 구조로 예제 거의 완성하셨기 때문에,
🧩 필요한 건 딱 2가지:

  1. requireContext() import 수정
  2. startCamera(texture: SurfaceTexture) 방식으로 매개변수 전달 (null 방지)

벡엔드에게 di (Dependency Injection) , daomodule, model, repository 설명해주려고해.

사실 나도 정확한 개념이 헷갈려. 알려줄래?

 

 

물론이죠! 😎
백엔드에게 설명하려면 Android 앱 아키텍처 관점백엔드 개발자가 이해하기 쉬운 비유를 같이 사용하는 게 효과적이에요.

 

✅ 빠르게 핵심 개념부터!

개념 한 줄 정의
Model 실제 데이터 구조 (DB 테이블 대응 클래스)
DAO DB에 접근하는 인터페이스 (insert, getAll 등 SQL 쿼리 메소드)
Repository DAO + 다른 데이터 소스(API 등)를 통합해서 뷰모델에 제공
DI (Dependency Injection) 객체를 직접 생성하지 않고, 외부에서 "주입"받는 구조
Module (Dagger/Hilt) DI가 어떤 객체를, 어떻게 만들지 정의한 "레시피"
ViewModel (참고) UI 데이터 상태를 보존하고, Repository를 통해 데이터 요청
 

 

 

 

✅ 백엔드 개발자에게 이렇게 설명해보세요:

📦 1. Model

백엔드에서 말하는 DTO나 Entity 클래스와 같아요.
예: Device(name: String, category: String)

 

 

🛠 2. DAO (Data Access Object)

DAO는 SQL을 대신해주는 DB 접근 인터페이스에요.
마치 JPA의 Repository 같은 역할.

@Dao
interface DeviceDao {
    @Query("SELECT * FROM device_table")
    fun getAll(): List<Device>
}

 

🧩 3. Repository

여러 데이터 소스(DB, API, 캐시 등)를 묶어서 ViewModel에 제공해주는 계층이에요.
백엔드의 Service Layer와 비슷합니다.

class DeviceRepository @Inject constructor(private val dao: DeviceDao) {
    fun getDevices() = dao.getAll()
}

 

💉 4. DI (Dependency Injection)

객체를 직접 생성하지 않고, 외부에서 주입 받아 사용하는 방식이에요.
Spring의 @Autowired, @Service, @Component와 같다고 보면 됩니다.

 

🔧 5. Module

Hilt나 Dagger에서는 DI에 필요한 객체 생성 방식을 정의하는 곳입니다.

@Module
@InstallIn(SingletonComponent::class)
object DaoModule {

    @Provides
    fun provideDeviceDao(db: AppDatabase): DeviceDao = db.deviceDao()
}

 

 

 

✅ 전체 흐름 예시

🔽 ViewModel
    🔽
Repository
    🔽
DAO ↔ Room DB

이 구조에서 Hilt가 각 객체를 알아서 생성하고 연결해줘서, 개발자는 그냥 @Inject만 붙이면 됩니다.

 

 

✅ 요약 키워드로 외우기

이름 역할 백엔드 비유
Model 데이터 구조 DTO / Entity
DAO DB 접근 메소드 JPA Repository
Repository 데이터 조합/가공 Service Layer
DI 의존성 주입 @Autowired
Module DI 등록소 Bean 설정

 

 

필요하시면 DI 없이 만든 코드 vs DI 사용한 코드 비교,
또는 전체 구조 그림도 그려드릴 수 있어요 😊


좋아요! 👇
"DI 없이 만든 코드 vs DI 사용한 코드" 비교
전체 구조 그림 모두 준비해드릴게요.

 

✅ 1. DI 없이 만든 코드 (직접 생성 방식, ❌비추천)

class DeviceRepository {
    private val dao = AppDatabase.getInstance().deviceDao()

    fun getDevices(): List<DeviceItem> {
        return dao.getAllDevices()
    }
}

class DeviceViewModel : ViewModel() {
    private val repository = DeviceRepository()

    fun loadDevices(): List<DeviceItem> {
        return repository.getDevices()
    }
}

 

❌ 문제점

  • DeviceRepository를 만들 때마다 내부에서 dao를 직접 생성함
  • ViewModel, Repository, DAO가 강하게 결합됨
  • 테스트/유지보수 힘듦 (모킹 불가능)

 

✅ 2. DI 사용한 코드 (Hilt 기반, 추천 ✅)

// 1. DAO 모듈에서 제공
@Module
@InstallIn(SingletonComponent::class)
object DaoModule {

    @Provides
    fun provideDeviceDao(db: AppDatabase): DeviceDao = db.deviceDao()

    @Provides
    fun provideDeviceRepository(dao: DeviceDao): DeviceRepository = DeviceRepository(dao)
}
// 2. Repository
class DeviceRepository @Inject constructor(
    private val dao: DeviceDao
) {
    fun getDevices(): List<DeviceItem> = dao.getAllDevices()
}
// 3. ViewModel
@HiltViewModel
class DeviceViewModel @Inject constructor(
    private val repository: DeviceRepository
) : ViewModel() {

    fun loadDevices(): List<DeviceItem> = repository.getDevices()
}

✅ 장점

  • ViewModel, Repository, DAO 간 결합도 낮음
  • 테스트하기 쉬움 (Mock 주입 가능)
  • 유지보수 쉬움 (Hilt가 객체 주입 관리)

 

 

 

✅ 3. 전체 구조 그림 (Hilt 기반 아키텍처)

[ View (Fragment/Activity) ]
          ↓
     ViewModel (DeviceViewModel)
          ↓
     Repository (DeviceRepository)
          ↓
         DAO (DeviceDao)
          ↓
        Room DB

[Hilt] 가 모든 걸 자동 주입해 줌

 

 

🔚 요약

비교 항목 DI 없이 DI 사용
객체 생성 직접 생성 (new) 자동 주입 (@Inject)
테스트 어려움 쉬움 (Mock 가능)
유지보수 결합도 높음 느슨한 구조
코드 재사용성 낮음 높음
의존성 관리 수동 자동 (Hilt/Dagger

 

로컬 코드는 Window 폴더에 있고 , yona 원격 코드는 Z드라이브 samba 폴더내에 있어.

코딩은 계속 로컬 코드인 Windows에서 할 때.. 변경사항을 Z samba에 어떻게 반영시키지?

 

 

✅  Beyond Compare로 비교 후 복사 (직관적)

  1. Beyond Compare 실행
  2. 왼쪽: C:\Users\User\AndroidStudioProjects\[로컬폴더]
  3. 오른쪽: Z:\[원격폴더]
  4. 변경된 파일만 복사
  5. Z 드라이브에 복사된 뒤, Git Bash에서 커밋 & 푸시:
 
cd Z:/Matter_Controller
git add .
git commit -m "윈도우 작업 내용 반영"
git push origin main  # 또는 master

✅ 눈으로 비교 가능하고 안전함
❌ 매번 수동 비교 필요

 

 

변경된 파일을 왼쪽 폴더로 옮기고

변경된 파일을 git add > git commit >  git push 해주면 된다...

 

 

로컬 코드는 Window 폴더에 있고 , yona 원격 코드는 Z드라이브 samba 폴더내에 있어.

코딩은 계속 로컬 코드인 Windows에서 할 때.. 변경사항을 Z samba에 어떻게 반영시키지?

 

 

✅  Beyond Compare로 비교 후 복사 (직관적)

  1. Beyond Compare 실행
  2. 왼쪽: C:\Users\User\AndroidStudioProjects\[로컬폴더]
  3. 오른쪽: Z:\[원격폴더]
  4. 변경된 파일만 복사
  5. Z 드라이브에 복사된 뒤, Git Bash에서 커밋 & 푸시:
 
cd Z:/Matter_Controller
git add .
git commit -m "윈도우 작업 내용 반영"
git push origin main  # 또는 master

눈으로 비교 가능하고 안전함
❌ 매번 수동 비교 필요

virtual matter device에서 각 필드의 의미

너가 말한 건 Matter 프로토콜에서 디바이스를 네트워크에 등록(Commissioning) 할 때 필요한 핵심 식별자들이야.

 

 

📦 1. Discriminator

💡 의미:

  • 디바이스 검색/설정 과정에서 사용되는 짧은 숫자형 ID
  • 동일한 네트워크 범위에 여러 디바이스가 있을 때, 어떤 걸 설정할지 선택하기 위한 값

 

🧪 예시:

  • QR 코드, Manual Code 안에도 포함되어 있음
  • ex: 3840, 2501 등

 

📌 현실에서:

"A, B, C 중 어떤 디바이스를 커미셔닝할래?" → 이때 보여지는 ID

 

 

🌐 2. Matter Port

💡 의미:

  • 디바이스가 Matter 프로토콜 통신을 위해 사용하는 TCP 포트 번호
  • 기본적으로 5540번이 자주 사용됨

📌 현실에서:

네트워크상에서 "이 디바이스와 연결하고 싶다" → "그럼 걔가 Listen 중인 포트가 몇 번이냐?" → 이걸 말해줌

 

 

🏷️ 3. Vendor ID (VID)

💡 의미:

  • 디바이스를 만든 회사(벤더) 를 식별하는 고유 ID
  • CSA(Matter 표준 관리 기구)에서 정식 할당

📌 예시:

  • 0xFFF1 → 테스트 벤더 (개발용)
  • 0x0001 → 예: Silicon Labs
  • 0x1234 → 너가 만든 가상 디바이스용 벤더 ID

 

🔢 4. Product ID (PID)

💡 의미:

  • 벤더 내에서 만든 각 제품의 고유 식별자
  • 같은 벤더라도 제품마다 PID 다름

📌 예시:

  • 벤더 0xFFF1 안에 제품 0x8000, 0x8001 등

 

✨ 요약표

항목 예시 설명
Discriminator 3840 어떤 디바이스인지 사용자 눈에 보이는 ID
Matter Port 5540 네트워크 연결 시 사용할 포트 번호
Vendor ID 0xFFF1 제조사 ID (테스트용도 OK)
Product ID 0x8000 제품 ID (기기 모델 식별용)
 

 

✅ Virtual Matter Device에서 필요한가?

항목 필요여부 이유
Discriminator 커미셔닝에서 어떤 디바이스인지 식별
Matter Port 컨트롤러가 연결할 포트
Vendor ID 벤더 식별 (테스트용 0xFFF1 가능)
Product ID 디바이스 모델 구분
 

 

🛠 개발 시 추천 값 (Virtual 환경)

항목 추천값
Discriminator 3840
Matter Port 5540
Vendor ID 0xFFF1
Product ID 0x8000
 

 

궁금하면 실제 QR 코드 생성 방식이나 manual code 생성 방법도 설명해줄게 😎
필요할 땐 “커미셔닝 전체 흐름”도 함께 도와줄게!

아래 표와 같이 기기 범주와 기기 종류에 따라
필요한 UI가 달라진다.

 

toggle_view(좌)와 slider_view(우)

 

이런식으로 기기종류에 따라 UI를 매핑하는 동적UI로 구성하려면?

응, 아주 좋은 접근이야!
**“기기 범주/종류에 따라 들어가는 기능(컴포넌트)을 정하고, 이 컴포넌트의 위치도 지정하고 싶다”**면,
**테이블 형태로 ‘기기종류 → UI 컴포넌트 목록 → 삽입 순서’**를 매핑하는 방식이 딱이야. 👇

 

✅ 1. 예시 테이블 설계 (매핑용)

기기 범주 기기 종류 컴포넌트 ID  삽입 순서
Lighting On/Off Light toggle_view 0
Lighting Dimmable Light toggle_view, slider_view 0, 1
Lighting Color Temperature Light toggle_view, slider_view, color_temp_view 0, 1, 2
Lighting Extended Color Light toggle_view, slider_view, color_temp_view, color_xy_view 0, 1, 2, 3

 

 

✅ 2. 구성 요소 XML 예시 (res/layout/*.xml)

 

  • view_toggle.xml → ON/OFF 스위치
  • view_slider.xml → 밝기 조절
  • view_color_temperature.xml → 색온도
  • view_color_xy.xml → 색상 조절

 

✅ 3. Kotlin 코드 (동적 UI 삽입)

val componentMap = mapOf(
    "On/Off Light" to listOf(R.layout.view_toggle),
    "Dimmable Light" to listOf(R.layout.view_toggle, R.layout.view_slider),
    "Color Temperature Light" to listOf(R.layout.view_toggle, R.layout.view_slider, R.layout.view_color_temperature),
    "Extended Color Light" to listOf(R.layout.view_toggle, R.layout.view_slider, R.layout.view_color_temperature, R.layout.view_color_xy)
)

val container = findViewById<LinearLayout>(R.id.right_panel_container) // LinearLayout or ViewGroup

val deviceType = "Extended Color Light"  // 예시
componentMap[deviceType]?.forEachIndexed { index, layoutRes ->
    val view = layoutInflater.inflate(layoutRes, container, false)
    container.addView(view, index)
}
 
 
💡 UI 위치 지정 팁
  • LinearLayout이면 순서대로 addView() 하면 됨
  • ConstraintLayout에 직접 넣고 싶으면, ConstraintSet으로 동적으로 연결해줘야 하므로 조금 복잡

 

✅ 정리

  • 기기 유형별 UI 구성 정의를 테이블로 명확히 하면 동적 UI 설계가 훨씬 쉬움
  • 순서를 지키고 싶다면 LinearLayout + addView(view, index)
  • 더 고급 제어 원하면 RecyclerView + ViewType 또는 ConstraintSet 활용 가능

필요하면 CSV/JSON으로 이 테이블 내보내서 코드 자동화도 가능해!
엑셀로 만든 테이블도 JSON으로 변환해줄 수 있음 😎
원하면 해줄게!

 


테이블 만들어서 넣는 것도 일일이 하드코딩안하고 좀 자동화?하고 싶어 테이블만 조절하면 되도록

 

 

완전 가능해! 그걸 하려면 “UI 매핑 테이블만 관리하고, 코드는 그걸 기반으로 동작”하는 구조로 만들면 돼.
아래처럼 하면 하드코딩 안 하고도 기기 추가 시 테이블만 바꾸면 끝 👇

 

✅ 1. 매핑 테이블 (JSON or CSV로 관리)

예시 JSON 형태 (device_ui_map.json):

{
  "On/Off Light": ["view_toggle"],
  "Dimmable Light": ["view_toggle", "view_slider"],
  "Color Temperature Light": ["view_toggle", "view_slider", "view_color_temperature"],
  "Extended Color Light": ["view_toggle", "view_slider", "view_color_temperature", "view_color_xy"]
}

 

이걸 앱에 assets/device_ui_map.json 으로 저장해놓자.

 

✅ 2. 코드: JSON 불러와서 자동 UI 구성

(1) DeviceUiLoader.kt

import android.content.Context
import org.json.JSONObject

object DeviceUiLoader {
    fun loadMapping(context: Context): Map<String, List<String>> {
        val json = context.assets.open("device_ui_map.json")
            .bufferedReader().use { it.readText() }
        val jsonObj = JSONObject(json)
        val result = mutableMapOf<String, List<String>>()

        for (key in jsonObj.keys()) {
            val list = jsonObj.getJSONArray(key)
            val views = mutableListOf<String>()
            for (i in 0 until list.length()) {
                views.add(list.getString(i))
            }
            result[key] = views
        }

        return result
    }
}

 

(2) MainActivity.kt 또는 Fragment에서 적용

val container = findViewById<LinearLayout>(R.id.right_panel_container)
val mapping = DeviceUiLoader.loadMapping(context)
val deviceType = "Dimmable Light"

mapping[deviceType]?.forEach { layoutName ->
    val layoutId = resources.getIdentifier(layoutName, "layout", packageName)
    val view = layoutInflater.inflate(layoutId, container, false)
    container.addView(view)
}

 

 

✅ 이렇게 하면 좋은 점

  • 하드코딩 없이 동적 구성
  • UI 구조는 device_ui_map.json만 수정하면 됨
  • 신규 기기 추가 시 layout/view_*.xml만 만들고 JSON에 추가
  •  

✅ 확장 아이디어 (추후)

할 일 방법
UI 순서 조절 JSON 리스트 순서대로 렌더링
UI 그룹별 설정 JSON에 "category": "Lighting" 같은 필드 추가
특정 UI만 조건부 비활성화 view_toggle_disabled, view_slider_disabled 등 추가

 

원하면 내가 지금 이미지에 있는 테이블을 바로 JSON으로 만들어줄 수도 있어 😎

필요하면 말만 해!

matter virtual device를 wsl에서 띄웠는데
이거 android studio에 연결 어떻게 해?

 

네, WSL(Windows Subsystem for Linux)에서 띄운 Matter virtual device
Android Studio의 Matter Controller 앱과 연결할 수 있습니다.
다만 몇 가지 설정이 필요해요. 아래 과정을 확인하세요:

 

✅ 1. WSL의 가상 디바이스 IP 확인

Matter virtual device는 WSL 내부에서 동작하므로,
WSL의 IP 주소가 Android 앱에서 접근 가능해야 합니다.

 
ip addr show eth0

위 명령으로 나온 IP 중 inet 항목 예: 172.20.117.58 등을 확인하세요.

 

 

✅ 2. Matter virtual device 실행 시 포트 확인

예를 들어 chip-tool로 디바이스를 띄울 때:

out/all-clusters-app/chip-all-clusters-app --secured-device-port 5540
  • Matter 디바이스는 보통 5540 포트를 사용합니다.
  • 이 포트가 **WSL 밖(Android 앱)**에서도 접근 가능해야 해요.

 

✅ 3. Android 앱(Android Studio)의 Commissioner 연결 대상 수정

Android에서 Matter 디바이스에 연결하려면 다음을 설정합니다:

  • 연결할 IP: 위에서 확인한 WSL의 IP (예: 172.20.117.58)
  • 포트: 디바이스 앱이 사용하는 포트 (예: 5540)

보통 Matter Controller 앱에서는 Manual Commissioning 기능이나 Network Scan 시 대상 IP와 포트를 설정할 수 있어야 합니다. 아니면 코드에서 직접 SetupPayload나 commissioning window 연결 시 정보를 수정해야 해요.

 

✅ 4. WSL 방화벽 설정 (필요시)

  • WSL2는 가상 네트워크 인터페이스를 사용하므로 Windows에서 WSL2로의 포트 포워딩이 필요할 수 있습니다.
  • 다음 명령어로 포트 포워딩을 설정할 수 있습니다:
 
netsh interface portproxy add v4tov4 listenport=5540 listenaddress=0.0.0.0 connectport=5540 connectaddress=<WSL_IP>

 

 

✅ 5. Android 기기와 WSL이 같은 네트워크에 있는지 확인

  • 만약 Android Studio의 에뮬레이터 대신 실제 안드로이드 기기를 사용한다면, PC와 같은 Wi-Fi에 연결되어 있어야 WSL에 접근 가능해요.

 

🧪 테스트

  • Android 앱에서 Commissioning 시도 → WSL의 디바이스 로그에서 연결 요청이 오는지 확인

 

추가 팁

  • Matter 디바이스가 commissioning window를 열어야 연결 가능
  • 예시:
 
chip-tool pairing onnetwork 1234 20202021 --commissioner-name my-commissioner

 

필요하면 디버그 로그나 연결 실패한 메시지 알려줘! 어디서 막히는지 함께 확인해줄게 🙂

+ Recent posts