ENI Developer Documentation v0.1.0

Embedded Neural Interface framework with provider contracts, event processing, safety policies, signal filtering, and Neuralink BCI integration. Headers: <eni/provider_contract.h>, <eni/event.h>, <eni/policy.h>, <eni_min/filter.h>, <eni_min/normalizer.h>, <eni/neuralink.h>

💡 Event System

Structured events for neural data, intents, and features. Header: <eni/event.h>

eni_event_t

iduint32_tUnique event ID (auto-assigned).
versionuint32_tEvent schema version.
typeeni_event_type_tENI_EVENT_INTENT, ENI_EVENT_FEATURES, ENI_EVENT_RAW, ENI_EVENT_CONTROL.
timestampeni_timestamp_tEvent creation time (microseconds).
source[64]charProvider name that generated this event.
payloadunionIntent (.intent), features (.features), or raw data (.raw).

eni_intent_t

name[64]charIntent name (e.g., "move_left", "select", "scroll_down").
confidencefloatConfidence score 0.0-1.0.

eni_feature_t / eni_feature_set_t

name[32]charFeature name (e.g., "alpha_power", "beta_coherence").
valuefloatFeature value.
features[32]eni_feature_tArray of up to 32 features in a set.
countintNumber of features in the set.

eni_event_type_t

ENI_EVENT_INTENT0Decoded user intent with confidence.
ENI_EVENT_FEATURES1Extracted neural features (alpha, beta, gamma power).
ENI_EVENT_RAW2Raw byte payload (up to 4096 bytes).
ENI_EVENT_CONTROL3System control event (calibration, config change).
eni_status_t eni_event_init(eni_event_t *ev, eni_event_type_t type, const char *source)

Initialize an event with type, source name, auto-generated ID, current timestamp, and zeroed payload. Must be called before setting intent/features/raw data.

Parameters
eveni_event_t *Event to initialize.
typeeni_event_type_tEvent type.
sourceconst char *Provider name (max 63 chars).
Returns

ENI_OK on success. ENI_ERR_INVALID if ev or source is NULL.

Example
eni_event_t ev;
eni_event_init(&ev, ENI_EVENT_INTENT, "neuralink_n1");
eni_event_set_intent(&ev, "move_cursor_left", 0.92f);
eni_event_print(&ev);
eni_status_t eni_event_set_intent(eni_event_t *ev, const char *intent, float confidence)

Set intent payload on an ENI_EVENT_INTENT event. Copies intent name and confidence score.

Parameters
eveni_event_t *Event (must be type INTENT).
intentconst char *Intent name (max 63 chars).
confidencefloatConfidence 0.0-1.0.
Returns

ENI_OK on success.

eni_status_t eni_event_add_feature(eni_event_t *ev, const char *name, float value)

Add a feature to an ENI_EVENT_FEATURES event. Up to ENI_EVENT_FEATURES_MAX (32) features per event.

Parameters
eveni_event_t *Event (must be type FEATURES).
nameconst char *Feature name (max 31 chars).
valuefloatFeature value.
Returns

ENI_OK on success. ENI_ERR_FULL if 32 features reached.

eni_status_t eni_event_set_raw(eni_event_t *ev, const uint8_t *data, size_t len)

Set raw byte payload on an ENI_EVENT_RAW event. Max ENI_EVENT_PAYLOAD_MAX (4096) bytes.

Parameters
eveni_event_t *Event (must be type RAW).
dataconst uint8_t *Raw data buffer.
lensize_tData length in bytes.
Returns

ENI_OK on success. ENI_ERR_INVALID if len exceeds 4096.

void eni_event_print(const eni_event_t *ev)

Print event details to stdout for debugging: type, source, timestamp, and payload contents.

Parameters
evconst eni_event_t *Event to print.

🔌 Provider Contract API

Interface for neural data providers. Header: <eni/provider_contract.h>

eni_provider_ops_t

Virtual function table that all providers must implement.

nameconst char *Provider name (e.g., "neuralink_n1", "emotiv_epoc").
initfn(prov, config) → statusInitialize with device-specific config.
pollfn(prov, ev) → statusPoll for next event (non-blocking).
startfn(prov) → statusStart data acquisition.
stopfn(prov) → statusStop data acquisition.
shutdownfn(prov)Release all resources.

eni_provider_t

name[64]charInstance name.
opsconst eni_provider_ops_t *Pointer to vtable.
ctxvoid *Provider-specific context data.
runningboolWhether provider is actively streaming.
transporteni_transport_tCommunication transport (BLE, USB, SPI, etc.).
eni_status_t eni_provider_init(eni_provider_t *prov, const eni_provider_ops_t *ops, const char *name, const void *config)

Initialize a provider instance with its vtable, name, and device-specific configuration. Calls the provider's ops->init() callback.

Parameters
proveni_provider_t *Provider instance to initialize.
opsconst eni_provider_ops_t *Provider vtable (must remain valid for lifetime).
nameconst char *Instance name (max 63 chars).
configconst void *Device-specific config (cast inside init callback).
Returns

ENI_OK on success. Provider-specific error codes.

Example
eni_provider_t prov;
eni_nl_config_t nl_cfg = {
    .mode = ENI_NL_MODE_INTENT,
    .channels = 256,
    .sample_rate = 30000,
    .device_addr = "NL-001-ABC",
    .signal_threshold = 0.3f,
    .auto_calibrate = 1
};
eni_provider_init(&prov, eni_neuralink_get_provider(),
                  "neuralink_primary", &nl_cfg);
eni_status_t eni_provider_start(eni_provider_t *prov)

Start the provider's data acquisition. After this call, eni_provider_poll() will return events.

Parameters
proveni_provider_t *Initialized provider.
Returns

ENI_OK on success.

eni_status_t eni_provider_poll(eni_provider_t *prov, eni_event_t *ev)

Poll for the next available event. Non-blocking: returns ENI_ERR_EMPTY if no event is ready. Call in a loop from your main task or timer callback.

Parameters
proveni_provider_t *Running provider.
eveni_event_t *Output event buffer.
Returns

ENI_OK if event available. ENI_ERR_EMPTY if no event ready.

Example
// Main polling loop
eni_event_t ev;
while (running) {
    if (eni_provider_poll(&prov, &ev) == ENI_OK) {
        if (ev.type == ENI_EVENT_INTENT) {
            printf("Intent: %s (%.1f%%)\n",
                   ev.payload.intent.name,
                   ev.payload.intent.confidence * 100.0f);
            if (ev.payload.intent.confidence > 0.8f) {
                execute_action(ev.payload.intent.name);
            }
        }
    }
    eos_task_delay_ms(10);  // Yield to other tasks
}
eni_status_t eni_provider_stop(eni_provider_t *prov)

Stop data acquisition. Provider remains initialized and can be restarted.

Parameters
proveni_provider_t *Running provider.
Returns

ENI_OK on success.

void eni_provider_shutdown(eni_provider_t *prov)

Fully shut down the provider and release all resources. Must not be used after this call.

Parameters
proveni_provider_t *Provider to shut down.

🛡 Safety Policy Engine

Action classification and permission control. Header: <eni/policy.h>

eni_action_class_t

ENI_ACTION_SAFE0Low-risk action (cursor movement, scrolling).
ENI_ACTION_CONTROLLED1Medium-risk (typing, file operations).
ENI_ACTION_RESTRICTED2High-risk (motor control, network access, system commands).

eni_policy_verdict_t

ENI_POLICY_ALLOW0Action permitted.
ENI_POLICY_DENY1Action blocked.
ENI_POLICY_CONFIRM2Action requires user confirmation.
eni_status_t eni_policy_init(eni_policy_engine_t *engine)

Initialize policy engine with no rules and default_deny=false. Maximum ENI_POLICY_MAX_RULES (64) rules.

Parameters
engineeni_policy_engine_t *Policy engine to initialize.
Returns

ENI_OK on success.

eni_status_t eni_policy_add_rule(eni_policy_engine_t *engine, const char *action, eni_policy_verdict_t verdict, eni_action_class_t action_class)

Add a policy rule mapping an action name to a verdict and classification.

Parameters
engineeni_policy_engine_t *Policy engine.
actionconst char *Action name (max 63 chars), e.g., "move_cursor", "emergency_stop".
verdicteni_policy_verdict_tALLOW, DENY, or CONFIRM.
action_classeni_action_class_tSAFE, CONTROLLED, or RESTRICTED.
Returns

ENI_OK on success. ENI_ERR_FULL if 64 rules reached.

Example
eni_policy_engine_t policy;
eni_policy_init(&policy);
eni_policy_set_default_deny(&policy, true);

// Safe actions - always allowed
eni_policy_add_rule(&policy, "move_cursor",  ENI_POLICY_ALLOW, ENI_ACTION_SAFE);
eni_policy_add_rule(&policy, "scroll",       ENI_POLICY_ALLOW, ENI_ACTION_SAFE);

// Controlled - require confirmation
eni_policy_add_rule(&policy, "click",        ENI_POLICY_CONFIRM, ENI_ACTION_CONTROLLED);
eni_policy_add_rule(&policy, "type_text",    ENI_POLICY_CONFIRM, ENI_ACTION_CONTROLLED);

// Restricted - denied by default
eni_policy_add_rule(&policy, "motor_start",  ENI_POLICY_DENY, ENI_ACTION_RESTRICTED);
eni_policy_verdict_t eni_policy_evaluate(const eni_policy_engine_t *engine, const char *action)

Evaluate an action against the policy rules. Returns the verdict for the first matching rule, or DENY if default_deny is true and no rule matches, or ALLOW otherwise.

Parameters
engineconst eni_policy_engine_t *Policy engine.
actionconst char *Action name to evaluate.
Returns

ENI_POLICY_ALLOW, ENI_POLICY_DENY, or ENI_POLICY_CONFIRM.

eni_status_t eni_policy_set_default_deny(eni_policy_engine_t *engine, bool deny)

Set whether unmatched actions are denied (true) or allowed (false). Recommended: true for safety-critical applications.

Parameters
engineeni_policy_engine_t *Policy engine.
denyboolDefault deny flag.
Returns

ENI_OK.

void eni_policy_dump(const eni_policy_engine_t *engine)

Print all policy rules to stdout for debugging.

Parameters
engineconst eni_policy_engine_t *Policy engine.
Sets up signal filters, callbacks, and prepares for connection. Does not connect to the device.

Parameters
cfgconst eni_nl_config_t *Configuration including mode, channels, sample rate, filters, and callbacks.
Returns

0 on success. -1 on invalid config. -2 on resource allocation failure.

Example
void on_intent(const char *intent, float conf, void *ctx) {
    printf("Intent: %s (%.0f%%)\n", intent, conf * 100);
}

eni_nl_config_t cfg = {
    .mode = ENI_NL_MODE_INTENT,
    .channels = 256,
    .sample_rate = 30000,
    .device_addr = "NL-001-ABC",
    .signal_threshold = 0.3f,
    .auto_calibrate = 1,
    .filter_enabled = 1,
    .bandpass_low_hz = 0.5f,
    .bandpass_high_hz = 300.0f,
    .notch_freq_hz = 60.0f,
    .on_intent = on_intent,
    .user_ctx = NULL
};
eni_neuralink_init(&cfg);
void eni_neuralink_deinit(void)

Deinitialize the Neuralink adapter. Disconnects if connected and releases all resources.

int eni_neuralink_connect(const char *device_addr)

Connect to a Neuralink device by address. Blocks until connected or timeout. If auto_calibrate is set, runs calibration after connecting.

Parameters
device_addrconst char *Device address/ID string (e.g., "NL-001-ABC").
Returns

0 on success. -1 on connection failure. -2 on timeout.

int eni_neuralink_disconnect(void)

Disconnect from the current device. Stops streaming if active.

Returns

0 on success.

int eni_neuralink_start_stream(void) / eni_neuralink_stop_stream(void)

Start or stop neural data streaming. Once started, packets arrive via the on_packet callback or can be read with eni_neuralink_read_packet().

Returns

0 on success. -1 if not connected.

int eni_neuralink_read_packet(eni_nl_packet_t *pkt)

Read the next available neural data packet. Non-blocking: returns -1 if no packet ready. Each packet contains up to 256 float samples from one electrode.

Parameters
pkteni_nl_packet_t *Output packet buffer.
Returns

0 on success (packet available). -1 if no data ready.

Example
eni_neuralink_connect("NL-001-ABC");
eni_neuralink_start_stream();

eni_nl_packet_t pkt;
while (running) {
    if (eni_neuralink_read_packet(&pkt) == 0) {
        printf("Electrode %d: %u samples, quality=%.2f\n",
               pkt.electrode_id, pkt.channel_count, pkt.signal_quality);
        // Process neural data...
        for (uint32_t i = 0; i < pkt.channel_count; i++) {
            float uv = pkt.channel_data[i];  // microvolts
            process_sample(pkt.electrode_id, uv);
        }
    }
    eos_task_delay_ms(1);
}
int eni_neuralink_calibrate(uint32_t duration_ms)

Run calibration for the specified duration. The device collects baseline neural signals and computes noise thresholds. Must be connected and not streaming.

Parameters
duration_msuint32_tCalibration duration (recommended: 5000-30000 ms).
Returns

0 on success. -1 if not connected or already streaming.

int eni_neuralink_set_threshold(float threshold)

Set the signal quality threshold for filtering noisy packets. Packets below this threshold are dropped.

Parameters
thresholdfloatThreshold 0.0-1.0 (e.g., 0.3 for 30% minimum quality).
Returns

0 on success.

int eni_neuralink_decode_intent(const eni_nl_packet_t *pkt, char *intent, int maxlen, float *confidence)

Decode a user intent from a neural data packet using the on-device decoder model. Returns intent name and confidence score.

Parameters
pktconst eni_nl_packet_t *Input neural data packet.
intentchar *Output intent name buffer.
maxlenintMax length of intent buffer.
confidencefloat *Output confidence score (0.0-1.0).
Returns

0 on success. -1 if packet quality too low for decoding.

int eni_neuralink_get_status(eni_nl_status_t *status)

Get comprehensive device status including battery, temperature, packet stats, and latency.

Parameters
statuseni_nl_status_t *Output status buffer.
Returns

0 on success. -1 if not connected.

Example
eni_nl_status_t status;
if (eni_neuralink_get_status(&status) == 0) {
    printf("Device: %s (FW %s)\n", status.device_id, status.firmware_version);
    printf("Battery: %.0f%%, Temp: %.1fC\n", status.battery_pct, status.temperature_c);
    printf("Packets: %llu recv, %llu dropped\n",
           status.packets_received, status.packets_dropped);
    printf("Latency: %.1f ms avg\n", status.avg_latency_ms);
}
const eni_provider_ops_t * eni_neuralink_get_provider(void)

Get the ENI provider vtable for Neuralink. Use with eni_provider_init() to integrate into the ENI provider framework.

Returns

Pointer to static provider ops vtable.

🎯 Calibration Workflow

// Complete Neuralink calibration and intent decoding workflow
eni_nl_config_t cfg = {
    .mode = ENI_NL_MODE_INTENT,
    .channels = 256,
    .sample_rate = 30000,
    .device_addr = "NL-001-ABC",
    .signal_threshold = 0.3f,
    .auto_calibrate = 0,  // Manual calibration
    .filter_enabled = 1,
    .bandpass_low_hz = 0.5f,
    .bandpass_high_hz = 300.0f,
    .notch_freq_hz = 60.0f
};

// 1. Initialize and connect
eni_neuralink_init(&cfg);
eni_neuralink_connect("NL-001-ABC");

// 2. Run calibration (user should remain still)
printf("Calibrating... please remain still for 10 seconds\n");
eni_neuralink_calibrate(10000);  // 10 second calibration
printf("Calibration complete!\n");

// 3. Set up policy for intent safety
eni_policy_engine_t policy;
eni_policy_init(&policy);
eni_policy_set_default_deny(&policy, true);
eni_policy_add_rule(&policy, "move_cursor",  ENI_POLICY_ALLOW, ENI_ACTION_SAFE);
eni_policy_add_rule(&policy, "click",        ENI_POLICY_CONFIRM, ENI_ACTION_CONTROLLED);
eni_policy_add_rule(&policy, "motor_start",  ENI_POLICY_DENY, ENI_ACTION_RESTRICTED);

// 4. Set up filter
eni_min_filter_t filter;
eni_min_filter_init(&filter, 0.80f, 200);

// 5. Start streaming and process intents
eni_neuralink_start_stream();

eni_nl_packet_t pkt;
char intent[64];
float confidence;

while (running) {
    if (eni_neuralink_read_packet(&pkt) == 0) {
        if (eni_neuralink_decode_intent(&pkt, intent, sizeof(intent), &confidence) == 0) {
            // Create event for filtering
            eni_event_t ev;
            eni_event_init(&ev, ENI_EVENT_INTENT, "neuralink_n1");
            eni_event_set_intent(&ev, intent, confidence);

            if (eni_min_filter_accept(&filter, &ev)) {
                // Check policy
                eni_policy_verdict_t v = eni_policy_evaluate(&policy, intent);
                if (v == ENI_POLICY_ALLOW) {
                    execute_action(intent);
                } else if (v == ENI_POLICY_CONFIRM) {
                    if (user_confirms(intent)) execute_action(intent);
                }
                // DENY actions are silently dropped
            }
        }
    }
    eos_task_delay_ms(1);
}

eni_neuralink_stop_stream();
eni_neuralink_disconnect();
eni_neuralink_deinit();

🔧 Provider Implementation Guide

Implementing a Custom Provider

// Example: Custom EMG (Electromyography) provider

typedef struct {
    uint8_t spi_port;
    uint16_t channels;
} emg_config_t;

typedef struct {
    emg_config_t cfg;
    bool streaming;
} emg_ctx_t;

static eni_status_t emg_init(eni_provider_t *prov, const void *config) {
    emg_ctx_t *ctx = (emg_ctx_t *)prov->ctx;
    memcpy(&ctx->cfg, config, sizeof(emg_config_t));
    eos_spi_config_t spi = { .port = ctx->cfg.spi_port, .clock_hz = 4000000 };
    eos_spi_init(&spi);
    return ENI_OK;
}

static eni_status_t emg_poll(eni_provider_t *prov, eni_event_t *ev) {
    emg_ctx_t *ctx = (emg_ctx_t *)prov->ctx;
    if (!ctx->streaming) return ENI_ERR_EMPTY;
    uint8_t raw[64];
    if (eos_spi_read(ctx->cfg.spi_port, raw, sizeof(raw)) < 0)
        return ENI_ERR_EMPTY;
    eni_event_init(ev, ENI_EVENT_RAW, "emg_sensor");
    eni_event_set_raw(ev, raw, sizeof(raw));
    return ENI_OK;
}

static eni_status_t emg_start(eni_provider_t *prov) {
    ((emg_ctx_t *)prov->ctx)->streaming = true;
    return ENI_OK;
}

static eni_status_t emg_stop(eni_provider_t *prov) {
    ((emg_ctx_t *)prov->ctx)->streaming = false;
    return ENI_OK;
}

static void emg_shutdown(eni_provider_t *prov) {
    emg_ctx_t *ctx = (emg_ctx_t *)prov->ctx;
    eos_spi_deinit(ctx->cfg.spi_port);
}

static const eni_provider_ops_t emg_ops = {
    .name = "emg_sensor",
    .init = emg_init, .poll = emg_poll,
    .start = emg_start, .stop = emg_stop, .shutdown = emg_shutdown
};

// Usage:
emg_ctx_t emg_ctx;
eni_provider_t prov;
prov.ctx = &emg_ctx;
emg_config_t emg_cfg = { .spi_port = 0, .channels = 8 };
eni_provider_init(&prov, &emg_ops, "arm_emg", &emg_cfg);
eni_provider_start(&prov);

Thread Safety: Provider poll() is NOT thread-safe per instance. Use one provider per task. The policy engine is read-only after initialization and can be shared. Neuralink callbacks fire from an internal reception thread — keep callback processing minimal.