[StepLED 프로젝트] 가상과 현실을 연결한 양방향 제어 시스템 구현

후추쌈·2025년 4월 29일
1
post-thumbnail

이제 실제 ESP32 보드의 LED 상태를 실시간으로 Autodesk Tandem에 반영하려 한다. 또한 Tandem에 업로드한 가상 모델을 웹 대시보드에 띄우고, 화면 속 스위치 모델을 클릭하면 실제 스위치가 눌리고 LED 상태가 변경되며, 이 변화가 다시 Tandem과 웹 화면에 동기화되도록 구현할 예정이다.

ESP32 보드와 Tandem 연결하기

ESP32에서 수집한 LED 상태 데이터를 Wi-Fi 2.4GHz를 통해 Tandem API로 전송한다.

우선 3-Legged Token이 필요하다.
이 블로그를 참고하여 3-Legged Token을 생성하여 API 요청할 때 같이 보내준다.

Tandem > Users 메뉴에서 Add API key 옵션을 선택한 후, Client ID를 입력하고 PermissionManage로 설정하면, 해당 App(Client)이 외부에서 Tandem Facility에 API를 통해 접근하고 조작할 수 있는 권한을 갖게 된다.

각 Stream ID별로 개별 URL에 데이터를 전송할 수도 있지만, Generic Webhook Endpoint를 사용하면 여러 Stream의 데이터를 하나의 HTTP POST 요청으로 통합하여 전송할 수 있다.

  • 요청 URL
https://tandem.autodesk.com/api/v1/timeseries/models/urn:adsk.dtm:Fk2zdny7QDKxF07arLserg/webhooks/generic
  • Payload
    id: 각각의 Stream ID (Tandem에서 스트림 생성 시 자동 생성된 고유 ID)
    value: 해당 Stream에 업데이트할 값 (숫자 또는 Boolean 등)
[
  {
    "id": "AQAAAMnQ2pAQd0IOhK647hF6A30AAAAA",
    "value": 1.0
  },
  {
    "id": "AQAAAKBmehwDIEhirCRRkXjT_GIAAAAA",
    "value": 0.0
  },
  {
    "id": "AQAAAFTDpe_Zg0hFhKJalFYSkioAAAAA",
    "value": 0.0
  },
  {
    "id": "AQAAAOD64XPdz0X-pr2YAFp5BkEAAAAA",
    "value": 1.0
  }
]


Postman에서 Tandem Generic Webhook Endpoint를 테스트해 200 OK 응답이 정상적으로 반환되는 것을 확인한 후, ESP32 펌웨어를 수정해준다.

ESP32는 WiFi.h를 이용해 Wi-Fi에 연결하고, HTTPClient.h를 사용해 Tandem Webhook으로 HTTP POST 요청을 전송한다.
시리얼 모니터를 통해 스위치를 누를 때마다 HTTP Response: 200 응답이 출력되고, Tandem에 값이 반영되는 것을 확인한다.

가상 모델에서 실제 모델 제어하기

Autodesk Forge Viewer를 사용해 웹에 가상 LED 모델을 시각화하고, 사용자가 스위치 가상 모델을 클릭하면, 이 이벤트가 실제 ESP32 장치로 전달되어 물리적인 LED가 제어된다.
이후 해당 상태는 Autodesk Tandem에 동기화되어, 가상 모델과 현실 상태가 실시간으로 일치하도록 구현했다.

우선 Autodesk Forge Viewer를 사용하기 위해 이번엔 2-Legged Token이 필요하다.

Autodesk Platform Services에서 Server-to-Server App을 선택하여 Client IDClient Secret을 생성한다.

그리고 3-legged 토큰을 발급할 때 사용한
https://github.com/YenaLey/Digital-Twin 의 ApsTokenGenerator 에서 마찬가지로 2-legged 토큰을 발급할거다.

2-Legged Flow 탭에서 다음과 같이 발급하면 된다.

추가로,
Revit 모델을 Forge Viewer SDK을 통해 웹에 띄울 수 있는 간단한 데모 기능을 만들었다.

RVT 파일을 올려 업로드하면,
해당 파일은 Autodesk Forge의 Object Storage에 저장되고 objectId가 부여된다.
이 objectId는 Base64로 인코딩되어 URN으로 변환되며, 이를 이용해 Derivatives API에 SVF 포맷(2D 및 3D 뷰 포함)으로의 번역이 요청된다. 번역이 완료되어 생성된 URN을 통해 웹에서 해당 모델을 바로 시각화할 수 있다. 눈 아이콘을 눌러보자.

이렇게 이제 여기서 발급한 2-Legged Token과 변환된 URN을 사용해, 직접 만든 Next.js 웹 프로젝트에 Forge Viewer SDK로 모델을 띄울 것이다.
프론트엔드와 백엔드를 모두 통합할 수 있고, 서버리스로 간편하게 구현 가능한 Next.js을 선택했다.

우선 모델을 띄우기 위해 위에서 생성한 2-Legged TokenURN을 사용하여 Viewer 스크립트를 로드한 후 다음과 같이 초기화한다:

Autodesk.Viewing.Initializer({ accessToken }, () => {
  viewer.start();
  Autodesk.Viewing.Document.load(`urn:${URN}`, (doc) => {
    viewer.loadDocumentNode(doc, doc.getRoot().getDefaultGeometry());
  });
});

모델이 로드되면, 각 요소에는 dbId가 부여되며, 사용자가 특정 요소(예: 스위치)를 클릭할 경우 이벤트로 다음과 같이 제어할 수 있다:

viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, (event) => {
  const dbId = event.dbIdArray[0];
  if (dbId === switchDbId) {
    fetch("/api/esp32/toggle-led", { method: "POST" });
  }
});

이 요청은 내부적으로 ESP32 장치의 /toggle-led 엔드포인트를 호출하며, 해당 핸들러에서는 현재 점등 중인 LED를 순차적으로 전환하는 함수 toggleLed()가 실행된다. 즉, Forge Viewer에서의 클릭 이벤트가 실제 ESP32의 물리적 회로를 직접 제어하는 구조이다.

ESP32는 LED의 상태를 2초마다 Tandem Webhook을 통해 전송한다.
웹에서는 /api/tandem/latest 엔드포인트를 2초마다 호출하여 주기적으로 데이터를 받아 가상 LED 색상을 시각적으로 반영한다:

setInterval(async () => {
  const data = await fetch("/api/tandem/latest").then(res => res.json());
  viewer.setThemingColor(dbId1, data.led1 ? red : redDim);
}, 2000);

이렇게 가상 스위치 클릭 → 실제 장치 제어 → 상태 Tandem에 전송 → 가상 모델 갱신이라는 양방향 디지털 트윈 제어 구조를 구현하였다.

다음은 위에서 설명한 구조를 기반으로 구현한 프로젝트의 코드이다.
https://github.com/YenaLey/Digital-Twin/tree/main/StepLED

이 프로젝트 public > assets 안에 RVT 모델 파일과 ESP32용 펌웨어 코드도 함께 포함하였다.
프로젝트를 실행하기 위해서는 루트 디렉터리에 .env 파일을 생성한 후, 아래와 같이 환경변수 값을 설정해주면 된다:

NEXT_PUBLIC_FORGE_ACCESS_TOKEN=<2-Legged Token>
NEXT_PUBLIC_FORGE_URN=<Forge URN>  

TANDEM_TOKEN=<3-Legged Token>   
TANDEM_MODEL_URN=<Tandem Model URN> 

ESP32_HOST=<http://[ESP32 IP]>

이번 프로젝트에 대한 회고 및 앞으로의 목표

현재 구조는 ESP32가 2초마다 Tandem에 상태 데이터를 HTTP POST 방식으로 전송하고, 웹 대시보드는 2초마다 Tandem의 마지막 상태를 Polling한다. 구조적으로 간단하고 구현이 쉬운 장점이 있지만, 상태 변화가 없는 경우에도 반복적인 네트워크 요청이 발생하며, 실제 변화가 발생한 시점과 화면 반영 사이에 delay가 생길 수밖에 없다.

이러한 풀(Polling) 기반 구조는 불필요한 트래픽을 발생시킨다는 점에서 실시간 동기화가 중요한 디지털 트윈 구현에 적합하지 않다.

이를 개선하기 위해, 상태 변화가 발생했을 때만 데이터를 전송하는 이벤트 기반 구조로 전환하려 한다.
WebSocket을 활용하여 양방향 연결을 유지하면, 상태 변화가 있을 때마다 즉시 push 방식으로 통신할 수 있어 폴링 없이도 빠른 반응이 가능하다.

또는, MQTT를 사용하여 ESP32와 서버, 웹 클라이언트 간에 pub/sub(발행-구독) 구조로 실시간 상태를 브로드캐스팅할 수도 있다. 이는 여러 장치가 동시에 연결된 상황에서 유리하다.

WebSocketMQTT의 장단점을 비교 분석한 후, 실시간성이 핵심인 디지털 트윈 서비스에 더 적합한 방식을 선택해 구조를 개선할 계획이다.
이를 통해, 보다 빠르고 정확한 상태 반영이 가능한 실시간 디지털 트윈 플랫폼으로 발전시키도록 방안을 찾는 것이 다음 목표이다.

profile
LEE YENA

0개의 댓글