C API Reference

Wuji SDK ships a C API (libwuji_sdk_c.so + wuji_sdk.h) with the same semantics as the Python interface — device discovery, connection, typed per-device subscription callbacks, and global coordinate-transform streams. For installation alongside Python, see Introduction.

The complete typed callback signatures and struct definitions live in include/wuji_sdk.h of the extracted tarball. The CMake example and build instructions are in the examples/c/ directory of the wuji-sdk repo.

Initialization and conventions

Initialization

Call once at process start and release at shutdown:

#include "wuji_sdk.h"

if (wuji_init(NULL) != WUJI_STATUS_OK) {
    fprintf(stderr, "init failed: %s\n", wuji_last_error());
    return 1;
}

// ... your code

wuji_shutdown();

Return value taxonomy

  • WujiStatus — most operations (wuji_init / wuji_scan / wuji_connect / opening subscriptions, etc.). WUJI_STATUS_OK means success. On failure, retrieve the per-thread error string via wuji_last_error().
  • int32_t — metadata readers (wuji_dev_serial_number / wuji_dev_device_name) return the number of bytes written.
  • uint8_t — status query wuji_dev_is_connected.
  • voidwuji_shutdown and resource cleanup functions.

Resource cleanup functions

  • wuji_discovered_free — release the device list returned by wuji_scan
  • wuji_dev_release — paired with wuji_connect, release the device handle after disconnecting
  • wuji_sub_close — close the subscription handle

Device discovery and connection

Scan for Wuji devices on the local network:

WujiDiscovered* devs = NULL;
size_t count = 0;
wuji_scan(&devs, &count);
for (size_t i = 0; i < count; i++) {
    printf("[%zu] %s @ %s\n", i, devs[i].serial_number, devs[i].address);
}
wuji_discovered_free(devs, count);

Connect by SN or address (target.kind is WUJI_CONNECT_TARGET_KIND_SN or _ADDR. The local alias is passed as the second argument to wuji_connect):

WujiConnectTarget target = {
    .kind = WUJI_CONNECT_TARGET_KIND_SN,
    .value = "WG1KA00260209001",
};
WujiConnectOptions opts = { .timeout_ms = 5000, .retry_count = 0 };

WujiDevice* dev = NULL;
if (wuji_connect(&target, "glove", &opts, &dev) != WUJI_STATUS_OK) {
    fprintf(stderr, "connect failed: %s\n", wuji_last_error());
    return 1;
}

char sn_buf[64];
wuji_dev_serial_number(dev, sn_buf, sizeof(sn_buf));
printf("connected: sn=%s\n", sn_buf);

// ... your code

wuji_dev_disconnect(dev);
wuji_dev_release(dev);

device_name has the same semantics as in Python: a local alias that does not select a device or determine the returned handle type. It must be non-empty and must not contain / or .. See Device Connection.

Subscribe to per-device data streams

Each typed data stream has a dedicated wrapper wuji_<device>_subscribe_<name>. The callback signature is bound to the stream schema. Wuji Glove tactile, for example:

void on_tactile(WujiFrameKind kind, const WujiTactileFrame* frame, void* user) {
    if (kind != WUJI_FRAME_KIND_OK) return;
    // frame->data, frame->header.seq, frame->header.timestamp_us
}

WujiSub* sub = NULL;
wuji_glove_subscribe_tactile(dev, on_tactile, /*user=*/NULL, &sub);

// ... callbacks fire from a background thread

wuji_sub_close(sub);

Wuji Glove typed subscribe entry points: tactile, tactile_zones, tactile_binary, tactile_residual, tactile_point_cloud, emf_poses, hand_joint_angles, hand_skeleton, tip_poses, imu_palm, and the per-finger imu_<finger> / imu_data_<finger> variants. Wuji Hand 2 currently exposes only wuji_hand_2_subscribe_joint_state. More typed entry points are planned.

Subscribe to global resources

The cross-device aggregated coordinate transforms exposed in Python as manager.tf() / manager.tf_static() map to device-less C wrappers:

void on_tf(WujiFrameKind kind, const WujiFrameTransforms* frames, void* user) {
    if (kind != WUJI_FRAME_KIND_OK) return;
    for (size_t i = 0; i < frames->transforms_len; i++) {
        const WujiFrameTransform* t = &frames->transforms[i];
        printf("%s -> %s\n", t->parent_frame_id, t->child_frame_id);
    }
}

WujiSub* sub = NULL;
wuji_subscribe_tf(on_tf, NULL, &sub);
// Same shape: wuji_subscribe_tf_static(...)

wuji_sub_close(sub);

Usage constraints

C SDK users must observe the following constraints to avoid use-after-free, deadlocks, and reading stale error strings.

  • A typed callback's frame pointer is valid only for the duration of the callback. Heap fields are released after the callback returns. Copy whatever you need to retain inside the callback. Do not store the pointer.
  • Never call wuji_sub_close from inside that subscription's own callback. close joins the worker thread and will deadlock if called from the callback itself. Close from a different thread.
  • The return value of wuji_last_error() is only valid until the next wuji_* call on the same thread. Copy the message immediately if you need to retain it.
  • END and ERROR are terminal frames — no further data will arrive after them, but you still need to call wuji_sub_close to release the subscription handle.

Python equivalents

CapabilityCPython
Initwuji_init(NULL)SdkManager.instance()
Scanwuji_scan(&devs, &count)manager.scan()
Connectwuji_connect(&target, alias, &opts, &dev)manager.connect(...) / manager.auto_connect(...)
Device subscribewuji_glove_subscribe_tactile(dev, cb, user, &sub)glove.tactile().subscribe_with_callback(...)
Global subscribewuji_subscribe_tf(cb, user, &sub)manager.tf().subscribe()
Disconnectwuji_dev_disconnect + wuji_dev_releasedevice.disconnect()

CMake project example

A complete runnable C project (with CMakeLists.txt plus a subscription callback demo) lives in the examples/c/ directory of the wuji-sdk repo. See that directory's README for build instructions. Minimal link:

cc my_app.c -I "${SDK}/include" -L "${SDK}/lib" -lwuji_sdk_c -o my_app
LD_LIBRARY_PATH="${SDK}/lib" ./my_app

${SDK} is the extracted tarball root. The CMake project uses -DWUJI_SDK_INCLUDE_DIR and -DWUJI_SDK_LIB to point at the same paths.