VerseGrip 스타일러스 시작하기
버즈그립에 방향 추적 기능이 추가된 Inverse3. 이 문서에서는 버즈그립 스타일러스 설정 절차와 그 기능에 대해 간략하게 설명합니다.
버즈그립 스타일러스
버즈그립 스타일러스 Inverse3 독점적인 고속 무선 통신 기술을 사용하여 방향 추적과 손에 들고 있는 입력/출력(I/O)을 제공합니다. 다양한 애플리케이션에서 다용도로 사용할 수 있도록 설계되었습니다.
주요 기능은 다음과 같습니다:
- 사용자 입력 버튼 2개,
- 보정 버튼 1개,
- 전원/대기 버튼 1개, 그리고
- 2개의 인디케이터 RGB LED 조명
- 10~12시간의 배터리 수명
- 충전용 USB-c 연결
- 최대 1KHz의 무선 통신 속도
- 하나의 밴드에서 최대 8개의 VerseGrip 스타일러스를 동글에 페어링할 수 있습니다.

버즈그립 스타일러스의 외부에는 (1) Inverse3 짐벌에 장착되는 볼 커넥터, (2) 및 (3) 입력 버튼, (4) USB-C 충전 포트, ( 5 ) 보정 버튼, (6) 상태 LED, (7) 전원 버튼 등 7가지 주요 기능이 있습니다.
고객 지원
장치 또는 장치 작동에 대한 질문이나 우려 사항이 있는 경우 지원팀에 문의하여 도움을 받으세요. VerseGrip 스타일러스에는 복잡한 독점 무선 기술이 포함되어 있습니다. 장치를 개봉하면 보증이 무효화되고 작동이 불가능해질 수 있습니다. 배송 시간이 길어지고 수리 비용이 많이 드는 것을 방지하려면 장치를 열지 마세요.
VerseGrip 스타일러스 설정 및 보정
이 섹션에서는 동글 연결, VerseGrip 스타일러스 켜기 및 보정 단계를 간략하게 설명합니다.
설정
- 동글을 컴퓨터의 USB 단자에 꽂습니다.
- 손잡이는 움직이지 않는 평평한 표면에 놓고 버튼은 위쪽을 향하게 하고 볼 커넥터는 화면을 향하게 합니다.
전원 켜기 및 보정
- 전원 버튼을 한 번 누릅니다. 상태 표시등이 빨간색으로 바뀌었다가 파란색으로 바뀝니다. 파란색은 장치의 IMU가 자체 보정 중임을 나타냅니다.
- 자체 보정 후에는 상태 표시등이 0.5초에 한 번씩 빨간색으로 깜박입니다. 이것은 대기 모드입니다.
- 대기 모드에서 디바이스를 깨우려면 아무 버튼이나 누릅니다. 깨어나는 동안 상태 표시등이 녹색으로 깜박이다가 녹색으로 고정됩니다. 이제 무선으로 데이터를 전송 중입니다.
- 배터리가 부족하면 버즈그립 스타일러스는 2분마다 자동으로 꺼지므로 즉시 충전하세요.
재보정
- 재보정하려면 보정 버튼을 3초 동안 길게 누르거나 시뮬레이션에서 방향이 바뀐 것을 확인할 때까지 누릅니다. 보정 프로세스는 보정 중에 기기를 잡는 방향이 새로운 기준 축이 되도록 VerseGrip의 좌표계를 재설정합니다. 예를 들어, 아래는 보정하지 않았을 때와 보정했을 때의 디바이스 동작을 비교하는 데모입니다. 데모에서는 보정을 수행하기 전에 표시된 구성으로 VerseGrip을 잡고 있어야 합니다.

대기 및 전원 끄기
- 전원 버튼을 다시 누르면 대기 모드로 돌아갑니다. 상태 표시등이 빨간색으로 바뀌고 2초마다 깜박이며 데이터 전송이 중지됩니다.
- 버즈그립 스타일러스를 끄려면 전원 버튼을 5초간 누르고 있다가 놓습니다. 손을 놓으면 상태 표시등이 꺼지고 아무 표시도 나타나지 않습니다. 데이터 전송이 중지됩니다.
충전
- USB-C 충전 단자를 통해 VerseGrip 스타일러스를 전원에 연결합니다.
- 전원을 연결하면 충전 표시등이 파란색으로 켜지고, 완전히 충전되면 파란색으로 빠르게 깜박입니다. 전원을 연결하면 기기가 시작되고 대기 상태가 됩니다.
경고: 배터리가 완전히 방전되지 않도록 주의하세요. 일단 방전되면 충전이 유지되지 않을 수 있습니다. 이 경우 플러그를 꽂아 두는 한 VerseGrip 스타일러스는 계속 작동합니다. 이는 2024년 8월부터 12월 사이에 배송된 VerseGrip 스타일러스 디바이스에만 해당되는 문제입니다. 이 문제를 해결하는 펌웨어 업데이트를 받으려면 Haply 지원팀에 문의하세요.
상태 및 표시등 표시기
| 색상 | 조명 시퀀스 | 설명 |
|---|---|---|
| 노란색 | 빠른 페이드 인/아웃 | 초기 자동 센서 기준 보정 (VerseGrip을 테이블 위에 평평하게 놓으십시오) |
| 빠른 깜빡임 | 보정 시작 버튼을 눌렀으며, 보정 과정이 시작되었습니다 | |
| 단단한 | 보정 시작 버튼을 눌렀습니다. 보정이 완료되었습니다. | |
| 빨간색 | 빠른 페이드 인/아웃 | 어댑터가 분리되었습니다. 호스트에서 신호가 수신되지 않습니다. |
| 빠른 깜빡임 | 전원 버튼을 눌렀습니다. 종료 절차가 시작되었습니다. | |
| 녹색 | 점진적 시작/종료 | 어댑터 연결됨, VerseGrip 데이터 스트림 모드 활성화됨 |
| 단단한 | 어댑터 연결됨, VerseGrip 데이터 스트림 모드 활성화됨, USB 전원 연결됨 | |
| 흰색 | 점진적 시작/종료 | 어댑터 연결됨, VerseGrip 대기 모드에서 데이터 스트리밍 없음 |
| 단단한 | 어댑터 연결됨, VerseGrip 유휴/대기 모드(데이터 전송 없음), USB 전원 연결됨 | |
| 오렌지 | 플래시 (인터레이스 방식) | 배터리 부족 경고 표시, 현재 상태에서 알림이 교대로 표시됩니다 |
| 더블 플래시 (인터레이스 방식) | 배터리 잔량이 매우 낮을 때의 경고 표시가 현재 상태에서 간헐적으로 나타납니다 | |
| 청록색 | 점진적 시작/종료 | 기기 펌웨어 업데이트 모드 |
| (더 밝게) | 단단한 | 사용자가 버튼을 누르면, 현재 표시 색상이 단색으로 변하고 더 밝아집니다 |
| 꺼짐 | 빛이 없다 | 종료, 전원 끄기 |
| 충전 표시등 색상 | 조명 시퀀스 | 설명 |
|---|---|---|
| 파란색 | 단단한 | 충전 |
| 빠른 깜빡임 | 충전이 거의 완료되었습니다 | |
| 꺼짐 | 불이 안 들어옴 | 충전이 완료되었습니다 |
레거시 상태 및 표시등
아래의 상태 표시등은 이전 버전의 VerseGrip 스타일러스에서 사용되던 상태와 색상입니다.
| 색상 | 조명 시퀀스 | 설명 |
|---|---|---|
| 빨간색 | 단단한 | 구성 요소 초기화 보류 중 |
| 깜빡 | 대기 모드. 데이터 전송 없음 | |
| 파란색 | 단단한 | 초기 자동 센서 기준 보정 (VerseGrip을 테이블 위에 평평하게 놓으십시오) |
| 녹색 | 깜빡 | 어댑터 연결됨, VerseGrip 활성 데이터 스트림 모드 |
| 꺼짐 | 빛이 없다 | 종료, 전원 끄기 |
| 충전 표시등 색상 | 조명 시퀀스 | 설명 |
|---|---|---|
| 파란색 | 단단한 | 충전 |
| 빠른 깜빡임 | 충전이 거의 완료되었습니다 | |
| 꺼짐 | 불이 안 들어옴 | 충전이 완료되었습니다 |
샘플 코드
다음은 C++에서 VerseGrip 스타일러스를 사용하는 방법에 대한 간단한 예시입니다.
- Inverse SDK
- 하드웨어API
#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);
}
}