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_OKmeans success. On failure, retrieve the per-thread error string viawuji_last_error().int32_t— metadata readers (wuji_dev_serial_number/wuji_dev_device_name) return the number of bytes written.uint8_t— status querywuji_dev_is_connected.void—wuji_shutdownand resource cleanup functions.
Resource cleanup functions
wuji_discovered_free— release the device list returned bywuji_scanwuji_dev_release— paired withwuji_connect, release the device handle after disconnectingwuji_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
framepointer 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_closefrom inside that subscription's own callback.closejoins 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 nextwuji_*call on the same thread. Copy the message immediately if you need to retain it. ENDandERRORare terminal frames — no further data will arrive after them, but you still need to callwuji_sub_closeto release the subscription handle.
Python equivalents
| Capability | C | Python |
|---|---|---|
| Init | wuji_init(NULL) | SdkManager.instance() |
| Scan | wuji_scan(&devs, &count) | manager.scan() |
| Connect | wuji_connect(&target, alias, &opts, &dev) | manager.connect(...) / manager.auto_connect(...) |
| Device subscribe | wuji_glove_subscribe_tactile(dev, cb, user, &sub) | glove.tactile().subscribe_with_callback(...) |
| Global subscribe | wuji_subscribe_tf(cb, user, &sub) | manager.tf().subscribe() |
| Disconnect | wuji_dev_disconnect + wuji_dev_release | device.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.