Play a haptic touch
C11 · Teslasuit v. 4.5+ · SDK 2.5.1+
A complete, runnable walkthrough: initialize the API, connect to a device, access the Haptic subsystem, and play a haptic touch on the right upper arm.
Code for haptic touch#
⚠️ Safety. Make sure the device is connected and calibrated before running. Start with conservative touch settings (low amplitude, short duration), check electrode placement, and stop if anything feels wrong.
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <ts_api/ts_types.h>
#ifdef _WIN32
#include <Windows.h>
#elif defined(__APPLE__) || defined(__linux__)
#include <dlfcn.h>
#include <unistd.h>
#endif
// Change these to play with the touch: which bone gets it, and how it feels.
static const TsBoneIndex TARGET_BONE_INDEX = TsBoneIndex_RightUpperArm;
static const uint64_t TOUCH_PERIOD_US = 21600;
static const uint64_t TOUCH_AMPLITUDE_PERCENT = 40;
static const uint64_t TOUCH_PULSE_WIDTH_US = 120;
static const uint64_t TOUCH_DURATION_MS = 3000;
#ifdef __cplusplus
extern "C" {
#endif
#define API_FUNCTION_NAME(name) name##_func_t
#define DECLARE_API_FUNCTION(ret, name, args) \
typedef ret(*API_FUNCTION_NAME(name)) args; \
static API_FUNCTION_NAME(name) name
#define LOAD_API_FUNCTION(lib, name) \
do { \
name = (API_FUNCTION_NAME(name))api_library_get_function(lib, #name); \
if (name == NULL) \
return false; \
} while (0)
typedef void *SharedLibrary;
static void *api_library_get_function(SharedLibrary lib,
const char *function_name) {
#ifdef _WIN32
return (void *)GetProcAddress((HMODULE)lib, function_name);
#elif defined(__APPLE__) || defined(__linux__)
return dlsym(lib, function_name);
#endif
}
static void sleep_ms(int ms) {
#ifdef _WIN32
Sleep(ms);
#elif defined(__APPLE__) || defined(__linux__)
usleep(ms * 1000);
#endif
}
// device/session
DECLARE_API_FUNCTION(TsStatusCode, ts_initialize, ());
DECLARE_API_FUNCTION(void, ts_uninitialize, ());
DECLARE_API_FUNCTION(TsStatusCode, ts_get_device_list,
(TsDevice *, uint32_t *));
DECLARE_API_FUNCTION(TsDeviceHandle *, ts_device_open, (TsDevice *));
DECLARE_API_FUNCTION(const char *, ts_device_get_name, (TsDeviceHandle *));
DECLARE_API_FUNCTION(void, ts_device_close, (TsDeviceHandle *));
DECLARE_API_FUNCTION(const char *, ts_get_status_code_message, (TsStatusCode));
// mapping/layout
DECLARE_API_FUNCTION(TsStatusCode, ts_mapping2d_get_by_device,
(TsDeviceHandle *, TsMapping2d *));
DECLARE_API_FUNCTION(TsStatusCode, ts_mapping2d_get_number_of_layouts,
(const TsMapping2d, uint64_t *));
DECLARE_API_FUNCTION(TsStatusCode, ts_mapping2d_get_layouts,
(const TsMapping2d, TsLayout2d *, uint64_t));
DECLARE_API_FUNCTION(TsStatusCode, ts_mapping2d_layout_get_type,
(const TsLayout2d layout, TsLayout2dType *layout_type));
DECLARE_API_FUNCTION(TsStatusCode, ts_mapping2d_layout_get_element_type,
(const TsLayout2d layout,
TsLayout2dElementType *layout_element_type));
DECLARE_API_FUNCTION(TsStatusCode, ts_mapping2d_layout_get_number_of_bones,
(const TsLayout2d, uint64_t *));
DECLARE_API_FUNCTION(TsStatusCode, ts_mapping2d_layout_get_bones,
(const TsLayout2d, TsMapping2dBone *, uint64_t));
DECLARE_API_FUNCTION(TsStatusCode, ts_mapping2d_bone_get_index,
(const TsMapping2dBone, TsBoneIndex *));
DECLARE_API_FUNCTION(TsStatusCode, ts_mapping2d_bone_get_number_of_contents,
(const TsMapping2dBone, uint64_t *));
DECLARE_API_FUNCTION(TsStatusCode, ts_mapping2d_bone_get_contents,
(const TsMapping2dBone, TsMapping2dBoneContent *,
uint64_t));
// haptic
DECLARE_API_FUNCTION(TsStatusCode, ts_haptic_create_touch,
(TsDeviceHandle *, TsHapticParam *, uint64_t,
const TsMapping2dBoneContent *, uint64_t, uint64_t,
uint64_t *));
DECLARE_API_FUNCTION(TsStatusCode, ts_haptic_play_playable,
(TsDeviceHandle *, uint64_t));
static bool load_api_functions(SharedLibrary lib) {
// device/session
LOAD_API_FUNCTION(lib, ts_initialize);
LOAD_API_FUNCTION(lib, ts_uninitialize);
LOAD_API_FUNCTION(lib, ts_get_device_list);
LOAD_API_FUNCTION(lib, ts_device_open);
LOAD_API_FUNCTION(lib, ts_device_get_name);
LOAD_API_FUNCTION(lib, ts_device_close);
LOAD_API_FUNCTION(lib, ts_get_status_code_message);
// mapping/layout
LOAD_API_FUNCTION(lib, ts_mapping2d_get_by_device);
LOAD_API_FUNCTION(lib, ts_mapping2d_get_number_of_layouts);
LOAD_API_FUNCTION(lib, ts_mapping2d_get_layouts);
LOAD_API_FUNCTION(lib, ts_mapping2d_layout_get_type);
LOAD_API_FUNCTION(lib, ts_mapping2d_layout_get_element_type);
LOAD_API_FUNCTION(lib, ts_mapping2d_layout_get_number_of_bones);
LOAD_API_FUNCTION(lib, ts_mapping2d_layout_get_bones);
LOAD_API_FUNCTION(lib, ts_mapping2d_bone_get_index);
LOAD_API_FUNCTION(lib, ts_mapping2d_bone_get_number_of_contents);
LOAD_API_FUNCTION(lib, ts_mapping2d_bone_get_contents);
// haptic
LOAD_API_FUNCTION(lib, ts_haptic_create_touch);
LOAD_API_FUNCTION(lib, ts_haptic_play_playable);
return true;
}
static bool check_code(TsStatusCode code) {
if (code != 0) {
printf("Error: %s\n", ts_get_status_code_message(code));
return false;
}
return true;
}
// Finds the Electric+Channel layout, for a given device.
static bool find_electric_channel_layout(TsDeviceHandle *dev,
TsLayout2d *out_layout) {
TsMapping2d mapping2d = NULL;
check_code(ts_mapping2d_get_by_device(dev, &mapping2d));
uint64_t number_of_layouts = 0;
check_code(ts_mapping2d_get_number_of_layouts(mapping2d, &number_of_layouts));
TsLayout2d *layouts = malloc(number_of_layouts * sizeof(TsLayout2d));
if (layouts == NULL) {
printf("Error: allocation failed (layouts)\n");
return false;
}
check_code(ts_mapping2d_get_layouts(mapping2d, layouts, number_of_layouts));
// Electric=1, Channel=2 values from ts_types.h doc comments
const TsLayout2dType electric_type = 1;
const TsLayout2dElementType channel_element_type = 2;
bool found = false;
for (uint64_t i = 0; i < number_of_layouts && !found; i++) {
TsLayout2dType layout_type = 0;
TsLayout2dElementType element_type = 0;
check_code(ts_mapping2d_layout_get_type(layouts[i], &layout_type));
check_code(ts_mapping2d_layout_get_element_type(layouts[i], &element_type));
if (layout_type == electric_type && element_type == channel_element_type) {
*out_layout = layouts[i];
found = true;
}
}
free(layouts);
if (!found) {
printf("Error: electric/channel layout not found\n");
}
return found;
}
static bool find_target_bone(TsLayout2d layout, TsMapping2dBone *out_bone) {
uint64_t number_of_bones = 0;
check_code(ts_mapping2d_layout_get_number_of_bones(layout, &number_of_bones));
TsMapping2dBone *bones = malloc(number_of_bones * sizeof(TsMapping2dBone));
if (bones == NULL) {
printf("Error: allocation failed (bones)\n");
return false;
}
check_code(ts_mapping2d_layout_get_bones(layout, bones, number_of_bones));
bool found = false;
// Some bones are listed once per TsBone2dSide (Front/Back); we select only
// the side with nonzero contents, since that's the one with electrodes wired.
for (uint64_t i = 0; i < number_of_bones && !found; i++) {
TsBoneIndex bone_index = TsBoneIndex_BonesCount;
uint64_t content_count = 0;
check_code(ts_mapping2d_bone_get_index(bones[i], &bone_index));
check_code(
ts_mapping2d_bone_get_number_of_contents(bones[i], &content_count));
if (bone_index != TARGET_BONE_INDEX || content_count == 0) {
continue;
}
*out_bone = bones[i];
found = true;
}
free(bones);
if (!found) {
printf("Error: target bone channel not found\n");
}
return found;
}
// Caller must free() the returned array.
static bool get_bone_channels(TsMapping2dBone bone,
TsMapping2dBoneContent **out_contents,
uint64_t *out_count) {
uint64_t content_count = 0;
check_code(ts_mapping2d_bone_get_number_of_contents(bone, &content_count));
TsMapping2dBoneContent *contents =
malloc(content_count * sizeof(TsMapping2dBoneContent));
if (contents == NULL) {
printf("Error: allocation failed (bone_contents)\n");
return false;
}
check_code(ts_mapping2d_bone_get_contents(bone, contents, content_count));
*out_contents = contents;
*out_count = content_count;
return true;
}
// Finds the target bone's channel on the connected device and plays a touch on
// it.
static void run(TsDeviceHandle *dev) {
TsLayout2d layout;
if (!find_electric_channel_layout(dev, &layout)) {
return;
}
TsMapping2dBone bone;
if (!find_target_bone(layout, &bone)) {
return;
}
TsMapping2dBoneContent *bone_contents = NULL;
uint64_t number_of_bone_contents = 0;
if (!get_bone_channels(bone, &bone_contents, &number_of_bone_contents)) {
return;
}
TsHapticParam haptic_params[] = {{1, TOUCH_PERIOD_US},
{2, TOUCH_AMPLITUDE_PERCENT},
{3, TOUCH_PULSE_WIDTH_US}};
uint64_t playable_id = 0;
if (check_code(ts_haptic_create_touch(
dev, haptic_params, sizeof(haptic_params) / sizeof(TsHapticParam),
bone_contents, number_of_bone_contents, TOUCH_DURATION_MS,
&playable_id))) {
check_code(ts_haptic_play_playable(dev, playable_id));
sleep_ms((int)TOUCH_DURATION_MS);
}
free(bone_contents);
}
#ifdef __cplusplus
}
#endif
int main() {
const char *api_path = getenv("TESLASUIT_API_LIB_PATH");
if (api_path == NULL) {
printf("Error: environment variable TESLASUIT_API_LIB_PATH not set\n");
return 0;
}
#ifdef _WIN32
SharedLibrary lib = (SharedLibrary)LoadLibraryEx(
api_path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
#elif defined(__APPLE__) || defined(__linux__)
SharedLibrary lib = (SharedLibrary)dlopen(api_path, RTLD_NOW | RTLD_LOCAL);
#endif
if (lib == NULL) {
printf("Error: library was not found\n");
return 0;
}
if (!load_api_functions(lib)) {
printf("Error: failed to load library functions\n");
goto unload;
}
if (!check_code(ts_initialize())) {
printf("Error: failed to initialize API\n");
goto unload;
}
TsDevice device;
uint32_t device_count = 0;
while (device_count == 0) {
printf("Waiting for a device...\n");
device_count = 1;
ts_get_device_list(&device, &device_count);
sleep_ms(100);
}
TsDeviceHandle *dev = ts_device_open(&device);
if (dev == NULL) {
printf("Error: failed to get device\n");
goto deinitialize;
}
printf("Using device: %s\n", ts_device_get_name(dev));
run(dev);
ts_device_close(dev);
deinitialize:
ts_uninitialize();
unload:
#ifdef _WIN32
FreeLibrary((HMODULE)lib);
#elif defined(__APPLE__) || defined(__linux__)
dlclose(lib);
#endif
return 0;
}Expected output#
Waiting for a device...
Using device: Teslasuit XR-5What to read next#
Explore these references and guides to build on this example and get the most out of Teslasuit haptic and EMS features:
Essential API references#
- Haptic API Reference: Full reference for
ts_haptic_create_touchandts_haptic_play_playableused in this example. - Mapping API: How to resolve the layout and bones used to target a touch.
- Common Types — Haptic: Reference for the haptic parameter and playable types.
Practical guides & examples#
- Examples Overview: Browse the full set of runnable examples.
Deeper dives & broader concepts#
- Haptics & EMS Main Concept: The conceptual background behind haptic feedback and EMS.
- Body & Channel Map: A Python-first tool to visualize which suit channels map to which areas of the body — conceptually useful here too.
- Teslasuit Main Concepts: Fundamental articles covering Teslasuit's core technology and how its subsystems interact.
