Skip to main content

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

Getting started with the VerseGrip Stylus

VerseGrips add orientation tracking abilities to the Inverse3, Inverse3X and MinVerse. This article outlines the VerseGrip Stylus setup procedure and its features.

The VerseGrip Stylus

The VerseGrip Stylus uses proprietary high-speed wireless communication technology to provide orientation tracking and in-hand input/output (I/O). It is engineered for versatility across a range of applications.

Key features include:

  • 2 user input buttons,
  • 1 calibration button,
  • 1 power/standby button
  • 2 status LED lights
  • USB-C charging port
  • Wireless communication speed up to 1 KHz
  • 10-12 hours battery life
  • Up to 8 VerseGrip Stylus can pair to a dongle on a single band

Customer Support

If you have any questions or concerns about your device or its operation, please contact our support team for assistance. The VerseGrip Stylus contains complex, proprietary wireless technology. Opening the device will void your warranty and may render it inoperable. To avoid extended shipping times and expensive repairs, DO NOT OPEN THE DEVICE.

VerseGrip Stylus Setup and Calibration

This section outlines the steps of plugging in the dongle, turning on your VerseGrip Stylus, and calibration.

Set Up

  • Plug in the dongle into a USB port in your computer.
  • Put the handle on a flat surface where it wont move, the buttons facing upwards and ball connector pointed towards the screen.

Power On and Calibration

  • Press the power button once. The status light will turn red, then change to yellow to indicate that the IMU in the device is undergoing self-calibration.
  • After self-calibration, the stylus will connect to the computer.
  • When the stylus is succesfully connected and transmitting data through wireless, the status light will blink green.
  • If the battery is critically low, the VerseGrip Stylus will turn itself off every 2 minutes; please charge it right away.

Recalibration

The calibration process resets the coordinate system of the VerseGrip so that the direction in which the device is held during calibration becomes the new reference axis.

  • To recalibrate, hold the stylus in the position you would like to recalibrate to, press and hold the calibration button for three seconds, or until the orientation shifts in the simulation.

Below is a demonstration that compares the device’s behavior when it is not calibrated versus when it is calibrated. For our demonstrations, the VerseGrip must be held in the configuration shown before calibration is performed.

Standby and Power Off

  • Press the power button once to go back into standby mode. The status light will start blinking white, and data transmission will stop.
  • To turn off the VerseGrip Stylus, hold the power button for 5 seconds and let go. When you let go, the status light should turn off and there will be no indications. It will stop transmitting data.

Charging

  • Plug the VerseGrip Stylus via the USB-C charging port.
  • The charging status light will be solid blue when plugged into power, and when fully charged it will turn off.
  • If the stylus is off when you plug into power, it will automatically turn on.

WARNING: If you have a VerseGrip Stylus that was shipped between August and December 2024 - Do not allow the battery to fully discharge. Once depleted, it may not retain a charge. If this happens, the VerseGrip Stylus will continue to operate as long as it remains plugged in. Contact Haply Support to receive the firmware update file to fix this issue.

States and Light Indicators

ColourLight sequenceDescription
YellowFast Fade in/outInitial automated sensor reference calibration (lay VerseGrip still on table)
Fast BlinkOrientation calibration button pressed, calibration process started
SolidOrientation calibration button pressed, calibration complete
RedFast Fade in/outAdapter disconnected, no signal from host
Fast BlinkPower button pressed, shutdown process started
GreenFade in/outAdapter connected, VerseGrip active data stream mode
SolidAdapter connected, VerseGrip active data steam mode, USB power connected
WhiteFade in/outAdapter connected, VerseGrip idle/standby mode no data streaming
SolidAdapter connected, VerseGrip idle/standby mode no data streaming, USB power connected
OrangeFlash (interlaced)Battery low warning indication, notification is interlaced interlaced in current state
Double Flash (interlaced)Battery critically low warning indication, notification is interlaced in current state
TealFade in/outDevice firmware update mode
(Brighter)SolidUser buttons pressed, current indication color is solid and brighter
OffNo LightShutdown, power off
Charge indicator colourLight sequenceDescription
BlueSolidCharging
Fast BlinkCharging near complete
OffNo lightCharging complete

Legacy States and Light Indicator

The status lights below are legacy states and colours found in previous versions of the VerseGrip Stylus (Firmware 1.10 & below).

ColourLight sequenceDescription
RedSolidPending initialization of components
BlinkStandby mode. No data transmission
BlueSolidInitial automated sensor reference calibration (lay VerseGrip still on table)
GreenBlinkAdapter connected, VerseGrip active data steam mode
OffNo LightShutdown, power off
Charge indicator colourLight sequenceDescription
BlueSolidCharging
Fast BlinkCharging near complete
OffNo lightCharging complete

Sample code

Here’s a simple example on how to use the VerseGrip Stylus with 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);

}
}