import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
VerseGrip 스타일러스 시작하기
VerseGrips는 Inverse3, Inverse3X 및 MinVerse에 방향 추적 기능을 추가합니다. 이 문서에서는 VerseGrip 스타일러스의 설정 절차와 기능을 설명합니다.
VerseGrip 스타일러스 소개
VerseGrip 스타일러스는 방향 추적 기능과 손에 쥔 상태에서의 입출력(I/O)을 제공하기 위해 독자적인 고속 무선 통신 기술을 사용합니다. 다양한 애플리케이션에 폭넓게 활용할 수 있도록 설계되었습니다.
주요 기능은 다음과 같습니다:
- 사용자 입력 버튼 2개,
- 캘리브레이션 버튼 1개,
- 전원/대기 버튼 1개
- 상태 LED 표시등 2개
- USB-C 충전 포트
- 최대 1 KHz의 무선 통신 속도
- 배터리 사용 시간 10~12시간
- 하나의 밴드에서 하나의 동글에 최대 8개의 VerseGrip Stylus를 페어링할 수 있습니다.

고객 지원
기기 또는 작동에 관한 질문이나 우려 사항이 있으시면 지원팀에 문의해 주십시오. VerseGrip Stylus 내부에는 복잡하고 독자적인 무선 기술이 적용되어 있습니다. 기기를 분해할 경우 보증이 무효화되며 기기가 작동하지 않을 수 있습니다. 배송 기간이 길어지거나 수리 비용이 많이 드는 상황을 피하려면 절대로 기기를 분해하지 마십시오.
VerseGrip 스타일러스 설정 및 보정
이 섹션에서는 동글을 연결하고, VerseGrip Stylus의 전원을 켜고, 보정 절차를 수행하는 단계를 설명합니다.
설정
- 동글을 컴퓨터의 USB 포트에 연결하십시오.
- 핸들이 움직이지 않도록 평평한 표면에 올려놓으십시오. 버튼이 위를 향하고 볼 커넥터가 화면을 향하도록 하십시오.
전원 켜기 및 보정
- 전원 버튼을 한 번 누르십시오. 상태 표시등이 빨간색으로 켜졌다가 노란색으로 바뀌면, 기기 내부의 IMU가 자체 보정 작업을 진행 중임을 나타냅니다.
- 자체 보정 과정이 끝나면 스타일러스가 컴퓨터에 연결됩니다.
- 스타일러스가 성공적으로 연결되어 무선으로 데이터를 전송하면 상태 표시등이 녹색으로 깜빡입니다.
- 배터리 잔량이 매우 부족하면 VerseGrip Stylus가 2분마다 자동으로 꺼집니다. 즉시 충전하십시오.
재보정
보정 과정은 VerseGrip의 좌표계를 재설정하여, 보정 중에 기기를 잡고 있는 방향이 새로운 기준축이 되도록 합니다.
- 재보정을 하려면 원하는 위치에서 스타일러스를 잡은 상태로 보정 버튼을 3초 동안 누르고 있거나, 시뮬레이션에서 방향이 바뀔 때까지 계속 누르고 계십시오.
아래는 보정을 하지 않았을 때와 했을 때의 기기 동작을 비교한 시연입니다. 시연에서는 보정을 수행하기 전에 VerseGrip의 자세를 아래 그림과 같이 유지해야 합니다.

대기 모드 및 전원 끄기
- 전원 버튼을 한 번 눌러 대기 모드로 돌아갑니다. 상태 표시등이 흰색으로 깜빡이기 시작하며 데이터 전송이 중지됩니다.
- VerseGrip 스타일러스의 전원을 끄려면 전원 버튼을 5초 동안 누른 후 손을 떼십시오. 버튼을 떼면 상태 표시등이 꺼지고 아무런 표시도 나타나지 않습니다. 데이터 전송도 중단됩니다.
충전
- USB-C 충전 포트를 통해 VerseGrip 스타일러스를 연결하십시오.
- 전원에 연결되면 충전 상태 표시등이 파란색으로 계속 켜져 있다가, 충전이 완료되면 꺼집니다.
- 스타일러스의 전원이 꺼진 상태에서 전원에 연결하면 자동으로 켜집니다.
경고: 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);
}
}