import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
VerseGrip 触控笔入门指南
VerseGrips 为Inverse3 、Inverse3X 和MinVerse 增加了方向检测功能。本文介绍了 VerseGrip 触控笔的设置方法及其功能。
VerseGrip 触控笔
VerseGrip 触控笔采用专有的高速无线技术,可实现方向检测以及手持输入/输出(I/O)功能。它用途广泛,专为多种应用场景而设计。
主要功能包括:
- 2 个用户输入键,
- 1 校准键,
- 1 个开机/待机按钮
- 2 个状态指示灯
- USB-C 充电接口
- 频率最高达 1 kHz 的无线电通信
- 10-12小时的电池续航时间
- 一条数据线上最多可将8支VerseGrip触控笔与一个加密狗配对

客户支持
如果您对设备或其操作有任何疑问或问题,请联系我们的支持团队。VerseGrip 触控笔采用了复杂的专有无线技术。擅自拆开设备将导致保修失效,并可能导致设备无法正常工作。为避免延长寄送时间及产生高额维修费用,请勿拆开设备。
VerseGrip 触控笔的设置与校准
本节介绍了插入加密狗、开启 VerseGrip Stylus 以及进行校准的步骤。
设施
- 将加密狗插入计算机的USB接口中。
- 将手柄放在不会滑动的平坦表面上,按键朝上,球形接口朝向屏幕。
开机与校准
- 按一下电源键。状态指示灯会先亮起红色,然后变为黄色,这表明设备中的IMU正在进行自校准。
- 完成自动校准后,触控笔将与计算机连接。
- 一旦触控笔成功连接并开始无线传输数据,状态指示灯就会闪烁绿色。
- 当电池电量极低时,VerseGrip Stylus 会每 2 分钟自动关机一次;请立即为其充电。
重新校准
校准过程会重置 VerseGrip 的坐标系,使得校准过程中握持设备的方向成为新的参考轴。
- 要重新校准,请将触控笔保持在您希望重新校准的位置,然后按住校准按钮三秒钟,或直到模拟中的对齐状态发生变化。
下文将展示一个演示,对比该设备在未校准和已校准状态下的工作表现。在进行演示时,请在校准前将VerseGrip保持在图示的配置状态。

待机和关机
- 按一下电源键,即可返回待机模式。状态指示灯将开始闪烁白色,数据传输随即终止。
- 要关闭 VerseGrip 触控笔,请长按电源键 5 秒,然后松开。松开后,状态指示灯应熄灭,屏幕将不再显示任何内容。此时数据传输将终止。
装载
- 通过 USB-C 充电接口将 VerseGrip 触控笔连接至电源。
- 连接电源后,充电状态指示灯会持续亮起蓝色,当电池充满电时,指示灯会熄灭。
- 如果触控笔处于关闭状态,将其连接到电源后,它会自动开启。
警告:对于2024年8月至12月期间发货的VerseGrip Stylus设备,请勿让电池完全放电。一旦电池电量耗尽,在某些情况下可能无法再保持电量。如果发生这种情况,VerseGrip Stylus在连接电源时仍可正常工作。请联系Haply 技术支持,获取用于解决此问题的固件更新文件。
状态和指示灯
| 颜色 | 灯光序列 | 描述 |
|---|---|---|
| 格尔布 | 快速显示/隐藏 | 传感器基准值的自动初始校准(将VerseGrip静置于桌面上) |
| 快速闪烁 | 按下对准校准按钮,校准过程已启动 | |
| 持久 | 按下对准校准键,校准完成 | |
| 腐烂 | 快速显示/隐藏 | 适配器已断开,未收到主机信号 |
| 快速闪烁 | 按下开/关按钮,已启动关机程序 | |
| 格伦 | 显示/隐藏 | 适配器已连接,VerseGrip 处于活动数据流模式 |
| 持久 | 适配器已连接,VerseGrip 处于活动数据流模式,USB 电源已连接 | |
| 魏斯 | 显示/隐藏 | 适配器已连接,VerseGrip 处于空闲/待机模式,未进行数据传输 |
| 持久 | 适配器已连接,VerseGrip 处于空闲/待机模式且未进行数据传输,USB 供电已连接 | |
| 橙色 | 闪电战(嵌套) | 电池电量低警告提示,当前状态下的通知嵌套显示 |
| 双重闪电(嵌套) | 电池电量过低警告提示,通知与当前状态嵌套显示 | |
| 土耳其石 | 显示/隐藏 | 设备的固件更新模式 |
| (海勒) | 持久 | 按下用户按键后,当前显示颜色将保持不变且更亮 |
| Aus | 无灯具 | 关机,关闭 |
| 充电指示灯颜色 | 灯光序列 | 描述 |
|---|---|---|
| 布劳 | 持久 | 装载 |
| 快速闪烁 | 充电过程即将完成 | |
| Aus | 无灯具 | 充电已完成 |
旧版状态和指示灯
以下状态指示灯显示的是早期版本的VerseGrip Stylus(固件版本1.10及更早)中出现过的状态和颜色。
| 颜色 | 灯光序列 | 描述 |
|---|---|---|
| 腐烂 | 持久 | 组件初始化尚未完成 |
| 布林肯 | 待机模式。不进行数据传输 | |
| 布劳 | 持久 | 传感器基准值的自动初始校准(将VerseGrip静置于桌面上) |
| 格伦 | 布林肯 | 适配器已连接,VerseGrip 处于活动数据流模式 |
| Aus | 无灯具 | 关机,关闭 |
| 充电指示灯颜色 | 灯光序列 | 描述 |
|---|---|---|
| 布劳 | 持久 | 装载 |
| 快速闪烁 | 充电过程即将完成 | |
| Aus | 无灯具 | 充电已完成 |
示例代码
以下是一个关于如何在 C++ 中使用 VerseGrip 触控笔的简单示例。
#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);
}
}