Microtube Docs
Developers / Guides / Triggering feedback

Triggering feedback

Fire pressure and vibration on the right finger, at the right moment.

How feedback fires

Feedback comes from an event you raise — a touch, a grab, a release — or from a physics collider in your scene. Gate it behind one of those; never poll it every frame.

Most Unity scenes never call the API at all: drop HexRInteractableHaptics beside your interactable to feel it when grabbed, or SpecialHaptics on a trigger collider to feel it when touched. Use the calls below when you want explicit control.

Warning

Never send feedback in a tight loop. On Quest and PICO builds, commands are collected and sent at most every 100 ms, and anything collected while the glove is disconnected is dropped; on Windows each command is its own Bluetooth write. Send on events, not every frame — see Latency.

Release what you apply

The glove keeps applying the last pressure it was sent until it receives a release; nothing on the glove lets go by itself. From 0.9.4 the Unity package releases every channel when your app quits, pauses or disconnects the glove, but nothing can release it if the app crashes or the Bluetooth link drops, and the Python library releases nothing for you. If a glove is left pressing, switch the arm module off.

Pressure

A steady squeeze on one of six channels — five fingers and the palm. intensity is 0.1–1.0; speed is how fast it ramps there.

csharp
// The Pressure Controller on each hand. finger, on/off,
// intensity 0.1-1.0, ramp speed 0.1-1.0, bypass the hand-near check.
hand.CustomSingleHaptics(Haptics.Finger.Index, true, 0.8f, 1f, false);

// Let go. Release with true as the last argument, or call RemoveIndexHaptics():
// a release that goes through the hand-near check is dropped if the hand has left.
hand.CustomSingleHaptics(Haptics.Finger.Index, false, 0f, 1f, true);

Leave the last argument false when you apply, so a squeeze only fires when a hand is actually at the object. Set it to true when you release, so the release always gets through.

python
F = Haptics.Finger

# finger, on/off, intensity 0.1-1.0, ramp speed 0.1-1.0
data = haptics.hexr_pressure(F.Index, True, 0.8, 1.0)
await client.write_gatt_char(CHAR_UUID, data)

# Let go.
data = haptics.hexr_pressure(F.Index, False, 0.0, 1.0)
await client.write_gatt_char(CHAR_UUID, data)

Every finger, its own value

Pass an array per argument instead of a single value and each channel gets its own setting, concatenated into one BLE write. The channel order is fixed — Thumb, Index, Middle, Ring, Pinky, Palm — and every array must be the same length as the finger list.

csharp
// handler is a glove's HaptGloveHandler: HexRManager.Instance.rightHand, or leftHand.
// One entry per channel. The order is fixed:
// Thumb, Index, Middle, Ring, Pinky, Palm.
Haptics.Finger[] fingers = { Haptics.Finger.Thumb,  Haptics.Finger.Index,
                           Haptics.Finger.Middle, Haptics.Finger.Ring,
                           Haptics.Finger.Pinky,  Haptics.Finger.Palm };

bool[]  states      = { true, true, true, true, true, true };
float[] intensities = { 0.3f, 0.9f, 0.9f, 0.6f, 0.4f, 0.8f };
float[] speeds      = { 1f,   1f,   1f,   1f,   1f,   1f   };

handler.BTSend(handler.haptics.HEXRPressure(fingers, states, intensities, speeds));

// Vibration takes per-finger frequency as well as intensity. 0 Hz is not
// "off": it is clamped to 0.1 Hz. Leave a channel out with state false.
float[] freqs = { 3f, 12f, 12f, 6f, 3f, 3f };
handler.BTSend(handler.haptics.HEXRVibration(fingers, states, freqs, intensities));

// Release everything: the pair the HexR Tools "All OFF" button sends.
hand.RemoveAllHaptics();
hand.RemoveAllVibrations();

Same value on every channel instead? The Pressure Controller has shortcuts: TriggerAllHapticsIncrease(pressure), TriggerCustomHapticsIncrease(bool[], pressure, speed), TriggerAllVibrations(frequency) and TriggerCustomlVibrations(bool[], intensity, frequency) — note the spelling. Their bool[] picks which channels fire, in the same fixed order.

python
F = Haptics.Finger
fingers = [F.Thumb, F.Index, F.Middle, F.Ring, F.Pinky, F.Palm]

# One entry per channel, same order as the list above.
data = haptics.hexr_pressure_multiple(
    fingers,
    [True] * 6,
    [0.3, 0.9, 0.9, 0.6, 0.4, 0.8],   # intensity
    [1.0] * 6)                              # ramp speed
await client.write_gatt_char(CHAR_UUID, data)

# Vibration takes seven lists -- still 0.1-2 Hz only.
data = haptics.hexr_vibrations_multiple(
    fingers, [True] * 6,
    [1.5] * 6, [0.8] * 6, [0.5] * 6, [1.0] * 6, [0.0] * 6)
await client.write_gatt_char(CHAR_UUID, data)
Arrays are not length-checked

A list shorter than the finger array throws IndexOutOfRangeException mid-frame, after part of the payload is already built. Build all of them from the same source of truth.

Vibration

The same squeeze, switched on and off repeatedly. frequency sets how fast it switches.

Frequency changes what intensity means

Below 5 Hz, intensity sets how hard each pulse presses. At 5 Hz and above, pressure is pinned at maximum and intensity sets the duty cycle instead.

FrequencyFeels likeWhat intensity changes
0.1–1 HzSlow, separated pulsesPressure of each pulse
1–5 HzDistinct, countable pulsesPressure of each pulse
5–40 HzContinuous buzzDuty cycle

Out-of-range values are clamped, not rejected. Pass false as the state to stop a channel.

csharp
// handler is a glove's HaptGloveHandler: HexRManager.Instance.rightHand, or leftHand.
// hand is a Pressure Controller.
// finger, on/off, frequency 0.1-40 Hz, intensity 0.1-1.0
byte[] data = handler.haptics.HEXRVibration(Haptics.Finger.Index, true, 12f, 0.7f);
handler.BTSend(data);

// Or through the Pressure Controller -- note that intensity
// comes before frequency here, not after it.
hand.CustomSingleVibrations(Haptics.Finger.Palm, true, 0.7f, 12f, false);

hand.RemoveAllVibrations();

Below 2 Hz a longer overload adds the pulse shape and a pressure to hold once it stops — HEXRVibration(finger, state, frequency, intensity, peakRatio, speed, endIntensity). Ranges are in Feedback parameters.

python
F = Haptics.Finger

# finger, on/off, frequency, intensity, peak ratio,
# ramp speed, and the pressure to hold once it stops
data = haptics.hexr_vibrations(F.Index, True, 1.5, 0.8, 0.5, 1.0, 0.0)
await client.write_gatt_char(CHAR_UUID, data)
Python is capped at 2 Hz

The library exposes only the low-frequency form, so hexr_vibrations clamps to 0.1–2 Hz. For a buzz rather than pulsing, drive the glove from Unity. Ranges are in the Python reference.