본문으로 건너뛰기

import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';

VerseGrip Stylus 시작하기

VerseGrips는 Inverse3, Inverse3X 및 MinVerse에 방향 추적 기능을 추가합니다. 이 문서에서는 VerseGrip Stylus의 설정 절차와 기능을 설명합니다.

VerseGrip Stylus 소개​

VerseGrip Stylus는 방향 추적과 손에 쥔 상태에서의 입출력(I/O)을 제공하기 위해 독자적인 고속 무선 통신 기술을 사용합니다. 다양한 애플리케이션에 폭넓게 활용할 수 있도록 설계되었습니다.

주요 기능은 다음과 같습니다:

  • 사용자 입력 버튼 2개,
  • 캘리브레이션 버튼 1개,
  • 전원/대기 버튼 1개
  • 상태 LED 표시등 2개
  • USB-C 충전 포트
  • 최대 1 KHz의 무선 통신 속도
  • 배터리 수명 10~12시간
  • 하나의 밴드에서 동글 하나에 최대 8개의 VerseGrip Stylus 페어링 가능

고객 지원​

기기 또는 작동에 관해 질문이나 우려 사항이 있으시면 지원팀에 문의해 주십시오. VerseGrip Stylus 내부에는 복잡하고 독자적인 무선 기술이 포함되어 있습니다. 기기를 열면 보증이 무효화되며 작동하지 않게 될 수 있습니다. 배송 기간이 길어지거나 수리 비용이 많이 드는 것을 피하려면 기기를 절대 열지 마십시오.

VerseGrip Stylus 설정 및 캘리브레이션​

이 섹션에서는 동글을 연결하고, VerseGrip Stylus의 전원을 켜고, 캘리브레이션을 수행하는 단계를 설명합니다.

설정​

  • 동글을 컴퓨터의 USB 포트에 연결하십시오.
  • 핸들을 움직이지 않을 평평한 표면에 놓으십시오. 버튼이 위를 향하고 볼 커넥터가 화면을 향하도록 합니다.

전원 켜기 및 캘리브레이션​

  • 전원 버튼을 한 번 누르십시오. 상태 표시등이 빨간색으로 켜졌다가 노란색으로 바뀌며, 기기 내 IMU가 자체 캘리브레이션을 진행 중임을 나타냅니다.
  • 자체 캘리브레이션이 끝나면 스타일러스가 컴퓨터에 연결됩니다.
  • 스타일러스가 성공적으로 연결되어 무선으로 데이터를 전송하면 상태 표시등이 녹색으로 깜박입니다.
  • 배터리가 매우 부족하면 VerseGrip Stylus가 2분마다 자동으로 꺼집니다. 즉시 충전하십시오.

재캘리브레이션​

캘리브레이션 과정은 VerseGrip의 좌표계를 재설정하여, 캘리브레이션 중 기기를 잡고 있는 방향이 새로운 기준 축이 되도록 합니다.

  • 재캘리브레이션하려면 원하는 위치에서 스타일러스를 잡은 상태로 캘리브레이션 버튼을 3초 동안 누르고 있거나, 시뮬레이션에서 방향이 바뀔 때까지 누르고 있으십시오.

아래는 캘리브레이션을 하지 않았을 때와 했을 때의 기기 동작을 비교한 시연입니다. 시연에서는 캘리브레이션을 수행하기 전에 VerseGrip의 자세를 아래 그림과 같이 유지해야 합니다.

대기 모드 및 전원 끄기​

  • 전원 버튼을 한 번 눌러 대기 모드로 돌아갑니다. 상태 표시등이 흰색으로 깜박이기 시작하며 데이터 전송이 중지됩니다.
  • VerseGrip Stylus의 전원을 끄려면 전원 버튼을 5초 동안 누른 후 떼십시오. 버튼에서 손을 떼면 상태 표시등이 꺼지고 아무 표시도 나타나지 않습니다. 데이터 전송도 중지됩니다.

충전​

  • USB-C 충전 포트를 통해 VerseGrip Stylus를 연결하십시오.
  • 전원에 연결되면 충전 상태 표시등이 파란색으로 계속 켜지며, 완전히 충전되면 꺼집니다.
  • 스타일러스가 꺼진 상태에서 전원에 연결하면 자동으로 켜집니다.

경고: 2024년 8월부터 12월 사이에 출고된 VerseGrip Stylus를 사용 중이라면 배터리가 완전히 방전되지 않도록 하십시오. 배터리가 방전되고 나면 전하를 유지하지 못할 수 있습니다. 이 경우 VerseGrip Stylus는 전원에 연결된 상태를 유지하는 한 계속 작동합니다. 이 문제를 해결하기 위한 펌웨어 업데이트 파일을 받으려면 Haply 지원팀에 문의하십시오.

상태 및 표시등​

색상표시등 패턴설명
노란색빠른 페이드 인/아웃초기 자동 센서 기준 캘리브레이션 (VerseGrip를 테이블 위에 가만히 올려 두십시오)
빠른 깜박임방향 캘리브레이션 버튼이 눌려 캘리브레이션 과정이 시작됨
계속 켜짐방향 캘리브레이션 버튼이 눌려 캘리브레이션이 완료됨
빨간색빠른 페이드 인/아웃어댑터 연결 해제, 호스트로부터 신호 없음
빠른 깜박임전원 버튼이 눌려 종료 과정이 시작됨
녹색페이드 인/아웃어댑터 연결됨, VerseGrip 활성 데이터 스트림 모드
계속 켜짐어댑터 연결됨, VerseGrip 활성 데이터 스트림 모드, USB 전원 연결됨
흰색페이드 인/아웃어댑터 연결됨, VerseGrip 유휴/대기 모드, 데이터 스트리밍 없음
계속 켜짐어댑터 연결됨, VerseGrip 유휴/대기 모드, 데이터 스트리밍 없음, USB 전원 연결됨
주황색점멸 (인터레이스)배터리 부족 경고 표시, 현재 상태에서 알림이 인터레이스 방식으로 표시됨
이중 점멸 (인터레이스)배터리 매우 부족 경고 표시, 현재 상태에서 알림이 인터레이스 방식으로 표시됨
청록색페이드 인/아웃기기 펌웨어 업데이트 모드
(더 밝음)계속 켜짐사용자 버튼이 눌림, 현재 표시 색상이 계속 켜져 있으며 더 밝음
꺼짐표시등 없음종료, 전원 꺼짐
충전 표시등 색상표시등 패턴설명
파란색계속 켜짐충전 중
빠른 깜박임충전 거의 완료
꺼짐표시등 없음충전 완료

이전 버전의 상태 및 표시등​

아래 표시등 상태와 색상은 VerseGrip Stylus의 이전 버전(펌웨어 1.10 이하)에서 사용되던 레거시 상태입니다.

색상표시등 패턴설명
빨간색계속 켜짐구성 요소 초기화 대기 중
깜박임대기 모드. 데이터 전송 없음
파란색계속 켜짐초기 자동 센서 기준 캘리브레이션 (VerseGrip를 테이블 위에 가만히 올려 두십시오)
녹색깜박임어댑터 연결됨, VerseGrip 활성 데이터 스트림 모드
꺼짐표시등 없음종료, 전원 꺼짐
충전 표시등 색상표시등 패턴설명
파란색계속 켜짐충전 중
빠른 깜박임충전 거의 완료
꺼짐표시등 없음충전 완료

샘플 코드​

다음은 C++ 연동 환경에서 VerseGrip Stylus를 사용하는 방법을 보여 주는 간단한 예제입니다.

#include <external/libhv.h>

#include <nlohmann/json.hpp>

#include <chrono>
#include <cstdio>
#include <string>

using namespace hv;
using json = nlohmann::json;

// Procedure to get the first detected and available Wired VerseGrip Stylus device id
std::string get_first_verse_grip_device_id(const json &data) {
const auto& vgs = data["wireless_verse_grip"];

if (vgs.empty()) {
return "";
}

return vgs.items().begin().key();
}

int main() {
const auto print_delay = std::chrono::milliseconds(100);
auto current = std::chrono::high_resolution_clock::now();
bool first_message = true;
std::string device_id;

WebSocketClient ws;

ws.onmessage = [&](const std::string &msg) {
json data = json::parse(msg);

if (first_message) {
first_message = false;
const std::string first_id = get_first_verse_grip_device_id(data);

if (first_id.empty()) {
printf("no Wireless VerseGrip found.\n");
ws.close();
return;
}

device_id = first_id;
}

if (device_id.empty() || !data["wireless_verse_grip"].contains(device_id)) {
return;
}

const auto now = std::chrono::high_resolution_clock::now();

if (std::chrono::high_resolution_clock::now() > current + print_delay) {
current = now;
const json state = data["wireless_verse_grip"][device_id];

printf("Rotation : { x:%f, y:%f, z:%f, w:%f }, Hall:%i, Button : [%d, %d, %d], Battery: {%f}\n",
state["orientation"]["x"].get<float>(),
state["orientation"]["y"].get<float>(),
state["orientation"]["z"].get<float>(),
state["orientation"]["w"].get<float>(),
state["hall"].get<int8_t>(),
state["buttons"]["a"].get<bool>(),
state["buttons"]["b"].get<bool>(),
state["buttons"]["c"].get<bool>(),
state["battery_level"].get<float>());
}
};

ws.open("ws://localhost:10000");

printf("Press ENTER to stop...\n\n");
while (std::cin.get() != '\n') {
}

if (ws.isConnected()) {
ws.close();
}

return 0;
}
#include <string.h>

#include <chrono>
#include <iostream>
#include <iterator>
#include <string>
#include <thread>

#include "HardwareAPI.h"

int main(int argc, char* argv[])
{
char* portName;

if (argc < 2)
{
std::printf("Usage: %s <port>\n", argv[0]);
}
else
{
#if defined(_WIN32) || defined(_WIN64)
portName = _strdup(argv[1]); // argv1;
#endif
#if defined(__linux__)
portName = strdup(argv[1]); // argv1;
#endif
}

Haply::HardwareAPI::IO::SerialStream serial_stream(portName);

Haply::HardwareAPI::Devices::Handle handle(&serial_stream);

while (true)
{
Haply::HardwareAPI::Devices::Handle::VersegripStatusResponse data;
data = handle.GetVersegripStatus();
std::printf(
"device_id: %d battery_level: %f quaternion: %f %f %f %f buttons: "
"%d error_flags: %d\n",
data.device_id, data.battery_level, data.quaternion[0],
data.quaternion[1], data.quaternion[2], data.quaternion[3],
data.buttons, data.error_flag);

}
}