Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.edgeimpulse.com/llms.txt

Use this file to discover all available pages before exploring further.

The base edge-impulse skill covers the Studio and Ingestion APIs: uploading data, triggering training, exporting deployments. It does not know anything about your target hardware, project structure, or how to wire the exported library into your application, unless you give it the context in a separate prompt. A companion skill helps fill that gap.

Why a companion skill

When you ask Export a C++ library from my Edge Impulse project, the agent downloads the archive. When you then ask it to write the code that calls run_classifier() on your STM32 using FreeRTOS, it can draw on general knowledge of the Edge Impulse SDK β€” but it has to make assumptions about your build system, directory layout, sensor drivers, and memory constraints, and those assumptions may not match your project. A companion skill gives the agent a persistent reference for the things it can’t infer: where you extracted the library, how your CMakeLists is structured, which sensor driver you’re using, any board-specific constraints or instructions, etc. You invoke it alongside or after the base skill, and the two together cover the full workflow from Studio to running inference on your specific target.
The examples on this page show Claude Code skill files (SKILL.md), but a companion skill is just an Agent Skill β€” plain markdown with name and description frontmatter that any compatible agent can load. The file content is the same regardless of agent; only the install path differs. See Create an Edge Impulse skill for the install paths for Claude Code, OpenAI Codex, GitHub Copilot, and Antigravity (previously Gemini CLI).

Add a companion skill for your target

Create a second skill file describing your target platform, or even custom deployment blocks, and how to integrate the Edge Impulse library into it:

Example: Custom Deployment Blocks

For example, for Claude Code create ~/.claude/skills/ei-custom-deployment-blocks/SKILL.md:
SKILL.md
---
name: ei-custom-deployment-blocks
description: Author Edge Impulse custom deployment blocks (Enterprise). Use when asked to scaffold, modify, test, or push a custom deployment block β€” including Dockerfile, parameters.json schema, the build script that reads deployment-metadata.json and produces deploy.zip, and the edge-impulse-blocks CLI workflow.
argument-hint: "<task description>"
allowed-tools: Bash Read Write
---

Help the user author an Edge Impulse custom deployment block. A deployment block is a Docker container that Studio runs to turn a trained impulse into a downloadable artifact (firmware, library, container image, etc.). Custom deployment blocks are an **Enterprise-only** feature.

## Required files

A deployment block directory contains:

- `Dockerfile` β€” builds the container Studio runs.
- `parameters.json` β€” block metadata + user-facing parameter definitions.
- Build script β€” `build.py` (Python) or `build.js` (Node.js). Entry point of the container.
- Optional `package.json` (Node.js) or `requirements.txt` (Python) for dependencies.
- Optional `app/` directory for static template files copied into the build.

## Block I/O contract

Studio runs the container with `--metadata <path>` pointing at `deployment-metadata.json`. The build script must:

1. Parse `--metadata` and load the JSON.
2. Read input from `metadata.folders.input`. The input directory contains:

       deployment-metadata.json
       edge-impulse-sdk/
       model-parameters/
       tflite-model/
       trained.h5.zip
       trained.savedmodel.zip
       trained.tflite

3. Write a single `deploy.zip` into `metadata.folders.output`.

Never hard-code paths β€” always read them from the metadata file. Input and output dirs are on network storage; copy what you need into a local `/tmp/build` directory before doing heavy work.

## parameters.json schema

```json
{
  "version": 1,
  "type": "deploy",
  "info": {
    "name": "My deploy block",
    "description": "Build a standalone Linux application",
    "category": "firmware",
    "mountLearnBlock": false,
    "supportsEonCompiler": true,
    "showOptimizations": true,
    "cliArguments": "",
    "privileged": false
  },
  "parameters": [
    {
      "name": "Build Option",
      "value": "option-a",
      "type": "select",
      "param": "build-option-1",
      "help": "Choose compilation target",
      "valid": ["option-a", "option-b", "option-c"]
    },
    {
      "name": "Advanced Setting",
      "value": "default",
      "type": "select",
      "param": "advanced-option",
      "valid": ["default", "custom"],
      "showIf": {
        "parameter": "build-option-1",
        "operator": "eq",
        "value": "option-a"
      }
    }
  ]
}
```

`info` flags:

- `mountLearnBlock` β€” mount the project's training data at `/data` in the container.
- `privileged` β€” give the container internet access and inject the project API key.
- `showOptimizations` β€” show the int8/float32 optimization picker in the UI.
- `supportsEonCompiler` β€” show the EON Compiler toggle in the UI.
- `cliArguments` β€” extra args appended to the container entrypoint.
- `category` β€” UI grouping (e.g. `firmware`, `library`).

Each entry in `parameters[]` becomes a UI control. The chosen value is passed to the build script as `--<param>`. Use `showIf` to display a parameter only when another parameter has a given value.

## Dockerfile skeleton (Python)

```dockerfile
FROM ubuntu:20.04
ARG DEBIAN_FRONTEND=noninteractive
WORKDIR /ei

RUN apt update && apt install -y build-essential python3 python3-pip wget

# COPY any app/ template files first
COPY app ./app

# COPY the build script and any deps
COPY *.py ./
# COPY requirements.txt ./ && pip3 install -r requirements.txt

ENTRYPOINT [ "python3", "-u", "build.py" ]
```

## Dockerfile skeleton (Node.js)

```dockerfile
FROM node:18
WORKDIR /ei

COPY app ./app
COPY package*.json ./
RUN npm ci --omit=dev
COPY *.js ./

ENTRYPOINT [ "node", "build.js" ]
```

## Build script skeleton (Python)

```python
import argparse, json, os, shutil

parser = argparse.ArgumentParser()
parser.add_argument('--metadata', type=str, required=True)
# Add one parser.add_argument per entry in parameters.json, e.g.:
# parser.add_argument('--build-option-1', type=str)
args = parser.parse_args()

with open(args.metadata) as f:
    metadata = json.load(f)

input_dir = metadata['folders']['input']
output_dir = metadata['folders']['output']
app_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app')

build_dir = '/tmp/build'
if os.path.exists(build_dir):
    shutil.rmtree(build_dir)
os.makedirs(build_dir)

os.system(f'cp -r {input_dir}/* {build_dir}')
os.system(f'cp -r {app_dir}/* {build_dir}')

# --- Build step goes here. Examples: invoke make, compile a binary,
#     run a code generator, package files. ---
os.chdir(build_dir)
os.system('make -j8')

os.makedirs(output_dir, exist_ok=True)
shutil.make_archive(os.path.join(output_dir, 'deploy'), 'zip', os.path.join(build_dir, 'build'))
```

## Build script skeleton (Node.js)

```javascript
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const { program } = require('commander');

program.requiredOption('--metadata <file>');
// .option('--build-option-1 <value>')
program.parse(process.argv);
const opts = program.opts();

const metadata = JSON.parse(fs.readFileSync(opts.metadata, 'utf8'));
const inputDir = metadata.folders.input;
const outputDir = metadata.folders.output;
const appDir = path.join(__dirname, 'app');

const buildDir = '/tmp/build';
fs.rmSync(buildDir, { recursive: true, force: true });
fs.mkdirSync(buildDir, { recursive: true });

execSync(`cp -r ${inputDir}/* ${buildDir}`);
if (fs.existsSync(appDir)) execSync(`cp -r ${appDir}/* ${buildDir}`);

// --- Build step goes here ---
execSync('make -j8', { cwd: buildDir });

fs.mkdirSync(outputDir, { recursive: true });
execSync(`cd ${path.join(buildDir, 'build')} && zip -r ${path.join(outputDir, 'deploy.zip')} .`);
```

## Local testing

Use the Edge Impulse block runner to download real project data and execute the block locally:

```bash
edge-impulse-blocks runner --extra-args "--build-option-1 option-a"
```

This creates `ei-block-data/` with `downloads/`, `input/`, and `output/` subdirectories.

To test the raw Docker image:

```bash
edge-impulse-blocks runner --download-data input/
docker build -t my-deploy-block .
docker run --rm -v $PWD:/home my-deploy-block --metadata /home/input/deployment-metadata.json
```

## Push workflow

```bash
edge-impulse-blocks init   # one-time: links this directory to an org block
edge-impulse-blocks push   # upload + build
```

After push, the block appears under the organization's **Custom blocks** and is selectable as a deployment target in any project.

## Reference

- Docs: https://docs.edgeimpulse.com/studio/organizations/custom-blocks/custom-deployment-blocks
- Example: https://github.com/edgeimpulse/example-custom-deployment-block
- Multi-impulse example: https://github.com/edgeimpulse/multi-impulse-deployment-block

## Instructions

1. Ask the user whether the build script should be **Python** or **Node.js** if not specified. Use the matching Dockerfile and build script skeleton.
2. Ask what the block actually does (compile firmware, package model files, generate language bindings, build a container image, etc.) so the build step in the script is meaningful β€” do not leave a placeholder `make` call without confirming it fits.
3. Only add entries to `parameters[]` if the user described user-facing options. Otherwise leave `parameters` empty or omitted.
4. For every parameter in `parameters.json`, add a matching `parser.add_argument` (Python) or `.option` (Node.js) in the build script.
5. Always read `metadata.folders.input` and `metadata.folders.output` from the metadata file. Never hard-code paths.
6. Always write the final artifact as `deploy.zip` into `folders.output`.
7. Set `privileged: true` only if the build genuinely needs internet access or the project API key β€” it is off by default.
8. Set `mountLearnBlock: true` only if the build needs the raw training data at `/data`.
9. Recommend the user test locally with `edge-impulse-blocks runner` before pushing.
10. After scaffolding, tell the user to run `edge-impulse-blocks init` (once) then `edge-impulse-blocks push` to upload. Remind them that custom deployment blocks require an **Enterprise** plan.

Example: Arduino

For example, for Claude Code create ~/.claude/skills/ei-arduino/SKILL.md:
SKILL.md
---
name: ei-arduino
description: Write Arduino application code that uses a downloaded Edge Impulse library. Use when asked to write a sketch, integrate an Edge Impulse model into Arduino code, or run inference on an Arduino board.
argument-hint: "<task description>"
allowed-tools: Bash Read Write
---

Write Arduino sketches that use a downloaded Edge Impulse Arduino library.

## Library structure

The downloaded .zip contains an Arduino library. The main header follows the pattern:

    #include <PROJECT_SLUG_inferencing.h>

where PROJECT_SLUG is derived from the project name. Find the exact header name
by listing the extracted library directory:

    find ./build -name '*_inferencing.h' 2>/dev/null | head -1

Use whatever filename that command returns β€” do not guess or hardcode it.

## Inference pattern

    #include <PROJECT_SLUG_inferencing.h>   // use the actual filename found above

    float features[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE];

    int raw_feature_get_data(size_t offset, size_t length, float *out_ptr) {
        memcpy(out_ptr, features + offset, length * sizeof(float));
        return 0;
    }

    void loop() {
        // Fill features[] with sensor readings here

        signal_t signal;
        numpy::signal_from_buffer(features, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);

        ei_impulse_result_t result;
        EI_IMPULSE_ERROR err = run_classifier(&signal, &result, false);
        if (err != EI_IMPULSE_OK) {
            Serial.print("run_classifier failed: "); Serial.println(err);
            return;
        }

        for (size_t i = 0; i < EI_CLASSIFIER_LABEL_COUNT; i++) {
            Serial.print(result.classification[i].label);
            Serial.print(": ");
            Serial.println(result.classification[i].value, 4);
        }
    }

## Key constants defined by the library

- EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE β€” total floats needed for one inference window
- EI_CLASSIFIER_LABEL_COUNT β€” number of output classes
- EI_CLASSIFIER_INTERVAL_MS β€” sampling interval in milliseconds

## Instructions

1. Before writing any code, run `find ./build -name '*_inferencing.h' 2>/dev/null | head -1`
   to find the exact header filename. Use that name in the #include β€” do not guess.
2. Use EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE for buffer sizing β€” never hardcode the number.
3. Collect sensor data at EI_CLASSIFIER_INTERVAL_MS intervals.
4. Ask the user which sensor they are using (IMU, microphone, or camera) if not specified.
5. Use the Arduino Wire, SPI, or sensor-specific libraries appropriate for the board.
Then invoke both skills together:

Export my Edge Impulse project as an Arduino library to ./build, then write a sketch for the Arduino Nano 33 BLE Sense that reads the IMU and runs inference, printing the top label to Serial

Example: Arduino UNO Q with App Lab

The Arduino UNO Q runs Edge Impulse models as .eim files through Arduino App Lab. Unlike the classic Arduino or Linux C++ workflows, you don’t write inference code directly β€” you configure an app.yaml that assembles pre-built bricks (modular components) and points each one at your model file. A companion skill gives the agent the brick names, model variable names, and app.yaml structure so it can write correct configuration files without guessing. For example, for Claude Code create ~/.claude/skills/ei-app-lab/SKILL.md:
SKILL.md
---
name: ei-app-lab
description: Write Arduino App Lab app.yaml configuration files for the Arduino UNO Q. Use when asked to create or modify an App Lab application, wire an Edge Impulse .eim model into App Lab bricks, or build a UNO Q application around a trained Edge Impulse model.
argument-hint: "<task description>"
allowed-tools: Bash Read Write
---

Write Arduino App Lab application configuration for the Arduino UNO Q.

## Deployment format

Models for the UNO Q are exported from Edge Impulse Studio as .eim files.
In Studio, go to Deployment and select `Arduino UNO Q (GPU)` as the target.
Place the downloaded .eim file at:

    /home/arduino/.arduino-bricks/ei-models/<model-name>.eim

## app.yaml structure

    name: My Application
    description: "Short description"
    ports: []
    bricks:
      - arduino:<brick-name>:
          variables:
            <MODEL_VARIABLE>: /home/arduino/.arduino-bricks/ei-models/<model-name>.eim
      - arduino:web_ui: {}
    icon: πŸ€–

## Bricks and model variables

Object detection:     brick arduino:video_object_detection   variable EI_OBJ_DETECTION_MODEL
Image classification: brick arduino:video_classification     variable EI_CLASSIFICATION_MODEL
Visual anomaly:       brick arduino:visual_anomaly_detection variable EI_V_ANOMALY_DETECTION_MODEL
Keyword spotting:     brick arduino:keyword_spotting         variable EI_KEYWORD_SPOTTING_MODEL
Audio classification: brick arduino:audio_classification     variable EI_AUDIO_CLASSIFICATION_MODEL
Motion / IMU:         brick arduino:motion_detection         variable EI_MOTION_DETECTION_MODEL

Always include `arduino:web_ui: {}` to expose the application UI on port 7000.

## Example: object detection app

    name: Rubber Duck Detector
    description: "Detects rubber ducks using a FOMO object detection model"
    ports: []
    bricks:
      - arduino:video_object_detection:
          variables:
            EI_OBJ_DETECTION_MODEL: /home/arduino/.arduino-bricks/ei-models/rubber-ducks.eim
      - arduino:web_ui: {}
    icon: πŸ¦†

## Instructions

1. Ask the user what type of model they have (object detection, classification, keyword spotting, etc.) if not specified.
2. Choose the correct brick and model variable from the table above for that model type.
3. Always include `arduino:web_ui: {}` unless the user says they do not want a web UI.
4. Use the full path /home/arduino/.arduino-bricks/ei-models/<name>.eim for model references.
5. Do not write C++ or Python inference code β€” App Lab handles inference through bricks.
6. After writing the app.yaml, tell the user to open App Lab, select the app, and click deploy. The UI will be available at http://<board-ip>:7000.
Then invoke both skills in sequence:

Export the deployment for Arduino UNO Q (GPU) from my Edge Impulse project and save the .eim file to ./build then write an app.yaml for Arduino App Lab object detection using the model at ./build

Example: Linux C++

The Linux SDK runs on ARM64 and x86-64 Linux boards β€” Raspberry Pi, NVIDIA Jetson, and similar SBCs. This skill covers the C++ library path. For the Python runner see the Python Linux SDK. For example, for Claude Code create ~/.claude/skills/ei-linux/SKILL.md:
SKILL.md
---
name: ei-linux
description: Write C++ application code that uses a downloaded Edge Impulse C++ library on Linux. Use when asked to write a Linux runner, integrate the Edge Impulse SDK into a CMake project, or run inference on a Raspberry Pi or Jetson.
argument-hint: "<task description>"
allowed-tools: Bash Read Write
---

Write C++ applications that link against a downloaded Edge Impulse C++ library on Linux.

## Library layout

After extracting the .zip, the structure is:

    edge-impulse-sdk/
      edge-impulse-sdk/   # SDK source
      model-parameters/   # model weights and config
      tflite-model/       # TFLite flatbuffer
      CMakeLists.txt      # top-level build file

## Minimal CMakeLists.txt

    cmake_minimum_required(VERSION 3.13)
    project(ei_app)
    set(CMAKE_CXX_STANDARD 11)
    add_subdirectory(edge-impulse-sdk)
    add_executable(ei_app main.cpp)
    target_link_libraries(ei_app ei_sdk)

## Inference pattern

    #include "edge-impulse-sdk/classifier/ei_run_classifier.h"

    static float features[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE];

    int raw_feature_get_data(size_t offset, size_t length, float *out_ptr) {
        memcpy(out_ptr, features + offset, length * sizeof(float));
        return 0;
    }

    int main() {
        signal_t signal;
        signal.total_length = EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE;
        signal.get_data = &raw_feature_get_data;

        ei_impulse_result_t result;
        EI_IMPULSE_ERROR err = run_classifier(&signal, &result, false);
        if (err != EI_IMPULSE_OK) {
            printf("run_classifier failed: %d\n", err);
            return 1;
        }

        for (size_t i = 0; i < EI_CLASSIFIER_LABEL_COUNT; i++) {
            printf("%s: %.4f\n", result.classification[i].label,
                                 result.classification[i].value);
        }
        return 0;
    }

## Build commands

    mkdir -p build && cd build
    cmake .. -DCMAKE_BUILD_TYPE=Release
    make -j$(nproc)

## Instructions

1. Assume the library is extracted at ./build/edge-impulse-sdk. Do not re-download it.
2. Always use EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE for buffer sizing.
3. Ask the user what input source they are using (CSV file, live camera, microphone, etc.).
4. For camera input, use OpenCV to capture frames and convert to the expected format.
5. Target ARM64 by default unless the user specifies otherwise.

Generate a companion skill with an AI agent

You don’t have to write companion skill files by hand. Ask your AI agent to create one based on your target hardware β€” it has general knowledge of SDK patterns, build systems, and integration details for many common platforms, though coverage varies.

Create one of the examples from this page

Create the file ~/.claude/skills/ei-linux/SKILL.md with the ei-linux companion skill exactly as described at https://docs.edgeimpulse.com/tutorials/topics/ai-agents/create-a-companion-skill

Create the file ~/.claude/skills/ei-app-lab/SKILL.md with the ei-app-lab companion skill for the Arduino UNO Q with App Lab, as described at https://docs.edgeimpulse.com/tutorials/topics/ai-agents/create-a-companion-skill

Generate a skill for your specific hardware

Describe your target and let Claude write a custom skill tailored to it:

Create a Claude Code skill file at ~/.claude/skills/ei-stm32/SKILL.md that helps write Edge Impulse inference code for the STM32H7 using STM32CubeIDE and FreeRTOS. The sensor is an IIS2DH accelerometer on SPI. Include the correct include paths for the Edge Impulse C++ library, the ISR-safe buffer pattern for collecting samples, and the FreeRTOS task structure for running inference.

Create a Claude Code skill file at ~/.claude/skills/ei-rpi-python/SKILL.md for running Edge Impulse .eim models on a Raspberry Pi 5 using the Python SDK. Include how to install the SDK, the inference loop pattern using AudioImpulseRunner or ImpulseRunner, and how to read from a USB microphone with PyAudio.

Create a Claude Code skill file at ~/.claude/skills/ei-zephyr/SKILL.md for integrating an Edge Impulse Zephyr module into a nRF5340 project using the nRF Connect SDK. Cover the CMakeLists additions, Kconfig options, and the sample main.c inference loop pattern.

Add project context with CLAUDE.md

For a project you return to repeatedly, put a CLAUDE.md in your repository root. Claude Code loads it automatically with every session β€” no skill invocation needed. A good CLAUDE.md for an Edge Impulse project includes instructions like:
CLAUDE.md
# Project context

## Hardware target
Raspberry Pi 4 (ARM64, Linux). The Edge Impulse C++ library is extracted at ./ei-sdk.

## Build system
CMake. Build directory is ./build. Run `cmake .. && make -j4` from ./build.

## Sensor
USB webcam via OpenCV. Capture at 96Γ—96, convert to RGB float normalized to [0, 1].

## Project layout
src/main.cpp       β€” inference loop
src/camera.cpp     β€” OpenCV capture helpers
include/           β€” project headers
ei-sdk/            β€” extracted Edge Impulse C++ library (do not modify)
data/              β€” sample images for testing

## Edge Impulse project
Project ID: 12345. API key is in EI_API_KEY env var.
Labels: person, no_person.
With this in place, you can run Retrain and re-export the C++ library for my Edge Impulse project and the agent will know where to put the output, without needing to re-prompt the project structure.

Extend the skill for a specific project

If you work on one project exclusively, edit the skill file itself to add project-specific context at the bottom. Anything you repeat across sessions β€” board type, sensor wiring, label names, file paths β€” belongs in the skill. For example, append to ~/.claude/skills/edge-impulse/SKILL.md for a gesture classifier running on the Arduino Nano 33 BLE Sense:
## Project-specific context

Project ID: 12345 (Gesture Classifier)
Target: Arduino Nano 33 BLE Sense
Sensor: LSM9DS1 IMU, 3-axis accelerometer at 62.5 Hz
Labels: idle, wave, punch
Library location after export: ./build/arduino-library.zip
The agent will use this context automatically whenever you invoke /edge-impulse or use an Edge Impulse specific prompt, without you having to repeat the information every time.

What to include in a companion skill

What to documentWhy it helps
Board or SoC name and architectureCorrect compiler flags, memory model
Sensor type and wiringCorrect driver library and data format
Buffer sizing strategyAvoids hardcoded numbers that break when the model changes
Build system and directory layoutThe agent can build and run without asking
Label names and their meaningBetter variable names and output formatting
Any existing integration codeThe agent extends rather than rewrites
Constraints (RAM, flash, RTOS)The agent avoids patterns that won’t fit

Next steps

  • Browse the prompt library for ready-to-use prompts covering data management, training, evaluation, deployment, and application code
  • Browse deployment targets for the full list of supported export formats
  • See the Linux SDK and Python SDK for running .eim models on Linux boards
  • Browse the hardware directory for setup guides for supported boards
  • See EON Tuner if the model is too large for your target after export