API Reference
Complete C API reference for the Haptica Nerv SDK. This page contains the full header file documentation for integrating Haptica tactile sensors into your application.
Core API (libhapnrv.h)
The main interface for Haptica Nerv - a high-performance, portable, and extensible middleware for Haptica Skin tactile sensor arrays.
/*
* Haptica Nerv Core API (V1)
* ===========================
* "A NIC in lieu of a haptic nerve."
*
* This header defines the public interface for Haptica Nerv, a high-performance,
* portable, and extensible middleware for Haptica Skin tactile sensor arrays.
*
* Design Philosophy:
* 1. Portable Edge: Runs on Workstation or Embedded. Cross platform (MacOS, Windows, Linux, STM32, Raspi, iOS)
* 2. Static Memory: Zero per-frame mallocs. Fixed-size "Rich Blobs".
* 3. Time Authority: Driver assigns Capture Time; Pipeline preserves it.
* 4. Extensible: Features (Calibration, Physics) are pluggable Strategies.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdarg.h> // For va_list
#ifdef __cplusplus
extern "C" {
#endif
// --- Constants, Terminology & Thread-Safety ----------------------------------
//
// Threading contract overview:
// * Config APIs (`hap_nrv_nic_config_*`) are single-threaded; build config on one thread.
// * `hap_nrv_nic_lock_latest` / `hap_nrv_nic_unlock_latest` are safe from multiple consumer threads.
// * `hap_nrv_nic_next_frame` and `hap_nrv_nic_get_composite` expose a single-consumer iterator.
// Use them from one thread (or guard externally) and release each frame via
// `hap_nrv_nic_release_frame`.
// * All pointer lifetimes are explicitly documented per function below.
// --- Constants & Conventions -------------------------------------------------
#define HAP_NRV_API_VERSION 1 // Bumps when ABI/structs change
#define HAP_NRV_MAX_DRIVERS 16 // Max concurrent sensors
#define HAP_NRV_MAX_BLOB_SIZE (1024 * 1024) // Hard ceiling per frame; tune per driver to avoid huge rings
#define HAP_NRV_NAME_MAX 64 // Max string length for names
#define HAP_NRV_MODEL_ID_UNKNOWN 0u // Unknown/unspecified model code
#define HAP_NRV_CAL_REPO_DEFAULT "./calibration" // Repo-local default asset root (override via HAPTICA_CAL_REPO)
#define HAP_NRV_STAGE_CALIBRATION "stage.calibration"
#define HAP_NRV_STAGE_COMPENSATION "stage.compensation"
#define HAP_NRV_CAL_HASH_BYTES 32 // SHA-256 digest
typedef struct hap_nrv_uuid {
uint8_t bytes[16];
} hap_nrv_uuid_t;
typedef uint32_t hap_nrv_model_id_t; // Model codes minted by hardware release process
typedef struct hap_nrv_calibration_digest {
uint8_t bytes[HAP_NRV_CAL_HASH_BYTES];
} hap_nrv_calibration_digest_t;
static inline hap_nrv_uuid_t hap_nrv_uuid_nil(void) {
hap_nrv_uuid_t u = {{0}};
return u;
}
typedef struct hap_nrv_version {
uint16_t major;
uint16_t minor;
uint16_t patch;
uint16_t reserved;
} hap_nrv_version_t;
// Query the runtime library's API version (mirrors HAP_NRV_API_VERSION compiled into this header).
uint16_t hap_nrv_runtime_api_version(void);
// Standard Property Names (Keys for set_property)
// Note: Axon analog behaviors (gain, rate, trigger) are immutable per hardware release.
#define HAP_NRV_PROP_LED "led_color" // Integer (RGB)
#define HAP_NRV_PROP_RESET "reset" // Bool (Write true to reset)
// Error Codes
typedef enum hap_nrv_error {
HAP_NRV_OK = 0,
HAP_NRV_ERR_INVALID_ARGS = -1, // Null pointer or bad value
HAP_NRV_ERR_NOMEM = -2, // Static allocation exhausted
HAP_NRV_ERR_DRIVER_LOAD = -3, // Failed to init hardware driver
HAP_NRV_ERR_TIMEOUT = -4, // Wait/Poll timed out
HAP_NRV_ERR_BAD_STATE = -5, // NIC not started or already stopped
HAP_NRV_ERR_PROP_NOT_FOUND = -6, // Property name unknown to driver
HAP_NRV_ERR_PROP_READ_ONLY = -7, // Tried to set a const property
HAP_NRV_ERR_TYPE_MISMATCH = -8, // Passed float to int property
HAP_NRV_ERR_STRATEGY_UNKNOWN = -9, // Calibration strategy not registered
HAP_NRV_ERR_CALIB_INVALID = -10, // Blob size mismatch for strategy
} hap_nrv_error_t;
// Log Levels
typedef enum hap_nrv_log_level {
HAP_NRV_LOG_DEBUG = 0,
HAP_NRV_LOG_INFO,
HAP_NRV_LOG_WARN,
HAP_NRV_LOG_ERROR,
HAP_NRV_LOG_NONE
} hap_nrv_log_level_t;
// Property Types (Tagged Union Support)
typedef enum hap_nrv_type {
HAP_NRV_TYPE_INT = 0, // int32_t
HAP_NRV_TYPE_FLOAT, // float
HAP_NRV_TYPE_BOOL, // bool
HAP_NRV_TYPE_STRING, // const char*
HAP_NRV_TYPE_ENUM, // int32_t (index) or string label
} hap_nrv_type_t;
// Type-Safe Value Container
// Used for get/set property calls to avoid void* casting errors.
typedef struct hap_nrv_value {
hap_nrv_type_t type;
union {
int32_t i32;
float f32;
bool b;
const char* str; // Pointer is borrowed, valid only for call duration
};
} hap_nrv_value_t;
// Helper macros for constructing values
#define HAP_NRV_VAL_INT(x) ((hap_nrv_value_t){HAP_NRV_TYPE_INT, {.i32 = (x)}})
#define HAP_NRV_VAL_FLOAT(x) ((hap_nrv_value_t){HAP_NRV_TYPE_FLOAT, {.f32 = (x)}})
#define HAP_NRV_VAL_BOOL(x) ((hap_nrv_value_t){HAP_NRV_TYPE_BOOL, {.b = (x)}})
#define HAP_NRV_VAL_STR(x) ((hap_nrv_value_t){HAP_NRV_TYPE_STRING, {.str = (x)}})
// Frame Encoding (for Layout Description)
typedef enum hap_nrv_encoding {
HAP_NRV_ENC_U8 = 0,
HAP_NRV_ENC_U16,
HAP_NRV_ENC_F32,
} hap_nrv_encoding_t;
// Driver Operational State
typedef enum hap_nrv_driver_state {
HAP_NRV_STATE_UNKNOWN = 0,
HAP_NRV_STATE_STOPPED, // Initialized but not started
HAP_NRV_STATE_SEARCHING, // Scanning for hardware (Auto-discovery)
HAP_NRV_STATE_CONNECTING, // Hardware found, handshaking
HAP_NRV_STATE_RUNNING, // Active and streaming
HAP_NRV_STATE_ERROR, // IO Error or Link Loss (Will retry)
} hap_nrv_driver_state_t;
// Opaque Handles
typedef struct hap_nrv_nic hap_nrv_nic_t;
typedef struct hap_nrv_nic_config hap_nrv_nic_config_t;
typedef enum hap_nrv_tick_source {
HAP_NRV_TICK_SOURCE_HARDWARE = 0,
HAP_NRV_TICK_SOURCE_SOFTWARE = 1,
} hap_nrv_tick_source_t;
typedef enum hap_nrv_clock_origin {
HAP_NRV_CLOCK_ORIGIN_HOST = 0,
HAP_NRV_CLOCK_ORIGIN_REMOTE = 1,
} hap_nrv_clock_origin_t;
typedef struct hap_nrv_clock_caps {
hap_nrv_tick_source_t source;
uint32_t tick_hz; // 0 if software
bool supports_sync_trigger;
hap_nrv_clock_origin_t origin;
uint8_t reserved[2];
} hap_nrv_clock_caps_t;
// --- Data Structures (The Blob) ----------------------------------------------
#if defined(_MSC_VER)
#pragma pack(push, 1)
#define HAP_NRV_PACKED
#elif defined(__clang__) || defined(__GNUC__)
#define HAP_NRV_PACKED __attribute__((__packed__))
#else
#pragma pack(push, 1)
#define HAP_NRV_PACKED
#endif
#define HAP_NRV_HEADER_SIZE 160
// HAP_NRV_HEADER wire layout (little-endian, 160 bytes)
// Offset Size Field Type
// 0x00 4 magic char[4] ('N','E','R','V')
// 0x04 2 api_version uint16_t
// 0x06 2 layout_version uint16_t
// 0x08 4 seq uint32_t
// 0x0C 8 ts_capture uint64_t (host monotonic ns)
// 0x14 8 ts_hardware uint64_t (raw Axon ticks)
// 0x1C 2 driver_slot uint16_t
// 0x1E 4 flags uint32_t
// 0x22 4 blob_len uint32_t
// 0x26 16 axon_uuid uint8_t[16]
// 0x36 4 axon_model uint32_t
// 0x3A 4 skin_model uint32_t
// 0x3E 16 skin_uuid uint8_t[16]
// 0x4E 8 axon_fw hap_nrv_version_t
// 0x56 8 driver_version hap_nrv_version_t
// 0x5E 16 calibration_uuid uint8_t[16]
// 0x6E 8 calibration_version hap_nrv_version_t
// 0x76 16 compensation_uuid uint8_t[16]
// 0x86 8 compensation_version hap_nrv_version_t
// 0x8E 18 reserved uint8_t[18]
// The Fixed Frame Header (HAP_NRV_HEADER_SIZE bytes)
// Wire format is little-endian for every integer. This struct is packed to
// guarantee byte-for-byte fidelity with the on-wire representation.
typedef struct HAP_NRV_PACKED hap_nrv_header {
uint32_t magic; // 'N','E','R','V' → 0x4E455256
uint16_t api_version; // == HAP_NRV_API_VERSION (header/ABI version)
uint16_t layout_version; // Driver-defined payload schema version
uint32_t seq; // Hardware sequence # provided by driver (wraps naturally)
uint64_t ts_capture; // Host monotonic ns at capture (derived from ts_hardware)
uint64_t ts_hardware; // Raw Axon tick value captured at the edge
uint16_t driver_slot; // NIC slot index that produced this frame
uint32_t flags; // Bitmask (e.g. HAP_NRV_FLAG_SATURATED)
uint32_t blob_len; // Payload size in bytes immediately following header
hap_nrv_uuid_t axon_uuid; // Physical Axon electronics UUID
hap_nrv_model_id_t axon_model; // Model code of the Axon hardware
hap_nrv_model_id_t skin_model; // Model code of the tactile skin
hap_nrv_uuid_t skin_uuid; // Installed tactile skin UUID
hap_nrv_version_t axon_fw; // Axon firmware version
hap_nrv_version_t driver_version;// Driver plugin version
hap_nrv_uuid_t calibration_uuid; // Active calibration asset identifier
hap_nrv_version_t calibration_version; // Calibration schema/semantic version
hap_nrv_uuid_t compensation_uuid; // Active compensation asset identifier
hap_nrv_version_t compensation_version; // Compensation semantic version
uint8_t reserved[18]; // Reserved for future extensions (maintains 160-byte header)
} hap_nrv_header_t;
#if defined(_MSC_VER) || !(defined(__clang__) || defined(__GNUC__))
#pragma pack(pop)
#endif
#if defined(__cplusplus)
static_assert(sizeof(hap_nrv_header_t) == HAP_NRV_HEADER_SIZE, "hap_nrv_header_t must remain 160 bytes");
#else
typedef char hap_nrv_header_size_must_be_160[(sizeof(hap_nrv_header_t) == HAP_NRV_HEADER_SIZE) ? 1 : -1];
#endif
// Flags for hap_nrv_header_t.flags
#define HAP_NRV_FLAG_SATURATED (1u << 0) // Sensor is clipping/saturated
#define HAP_NRV_FLAG_STALE (1u << 1) // Data is older than staleness threshold
#define HAP_NRV_FLAG_SYNC_LOST (1u << 2) // Parser lost sync (CRC error recovered)
// The Rich Frame (Pointer Access)
// Represents a view into the Ring Buffer or Triple Buffer.
typedef struct hap_nrv_frame {
hap_nrv_header_t header;
uint8_t payload[]; // Variable length blob (Taxels + Metadata fields)
} hap_nrv_frame_t;
// Opaque event handle for poll/select integration.
// On POSIX this is a file descriptor. On Windows it is a HANDLE cast to uintptr_t.
typedef uintptr_t hap_nrv_event_handle_t;
#define HAP_NRV_INVALID_EVENT_HANDLE ((hap_nrv_event_handle_t)0)
// Calibration provenance states.
typedef enum hap_nrv_calibration_match {
HAP_NRV_CAL_MATCH_UNKNOWN = 0,
HAP_NRV_CAL_MATCH_EXACT, // Skin UUID + model + Axon match the asset metadata
HAP_NRV_CAL_MATCH_FOREIGN // Asset was applied despite mismatched metadata
} hap_nrv_calibration_match_t;
typedef struct hap_nrv_stage_state {
hap_nrv_uuid_t uuid;
hap_nrv_version_t version;
hap_nrv_calibration_digest_t hash;
char label[HAP_NRV_NAME_MAX];
bool bypassed;
uint8_t reserved[3];
} hap_nrv_stage_state_t;
typedef struct hap_nrv_stage_snapshot {
hap_nrv_stage_state_t calibration;
hap_nrv_stage_state_t compensation;
hap_nrv_calibration_match_t match;
} hap_nrv_stage_snapshot_t;
// Descriptor describing a calibration blob sourced from persistent storage.
// Callers populate this metadata (after hashing the blob) before passing it
// to hap_nrv_nic_apply_calibration(). The NIC stamps these fields into headers.
typedef struct hap_nrv_calibration_descriptor {
hap_nrv_uuid_t calibration_uuid;
hap_nrv_version_t calibration_version;
hap_nrv_calibration_digest_t calibration_hash;
char label[HAP_NRV_NAME_MAX];
char strategy[HAP_NRV_NAME_MAX]; // Must match a registered transform (e.g., "stage.calibration.linear")
uint32_t layout_version;
uint32_t manifest_hash;
hap_nrv_uuid_t skin_uuid;
hap_nrv_model_id_t skin_model;
hap_nrv_uuid_t axon_uuid;
hap_nrv_model_id_t axon_model;
hap_nrv_version_t axon_fw;
} hap_nrv_calibration_descriptor_t;
typedef struct hap_nrv_compensation_descriptor {
hap_nrv_uuid_t compensation_uuid;
hap_nrv_version_t compensation_version;
hap_nrv_calibration_digest_t compensation_hash;
char label[HAP_NRV_NAME_MAX];
char strategy[HAP_NRV_NAME_MAX]; // Must match a registered compensation transform
uint32_t layout_version;
uint32_t manifest_hash;
} hap_nrv_compensation_descriptor_t;
// --- Discovery (Manifests) ---------------------------------------------------
// Describes a single field within the payload blob.
// E.g. "Pressure" at offset 0, "Temperature" at offset 2048.
typedef struct hap_nrv_field_desc {
char name[HAP_NRV_NAME_MAX];
uint32_t offset; // Byte offset from start of payload
uint32_t count; // Number of elements (e.g. 1024 taxels)
uint8_t encoding; // hap_nrv_encoding_t (U16, F32)
uint8_t reserved[3];
} hap_nrv_field_desc_t;
// The Layout Manifest
// Describes the structure of the Blob for a specific driver.
// Sent to the consumer during Handshake.
// 'layout_version' + 'manifest_hash' form the "field signature" so calibration blobs
// can assert compatibility without needing firmware-specific knowledge.
typedef struct hap_nrv_layout {
uint32_t total_size; // Total payload size in bytes
uint32_t field_count; // Number of described fields
uint32_t layout_version;// Monotonic version for this manifest
uint32_t manifest_hash; // Stable hash (e.g., CRC32) of all field descriptors
uint32_t reserved; // Align to 16 bytes / future use
hap_nrv_field_desc_t fields[]; // Flexible array of descriptors
} hap_nrv_layout_t;
// Property Description
// Used for GUI generation to discover available knobs.
typedef struct hap_nrv_prop_desc {
char name[HAP_NRV_NAME_MAX];
hap_nrv_type_t type;
bool read_only;
hap_nrv_value_t min; // Range min (Int/Float)
hap_nrv_value_t max; // Range max (Int/Float)
hap_nrv_value_t def; // Default value
} hap_nrv_prop_desc_t;
// --- Configuration (Builder Pattern) -----------------------------------------
// Allocate a config builder.
// Returns NULL on OOM.
hap_nrv_nic_config_t* hap_nrv_nic_config_create(void);
// Add a driver to the config.
// Returns the assigned driver_id (0..N) or negative error code.
// 'driver_name': String ID of the driver (e.g., "kitronyx_usb")
// 'driver_opts': Driver-specific configuration struct (void* to allow polymorphism)
int hap_nrv_nic_config_add_driver(hap_nrv_nic_config_t* cfg,
const char* driver_name,
const void* driver_opts);
// Set default ring buffer depth for all drivers.
// Default is 64 frames.
void hap_nrv_nic_config_set_default_ring_size(hap_nrv_nic_config_t* cfg,
uint32_t n_frames);
// Override ring buffer depth for a specific driver.
void hap_nrv_nic_config_set_ring_size(hap_nrv_nic_config_t* cfg,
uint16_t driver_id,
uint32_t n_frames);
// Set the Clock Sync Policy (Level 0..3).
// - LEVEL 0 (Latch): Simple offset.
// - LEVEL 1 (Regression): Drift compensation (Default).
// - LEVEL 2 (Ping-Pong): Network latency compensation.
// - LEVEL 3 (PPS): Hardware trigger.
void hap_nrv_nic_config_set_sync_policy(hap_nrv_nic_config_t* cfg,
int policy_level,
uint32_t window_size);
// Logging Callback Type
typedef void (*hap_nrv_log_fn)(void* user_data, hap_nrv_log_level_t level, const char* fmt, va_list args);
// Set the Logging Callback.
// If NULL (default), logs to stderr on Host, or no-op on Embedded.
void hap_nrv_nic_config_set_logger(hap_nrv_nic_config_t* cfg,
hap_nrv_log_fn log_fn,
void* user_data);
// Free the config builder.
void hap_nrv_nic_config_free(hap_nrv_nic_config_t* cfg);
// --- Lifecycle ---------------------------------------------------------------
// Initialize the NIC Core.
// Consumes the config object (do not free cfg after this).
// Allocates all Ring Buffers and Triple Buffers.
int hap_nrv_nic_init(hap_nrv_nic_t** out_nic, hap_nrv_nic_config_t* cfg);
// Start all drivers and processing threads.
// Begins the data stream.
int hap_nrv_nic_start(hap_nrv_nic_t* nic);
// Stop all threads.
// Safe to call hap_nrv_nic_start() again after this.
int hap_nrv_nic_stop(hap_nrv_nic_t* nic);
// Destroy the NIC and free all resources.
void hap_nrv_nic_shutdown(hap_nrv_nic_t* nic);
// --- Control Plane (Properties & Calibration) --------------------------------
// Enumerate available properties for a driver.
// Returns number of properties found.
// If 'out_descs' is provided, fills up to 'capacity' items.
int hap_nrv_nic_enum_properties(hap_nrv_nic_t* nic, uint16_t driver_id,
hap_nrv_prop_desc_t* out_descs, size_t capacity);
// Set a runtime property (Type-Safe).
// Validates value against Min/Max/Type.
// Returns HAP_NRV_ERR_PROP_READ_ONLY if read-only.
int hap_nrv_nic_set_property(hap_nrv_nic_t* nic, uint16_t driver_id,
const char* name, hap_nrv_value_t val);
// Get a runtime property.
int hap_nrv_nic_get_property(hap_nrv_nic_t* nic, uint16_t driver_id,
const char* name, hap_nrv_value_t* out_val);
// The Transform Interface (Strategy Pattern).
// Each stage (Calibration or Compensation) is registered under a string key
// (see HAP_NRV_STAGE_*). Drivers never touch this; applications register strategies.
// 'ctx': Custom context pointer supplied during registration.
// 'in': Input frame (Raw or output of previous stage).
// 'out': Output frame (must write header+payload before returning).
// Returns 0 on success.
typedef int (*hap_nrv_transform_fn)(void* ctx, const hap_nrv_frame_t* in, hap_nrv_frame_t* out);
// Register a custom strategy implementation.
// - Host: Pass a function pointer from dlsym().
// - Embedded: Pass a static function pointer compiled into firmware.
// 'strategy_name' must match the descriptor->strategy string supplied in
// hap_nrv_calibration_descriptor_t / hap_nrv_compensation_descriptor_t (for example,
// "stage.calibration.linear"). Registration must occur before hap_nrv_nic_start();
// runtime swapping is not supported. Returns error if strategy_name already exists.
int hap_nrv_nic_register_transform(hap_nrv_nic_t* nic, const char* strategy_name,
hap_nrv_transform_fn fn, void* ctx);
// Apply a calibration blob (Stage A) from persistent storage.
// The descriptor encodes provenance + compatibility info that the NIC stamps into
// frame headers and identity queries. The blob buffer is copied immediately; callers
// may free their RAM buffer after this call returns.
// - desc->strategy must name a registered transform. If missing, returns HAP_NRV_ERR_STRATEGY_UNKNOWN and
// the application should log desc->strategy plus hap_nrv_runtime_api_version() for operator clarity.
// Must be called before hap_nrv_nic_start(); hot-swapping at runtime is unsupported.
int hap_nrv_nic_apply_calibration(hap_nrv_nic_t* nic,
uint16_t driver_id,
const hap_nrv_calibration_descriptor_t* desc,
const void* blob, size_t blob_size);
// Apply a compensation blob (Stage B). Same lifecycle rules as calibration.
// - desc->strategy must exist in the transform registry or the call fails with HAP_NRV_ERR_STRATEGY_UNKNOWN.
int hap_nrv_nic_apply_compensation(hap_nrv_nic_t* nic,
uint16_t driver_id,
const hap_nrv_compensation_descriptor_t* desc,
const void* blob, size_t blob_size);
// Bypass helpers: force a stage to behave as identity (raw passthrough).
int hap_nrv_nic_set_calibration_bypass(hap_nrv_nic_t* nic,
uint16_t driver_id,
bool enable);
int hap_nrv_nic_set_compensation_bypass(hap_nrv_nic_t* nic,
uint16_t driver_id,
bool enable);
// Request a clock sync trigger from the driver (if supports_sync_trigger == true).
// For remote nodes this usually latches PPS or emits a sync pulse.
int hap_nrv_nic_trigger_clock_sync(hap_nrv_nic_t* nic, uint16_t driver_id);
// --- Data Plane (Consumption) ------------------------------------------------
// 1. Discovery: Get the Layout Manifest for a driver slot (driver_id == slot index).
// Pointer remains valid for the lifetime of the NIC (immutable manifest).
const hap_nrv_layout_t* hap_nrv_nic_get_layout(hap_nrv_nic_t* nic, uint16_t driver_id);
// Export Layout to JSON (for Network Handshake).
// Writes a JSON string describing the layout into 'out_buf'.
// Returns bytes written.
int hap_nrv_nic_export_layout_json(const hap_nrv_layout_t* layout,
char* out_buf, size_t buf_size);
// 2. State Path: Access the absolute latest frame (Zero-Copy).
// Locks the "Read" slot of the Triple Buffer so it won't be overwritten.
// Thread-safe: many threads can lock/unlock different driver IDs concurrently.
// MUST call hap_nrv_nic_unlock_latest() when done.
// Returns NULL if no frame is available.
const hap_nrv_frame_t* hap_nrv_nic_lock_latest(hap_nrv_nic_t* nic, uint16_t driver_id);
// Release the lock on the latest frame.
// Allows the slot to be reused by the driver.
void hap_nrv_nic_unlock_latest(hap_nrv_nic_t* nic, uint16_t driver_id);
// 3. Stream Path: Get the next chronological frame from ANY driver.
// This acts as a Time-Ordered Iterator across all active drivers.
// Single-consumer: must be called from exactly one thread. Destructive read:
// advancing the iterator frees the underlying ring slot for reuse.
// Gateways that need multiple consumers should fan out above this API.
// Pointers remain valid until you call hap_nrv_nic_release_frame() or call next_frame again.
// 'timeout_ms': < 0 for infinite wait, 0 for non-blocking.
int hap_nrv_nic_next_frame(hap_nrv_nic_t* nic, const hap_nrv_frame_t** out_frame, int timeout_ms);
// 4. Composite: Get a composite frame (Virtual Sensor).
// Aggregates snapshots from multiple drivers into one super-frame.
// Pointer lifetime matches next_frame: release via hap_nrv_nic_release_frame().
int hap_nrv_nic_get_composite(hap_nrv_nic_t* nic, uint16_t virtual_id,
const hap_nrv_frame_t** out_frame);
// Release a frame obtained from hap_nrv_nic_next_frame() or hap_nrv_nic_get_composite().
// Must be called once per borrowed pointer before requesting another frame.
int hap_nrv_nic_release_frame(hap_nrv_nic_t* nic, const hap_nrv_frame_t* frame);
// Obtain an OS-level event handle that becomes signaled when new data arrives.
// POSIX: returns a poll/epoll-safe file descriptor.
// Windows: returns a HANDLE cast to hap_nrv_event_handle_t.
// Returns 0 on success, negative error if the platform does not expose a handle.
int hap_nrv_nic_get_event_handle(hap_nrv_nic_t* nic, hap_nrv_event_handle_t* out_handle);
// Identity / provenance snapshot for a driver slot.
typedef struct hap_nrv_identity_info {
uint16_t api_version;
uint32_t layout_version;
uint16_t driver_slot;
uint16_t reserved;
hap_nrv_model_id_t axon_model;
hap_nrv_model_id_t skin_model;
hap_nrv_uuid_t axon_uuid;
hap_nrv_uuid_t skin_uuid;
hap_nrv_version_t axon_fw;
hap_nrv_version_t driver_version;
hap_nrv_stage_snapshot_t stages;
hap_nrv_clock_caps_t clock_caps;
} hap_nrv_identity_info_t;
// Fetch the current identity metadata for a driver slot.
// Populates effective UUIDs and version numbers exactly as they will appear in frame headers.
int hap_nrv_nic_get_identity_info(hap_nrv_nic_t* nic,
uint16_t driver_id,
hap_nrv_identity_info_t* out_info);
// --- Telemetry ---------------------------------------------------------------
typedef struct hap_nrv_health {
hap_nrv_driver_state_t state; // Current lifecycle state
uint32_t crc_errors; // Link integrity
uint32_t resync_events; // Parser framing loss
uint32_t dropped_frames; // Queue overflow
float throughput_mbps; // Bandwidth
float jitter_us; // Arrival variance
} hap_nrv_health_t;
// Get aggregate system health for a driver.
// This is calculated by the Service Layer (1Hz), distinct from per-frame flags.
int hap_nrv_nic_get_health(hap_nrv_nic_t* nic, uint16_t driver_id, hap_nrv_health_t* out_health);
#ifdef __cplusplus
}
#endif
Driver Interface (libhapnrv_driver.h)
Service Provider Interface for implementing custom hardware drivers.
/*
* Haptica Nerve Driver SPI (V1)
* =============================
* Service Provider Interface for implementing new hardware drivers.
*
* This header defines the contract between the NIC Core and a Driver.
* To support a new sensor, implement this VTable.
*/
#pragma once
#include "libhapnrv.h"
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif
// Forward declarations
typedef struct hap_nrv_driver_instance hap_nrv_driver_instance_t;
// --- The Push Callback -------------------------------------------------------
// The mechanism for a driver to hand a completed frame to the NIC Core.
// 'user_data': The pointer passed to driver->open() (usually the NIC context).
// 'header': Partially filled header (Seq, TS_Hardware, Flags).
// 'payload': Pointer to the raw blob data.
// 'len': Size of payload.
// Returns 0 on success (accepted), non-zero on drop/error.
typedef int (*hap_nrv_nic_submit_fn)(void* user_data,
const hap_nrv_header_t* header,
const uint8_t* payload,
size_t len);
// --- The Logging Callback ----------------------------------------------------
// The mechanism for a driver to log messages.
// The NIC Core implements this and prepends "[Driver ID:Name]".
typedef void (*hap_nrv_driver_log_fn)(void* log_ctx, hap_nrv_log_level_t level, const char* fmt, ...);
// --- The Driver VTable -------------------------------------------------------
typedef struct hap_nrv_driver_vtbl {
// Driver Name (e.g. "kitronyx_usb")
const char* name;
// create: Allocate driver-specific state.
// Returns the instance context (void*).
void* (*create)(void);
// destroy: Free driver state.
void (*destroy)(void* ctx);
// open: Initialize hardware and bind to a port.
// 'params': Binding string (e.g. "serial:A123").
// 'submit_fn': Callback to push frames to.
// 'user_data': Context for submit_fn.
// 'log_fn': Callback for logging.
// 'log_ctx': Context for log_fn.
int (*open)(void* ctx, const char* params,
hap_nrv_nic_submit_fn submit_fn, void* user_data,
hap_nrv_driver_log_fn log_fn, void* log_ctx);
// close: Stop streaming and release hardware.
int (*close)(void* ctx);
// get_manifest: Return the fixed blob layout, properties, and clock caps.
// Should fill 'out_layout', 'out_props', and 'out_clock_caps' if provided.
int (*get_manifest)(void* ctx,
hap_nrv_layout_t* out_layout,
hap_nrv_clock_caps_t* out_clock_caps,
hap_nrv_prop_desc_t* out_props, size_t max_props);
// trigger_clock_sync: Optional hook to latch PPS / emit sync pulses.
int (*trigger_clock_sync)(void* ctx);
// set_property: Runtime control.
int (*set_property)(void* ctx, const char* key, hap_nrv_value_t val);
// get_property: Read current state.
int (*get_property)(void* ctx, const char* key, hap_nrv_value_t* out_val);
} hap_nrv_driver_vtbl_t;
// --- Registration ------------------------------------------------------------
// Register a driver class with the NIC Core.
// Usually called by a constructor or plugin loader.
int hap_nrv_nic_register_driver(const hap_nrv_driver_vtbl_t* vtbl);
#ifdef __cplusplus
}
#endif
Helper Utilities (libhapnrv_helpers.h)
Convenience functions for working with NIC API structures.
/*
* Haptica Nerve Helpers (V1)
* ==========================
* Convenience functions for working with NIC API structures.
*
* These helpers trade a small amount of CPU (lookups, casting) for
* significant developer ergonomics.
*/
#pragma once
#include "libhapnrv.h"
#include <stdio.h>
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
#endif
// --- Field Lookup ------------------------------------------------------------
// Find a field descriptor by name.
// Returns pointer to descriptor inside layout, or NULL if not found.
// Complexity: O(N) linear scan. Cache this pointer if performance matters.
static inline const hap_nrv_field_desc_t* hap_nrv_helper_lookup_field(const hap_nrv_layout_t* layout,
const char* name) {
if (!layout || !name) return NULL;
for (uint32_t i = 0; i < layout->field_count; ++i) {
// Simple string match (assumes strcmp availability or use custom loop)
const char* f_name = layout->fields[i].name;
const char* q_name = name;
while (*f_name && *f_name == *q_name) { f_name++; q_name++; }
if (*f_name == '\0' && *q_name == '\0') return &layout->fields[i];
}
return NULL;
}
// --- Safe Accessors (Type Conversion) ----------------------------------------
// Get a float value from a specific index in a field.
// Handles U8/U16 -> Float conversion automatically.
// Returns 0.0f if index out of bounds or type invalid.
static inline float hap_nrv_helper_get_float(const hap_nrv_frame_t* frame,
const hap_nrv_field_desc_t* field,
uint32_t index) {
if (!frame || !field || index >= field->count) return 0.0f;
const uint8_t* ptr = frame->payload + field->offset;
switch (field->encoding) {
case HAP_NRV_ENC_U8:
return (float)((const uint8_t*)ptr)[index];
case HAP_NRV_ENC_U16:
return (float)((const uint16_t*)ptr)[index];
case HAP_NRV_ENC_F32:
return ((const float*)ptr)[index];
default:
return 0.0f;
}
}
// Get an int value from a specific index in a field.
static inline int32_t hap_nrv_helper_get_int(const hap_nrv_frame_t* frame,
const hap_nrv_field_desc_t* field,
uint32_t index) {
if (!frame || !field || index >= field->count) return 0;
const uint8_t* ptr = frame->payload + field->offset;
switch (field->encoding) {
case HAP_NRV_ENC_U8:
return (int32_t)((const uint8_t*)ptr)[index];
case HAP_NRV_ENC_U16:
return (int32_t)((const uint16_t*)ptr)[index];
case HAP_NRV_ENC_F32:
return (int32_t)((const float*)ptr)[index]; // Truncates
default:
return 0;
}
}
// --- Debug Utilities ---------------------------------------------------------
// Print a frame summary to stdout (JSON-like).
// Useful for "First Value" debugging.
static inline void hap_nrv_helper_print_frame(const hap_nrv_frame_t* frame,
const hap_nrv_layout_t* layout) {
if (!frame || !layout) return;
printf("{\n");
printf(" \"magic\": 0x%08X,\n", frame->header.magic);
printf(" \"seq\": %u,\n", frame->header.seq);
printf(" \"ts_capture\": %" PRIu64 ",\n", frame->header.ts_capture);
printf(" \"driver_slot\": %u,\n", frame->header.driver_slot);
printf(" \"flags\": 0x%08X,\n", frame->header.flags);
printf(" \"fields\": {\n");
for (uint32_t i = 0; i < layout->field_count; ++i) {
const hap_nrv_field_desc_t* field = &layout->fields[i];
printf(" \"%s\": [", field->name);
// Print first 3 values
uint32_t limit = field->count > 3 ? 3 : field->count;
for (uint32_t j = 0; j < limit; ++j) {
float val = hap_nrv_helper_get_float(frame, field, j);
printf("%.2f%s", val, (j < limit - 1) ? ", " : "");
}
if (field->count > 3) printf(", ...");
printf("]%s\n", (i < layout->field_count - 1) ? "," : "");
}
printf(" }\n");
printf("}\n");
}
#ifdef __cplusplus
}
#endif