LUVINER
Live Demo Benchmarks Docs Edge AI About Blog IT Log in
Docs
Getting Started CSV Format Supported Hardware Firmware Integration

Firmware Integration

How to include the Luviner model in your firmware project and run inference on your MCU.

What you receive

After compilation, you download a ZIP containing two files:

  • luviner_v3_model.a — static library containing the trained model (UID-locked)
  • luviner_v3_model.h — header file with the inference API and model constants

API overview

The header defines model constants and exposes the inference functions. All inputs and outputs use integer arithmetic (int16_t) — no floating point needed on the MCU.

// luviner_v3_model.h (excerpt) #include <stdint.h> // Model constants (set automatically from your dataset) #define NC3_N_INPUTS 8 // number of features (CSV columns) #define NC3_N_OUTPUTS 4 // number of classes // Activate the library with your device UID (call once at startup). // Returns 1 on success, 0 if UID is not authorized. // All inference functions return -1 until activated. int nc3_activate(uint64_t device_uid); // Initialize model state (call once at startup, after activation) void nc3_init(void); // Run inference on one sample (stateless — resets internal state) // inputs: fixed-point array (value * 256) // returns: predicted class index (0, 1, 2, ...) int nc3_predict(const int16_t inputs[NC3_N_INPUTS]); // Streaming inference (maintains state between calls) // Use this for time-series / continuous monitoring int nc3_stream_predict(const int16_t inputs[NC3_N_INPUTS]); // Reset streaming state (e.g. between sessions) void nc3_stream_reset(void);

Converting sensor readings

The model expects inputs as fixed-point integers. Normalize your sensor values the same way your CSV training data was structured, then multiply by 256:

// Convert a float sensor reading to int16_t static inline int16_t float_to_fixed(float value) { int32_t q = (int32_t)(value * 256.0f); if (q > 32767) return 32767; if (q < -32768) return -32768; return (int16_t)q; }

Basic usage example

#include "luviner_v3_model.h" #include <stdio.h> // Class names (same order as training CSV) const char *classes[] = {"normal", "bearing_fault", "misalignment", "imbalance"}; // Your device's unique ID (from chip UID register) #define MY_DEVICE_UID 0x1234567890ABCDEFULL int main(void) { // Activate with device UID if (!nc3_activate(MY_DEVICE_UID)) { printf("License check failed\n"); return 1; } nc3_init(); // Read sensor data and convert to fixed-point int16_t inputs[NC3_N_INPUTS]; inputs[0] = float_to_fixed(0.12f); // sensor 1 inputs[1] = float_to_fixed(0.98f); // sensor 2 inputs[2] = float_to_fixed(-0.05f); // sensor 3 // ... fill all NC3_N_INPUTS values // Run inference int prediction = nc3_predict(inputs); printf("Predicted: %s\n", classes[prediction]); return 0; }

Build integration

ARM Cortex-M (Makefile)

# Add to your Makefile LIBS += luviner_v3_model.a INCLUDES += -I./luviner/ # Compile arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -Os \ -I./luviner/ main.c -L./luviner/ -lluviner_v3_model -o firmware.elf

ESP-IDF (CMakeLists.txt)

# In your component CMakeLists.txt idf_component_register( SRCS "main.c" INCLUDE_DIRS "." "luviner" ) target_link_libraries(${COMPONENT_LIB} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/luviner/luviner_v3_model.a" )

Arduino

// Copy luviner_v3_model.h and luviner_v3_model.a to your sketch folder #include "luviner_v3_model.h" void setup() { Serial.begin(115200); nc3_activate(0x1234567890ABCDEFULL); // your device UID nc3_init(); } void loop() { int16_t data[NC3_N_INPUTS]; data[0] = (int16_t)((analogRead(A0) * 256L) / 1023); data[1] = (int16_t)((analogRead(A1) * 256L) / 1023); int cls = nc3_predict(data); Serial.println(cls); delay(100); }

Streaming (time-series) inference

For time-series data (vibration monitoring, ECG, etc.), use nc3_stream_predict() instead of nc3_predict(). The model maintains internal state between calls, allowing it to detect temporal patterns across consecutive readings.

// Continuous monitoring loop nc3_stream_reset(); // start fresh while (1) { read_and_convert_sensors(inputs); // fill int16_t inputs[] int cls = nc3_stream_predict(inputs); if (cls != CLASS_NORMAL) { trigger_alert(cls); } sleep_ms(100); // 10 Hz sampling }
No windowing needed. Unlike traditional models that require collecting a window of N samples, Luviner processes each reading individually while retaining temporal context. This reduces latency and memory usage.

UID verification

The compiled library is bound to specific chip UIDs. Call nc3_activate(device_uid) at startup with your chip's unique ID (read from the MCU's UID register). If the UID is not in the authorized list, the function returns 0 and all inference functions will return -1.

To add more devices to your deployment, add their UIDs in the Luviner dashboard and recompile.

Reading the chip UID. Each MCU family has a specific register for the unique ID: STM32 uses 0x1FFF7A10, ESP32 uses esp_efuse_mac_get_default(), nRF uses NRF_FICR->DEVICEID. Consult your MCU datasheet for the exact location.
Pricing Contact Terms of Service Privacy Policy End User License Agreement

© 2026 Luviner. Edge AI for every device.

P.IVA / VAT ID: IT02880910340