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

고객 지원
기기 또는 기기 작동에 대해 궁금한 점이나 우려 사항이 있으시면 지원팀에 문의해 주십시오. VerseGrip Stylus에는 복잡하고 독자적인 무선 기술이 적용되어 있습니다. 기기를 개봉할 경우 보증이 무효화될 뿐만 아니라 기기가 작동하지 않을 수도 있습니다. 배송 지연이나 고액의 수리 비용을 피하기 위해기기를 개봉하지 마십시오.
VerseGrip 스타일러스의 설정 및 보정
이 섹션에서는 동글 연결, VerseGrip Stylus 전원 켜기 및 보정 절차에 대해 설명합니다.
설정
- 동글을 컴퓨터의 USB 포트에 연결합니다.
- 핸들은 움직이지 않는 평평한 표면에 올려 놓으십시오. 버튼은 위를 향하게 하고, 볼 커넥터는 화면 쪽을 향하게 하십시오.
전원 켜기 및 보정
- 전원 버튼을 한 번 누릅니다. 상태 표시등이 빨간색으로 켜진 후, 장치 내부의 IMU가 자체 보정 중임을 나타내기 위해 노란색으로 바뀝니다.
- 자동 보정이 완료되면 스타일러스가 컴퓨터에 연결됩니다.
- 스타일러스가 정상적으로 연결되어 무선으로 데이터를 전송하고 있는 상태가 되면, 상태 표시등이 녹색으로 깜빡입니다.
- 배터리 잔량이 극히 적은 경우, VerseGrip Stylus는 2분마다 자동으로 전원이 꺼집니다.즉시 충전해 주십시오.
재보정
보정 처리를 통해 VerseGrip의 좌표계가 재설정되며, 보정 중에 장치를 잡고 있던 방향이 새로운 기준축이 됩니다.
- 재보정을 하려면, 재설정하려는 위치에서 스타일러스를 고정하고 보정 버튼을 3초 동안 누르고 있거나, 시뮬레이션 내에서 자세가 바뀔 때까지 계속 누르고 계십시오.
다음은 기기가 보정되지 않은 상태와 보정된 상태에서의 동작을 비교한 데모입니다. 데모를 진행하려면 보정을 실행하기 전에 그림과 같이 VerseGrip을 잡은 상태를 유지해야 합니다.

대기 모드와 전원 끄기
- 전원 버튼을 한 번 누르면 대기 모드로 돌아갑니다. 상태 표시등이 흰색으로 깜빡이기 시작하며, 데이터 전송이 중단됩니다.
- VerseGrip 스타일러스의 전원을 끄려면 전원 버튼을 5초 동안 길게 누른 다음 손을 떼십시오. 손을 떼면 상태 표시등이 꺼지고 화면이 비어집니다. 데이터 전송도 중단됩니다.
충전
- VerseGrip 스타일러스를 USB-C 충전 포트에 연결합니다.
- 전원에 연결되어 있는 동안 충전 상태 표시등이 파란색으로 켜져 있으며, 충전이 완료되면 꺼집니다.
- 스타일러스가 꺼진 상태에서 전원에 연결하면 자동으로 켜집니다.
경고: 2024년 8월부터 12월 사이에 출하된 VerseGrip Stylus를 사용 중이신 경우, 배터리를 완전히 방전시키지 마십시오. 한 번 완전히 방전되면 충전을 유지하지 못할 수 있습니다. 이 경우에도 전원에 연결된 상태라면 VerseGrip Stylus는 계속 작동합니다. 이 문제를 해결하는 펌웨어 업데이트 파일을 받으시려면 Haply 고객 지원팀에 문의해 주십시오.
상태 및 표시등
| 색 | 조명 시퀀스 | 설명 |
|---|---|---|
| 노란색 | 고속 페이드 인/아웃 | 초기 자동 센서 기준 보정 (VerseGrip을 받침대 위에 고정해 주십시오) |
| 고속 깜빡임 | 자세 보정 버튼이 눌려 보정 처리가 시작되었습니다. | |
| 점등 | 자세 보정 버튼이 눌려 보정이 완료되었습니다. | |
| 빨강 | 고속 페이드 인/아웃 | 어댑터가 분리되어 호스트로부터 신호가 수신되지 않습니다. |
| 고속 깜빡임 | 전원 버튼이 눌려 시스템 종료 처리가 시작되었습니다. | |
| 녹색 | 페이드 인/아웃 | 어댑터가 연결되어 있으며, VerseGrip은 활성 데이터 스트림 모드입니다. |
| 점등 | 어댑터가 연결되어 있고, VerseGrip이 활성 데이터 스트림 모드에 있으며, USB 전원도 연결되어 있습니다. | |
| 하얀 | 페이드 인/아웃 | 어댑터가 연결되어 있으며, VerseGrip은 유휴/대기 모드 상태이므로 데이터 스트리밍이 이루어지지 않습니다. |
| 점등 | 어댑터가 연결되어 있고, VerseGrip이 유휴/대기 모드이며 데이터 스트리밍이 이루어지지 않고, USB 전원도 연결되어 있습니다. | |
| 오렌지 | 플래시 (인터레이스) | 배터리 잔량 부족 경고 메시지입니다. 알림은 현재 상태에서 교차 표시됩니다. |
| 더블 플래시 (인터레이스) | 배터리 잔량이 위험할 정도로 낮아졌음을 알리는 경고 표시입니다. 알림은 현재 상태에서 교차 표시됩니다. | |
| 티얼 | 페이드 인/아웃 | 이것은 기기의 펌웨어 업데이트 모드입니다. |
| (밝은) | 점등 | 사용자 버튼이 눌렸습니다. 현재 표시 색상이 켜지면서 더 밝아집니다. |
| 꺼짐 | 소등 | 종료, 전원 끄기 |
| 충전 표시등 색상 | 조명 시퀀스 | 설명 |
|---|---|---|
| 청 | 점등 | 충전 중 |
| 고속 깜빡임 | 충전이 거의 끝났습니다 | |
| 꺼짐 | 소등 | 충전 완료 |
이전 버전의 상태 및 표시등
다음 상태 표시등은 VerseGrip Stylus의 이전 버전(펌웨어 1.10 이하)에서 볼 수 있었던 구형 상태 및 색상입니다.
| 색 | 조명 시퀀스 | 설명 |
|---|---|---|
| 빨강 | 점등 | 컴포넌트 초기화 대기 중 |
| 깜빡임 | 대기 모드. 데이터 전송 없음 | |
| 청 | 점등 | 초기 자동 센서 기준 보정 (VerseGrip을 받침대 위에 고정해 주십시오) |
| 녹색 | 깜빡임 | 어댑터가 연결되어 있으며, VerseGrip은 활성 데이터 스트림 모드입니다. |
| 꺼짐 | 소등 | 종료, 전원 끄기 |
| 충전 표시등 색상 | 조명 시퀀스 | 설명 |
|---|---|---|
| 청 | 점등 | 충전 중 |
| 고속 깜빡임 | 충전이 거의 끝났습니다 | |
| 꺼짐 | 소등 | 충전 완료 |
샘플 코드
다음은 VerseGrip 스타일러스를 C++과 함께 사용하는 간단한 예제입니다.
#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);
}
}