This document shows how to trigger haptic effects on TITAN Core from common external inputs using the Vector Haptics library. The included examples cover a push button, a rotary encoder, and a capacitive touch input.
These examples provide a simple starting point for building interactive haptic prototypes with TITAN Core. By combining external inputs with the Vector Haptics library, you can quickly create button-driven, touch-driven, or user-adjustable haptic behaviors for your own projects.
The same structure can be extended to other sensors, switches, and control interfaces depending on your application.
Install Arduino IDE and open it. (Recommended version: Arduino IDE v1.8.x)
Install the ESP32 board package by following the official Espressif setup process for Arduino.
Once installed:
Copy all Vector Haptics library folders into your local Arduino libraries folder:
The required library folders are:
Then restart the Arduino IDE so the libraries are detected properly.
Before moving on, make sure:
PlatformIO can also be used to build and upload TITAN Core example projects.
Download and install Visual Studio Code.
In Visual Studio Code, install the PlatformIO IDE extension.
Create a new PlatformIO project with:
Copy the required Vector Haptics library folders into the project's lib folder.
Example structure:
your-project/ |-- lib/ |-- src/ | +-- main.cpp +-- platformio.ini
Use:
[env:esp32dev] platform = espressif32 board = esp32dev framework = arduino monitor_speed = 115200 upload_speed = 921600
Place your example code in src/main.cpp.
If using PlatformIO, comment out this line where shown in the examples:
Connect TITAN Core by USB, then build and upload the project through PlatformIO.
Make sure:
PlatformIO note: If using PlatformIO, comment out #include "ESP32Profiles.h" where indicated.
All examples in this workshop follow the same basic structure:
#include "VectorHaptics.h" #include "ESP32Profiles.h" // If using PlatformIO comment this line out
These include the Vector Haptics API and the ESP32 hardware profile used by TITAN Core.
VectorHaptics vh; ESP32Profile board; VHChannels channels;
vh is the main Vector Haptics controllerboard defines the ESP32 hardware profilechannels assigns the haptic output channel and output pinchannels.add(1, {25}, {"main"});
vh.init(&board, {&channels});
This creates one haptic output channel named "main" on GPIO 25.
If your hardware uses a different output pin or channel configuration, this is the section to modify.
Channel IO25 → Left Channel
Channel IO26 → Right Channel
This example plays a short haptic pulse when a push button is pressed.
#include "VectorHaptics.h"
#include "ESP32Profiles.h" // If using PlatformIO comment this line out
VectorHaptics vh;
ESP32Profile board;
VHChannels channels;
const int BUTTON_PIN = 7;
long lastDebounceTime = 0;
const long debounceDelay = 50;
void setup() {
Serial.begin(115200);
pinMode(BUTTON_PIN, INPUT_PULLUP);
channels.add(1, {25}, {"main"});
vh.init(&board, {&channels});
}
void loop() {
if (millis() - lastDebounceTime > debounceDelay) {
if (digitalRead(BUTTON_PIN) == LOW) {
vh.play(PULSE(20, 1.0, 0.5)); // change this to any effect
}
lastDebounceTime = millis();
}
}
The button pin is configured using INPUT_PULLUP, so no external pull-up resistor is needed. The code checks the button state every 50 ms to reduce repeated triggering from switch bounce. When the button is pressed, vh.play() sends a pulse effect to the haptic output.
This example uses a simple pulse effect as a starting point. You can replace it with any other supported effect once the basic trigger is working.
This example triggers a pulse when the user touches a capacitive touch input.
#include "VectorHaptics.h"
#include "ESP32Profiles.h" // If using PlatformIO comment this line out
VectorHaptics vh;
ESP32Profile board;
VHChannels channels;
long lastDebounceTime = 0;
const int debounceDelay = 100;
const int TOUCH_PIN = 4;
const int threshold = 30;
void setup()
{
Serial.begin(115200);
channels.add(1, {25}, {"main"});
vh.init(&board, {&channels});
}
void loop()
{
if (millis() - lastDebounceTime > debounceDelay) {
if (touchRead(TOUCH_PIN) < threshold) {
vh.play(PULSE(20, 1.0, 0.5));
lastDebounceTime = millis();
}
}
}
touchRead(TOUCH_PIN) returns a capacitive measurement value. When a finger touches the pad, that value changes. If the reading falls below the selected threshold, the code triggers a pulse effect.
The debounce timer is used here to prevent repeated rapid triggering while the pad is still being touched.
This threshold may need to be adjusted depending on:
A good way to tune this is to print touchRead() values to the Serial Monitor for both touched and untouched states, then set a threshold with reasonable margin between them.
This example uses a rotary encoder to change vibration intensity. Pressing the encoder button plays the effect using the current intensity value.
#include "VectorHaptics.h"
#include "ESP32Profiles.h" // If using PlatformIO comment this line out
VectorHaptics vh;
ESP32Profile board;
VHChannels channels;
const int PIN_A = 7;
const int PIN_B = 8;
const int BUTTON_PIN = 5;
int intensity = 10;
int lastValueA;
int valueA;
long lastDebounceTime = 0;
const long debounceDelay = 50;
void setup()
{
Serial.begin(115200);
pinMode(PIN_A, INPUT_PULLUP);
pinMode(PIN_B, INPUT_PULLUP);
pinMode(BUTTON_PIN, INPUT_PULLUP);
lastValueA = digitalRead(PIN_A);
channels.add(1, {25}, {"main"});
vh.init(&board, {&channels});
}
void loop()
{
valueA = digitalRead(PIN_A);
if (valueA != lastValueA)
{
if (digitalRead(PIN_B) != valueA)
{
if (intensity < 10)
intensity++;
Serial.println(intensity);
}
else
{
if (intensity > 0)
intensity--;
Serial.println(intensity);
}
vh.play(VIBRATE(20, float(intensity) / 10, 60, 0.5));
}
lastValueA = valueA;
if (millis() - lastDebounceTime > debounceDelay) {
if (digitalRead(BUTTON_PIN) == LOW) {
vh.play(VIBRATE(20, float(intensity) / 10, 60, 0.5));
}
lastDebounceTime = millis();
}
}
The rotary encoder updates the intensity variable between 0 and 10. Turning in one direction increases intensity, while turning the other direction decreases it.
The current intensity value is also printed to the Serial Monitor, which is useful during testing and tuning.
In this example, the encoder value is scaled into a range from 0.0 to 1.0.
For example:
This makes the encoder a simple live haptic strength control.
This is a simple polling-based encoder example. For more precise or faster encoder reading, an interrupt-based approach or dedicated encoder library may be preferred.
In all examples, the effect is defined by the vh.play(...) call.
Examples:
vh.play(PULSE(20, 1.0, 0.5)); vh.play(VIBRATE(20, 0.8, 60, 0.5));
Once the trigger logic is working, you can begin changing the effect parameters to suit your application.
Common parameters to experiment with include:
The safest way to tune is to change one parameter at a time and test the result.
Check the following: