Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/arduino/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Generated by build_arduino_library.sh — do not check in
arduino_lib/
24 changes: 24 additions & 0 deletions examples/arduino/ExecuTorchArduino.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

// Arduino's custom <new> header omits <exception>, which breaks
// std::bad_variant_access in <variant>. Include it first.
#include <exception>

#define C10_USING_CUSTOM_GENERATED_MACROS
#define ET_ENABLE_DEPRECATED_CONSTANT_BUFFER 0
#define FLATBUFFERS_MAX_ALIGNMENT 1024

#include <executorch/runtime/platform/runtime.h>
#include <executorch/runtime/executor/program.h>
#include <executorch/runtime/executor/method.h>
#include <executorch/runtime/core/memory_allocator.h>
#include <executorch/runtime/executor/method_meta.h>
#include <executorch/extension/data_loader/buffer_data_loader.h>
211 changes: 211 additions & 0 deletions examples/arduino/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
<!---
Copyright (c) Meta Platforms, Inc. and affiliates.
All rights reserved.

This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree.
--->

# ExecuTorch Arduino Library

Run PyTorch models on Arduino microcontrollers using ExecuTorch.

This directory contains everything needed to package ExecuTorch as an
Arduino library. A build script vendors the runtime sources from this
repository into a self-contained library that Arduino users install
through the Library Manager or by copying into their libraries folder.

## How It Works

```
PyTorch Model ──► torch.export ──► .pte file ──► model.h (C array)
Arduino Sketch (.ino)
#include <ExecuTorchArduino.h>
#include "model.h"
arduino-cli compile ──► Upload ──► Runs on board
```

### The three pieces

1. **The library** (`arduino_lib/ExecuTorchArduino/`) — the ExecuTorch
runtime, CMSIS-NN kernels, and portable ops packaged for the Arduino
build system. Generated by `build_arduino_library.sh`; not checked in.

2. **The model** (`model.h`) — a `.pte` file converted to a C byte array.
Each user brings their own model, exported from PyTorch with the
Cortex-M backend.

3. **The sketch** (`.ino`) — a standard Arduino program that loads the
model, feeds it input, and reads the output. Uses the native
ExecuTorch C++ API (`Program::load`, `Method::execute`, etc.).

## Supported Boards

| Board | MCU | Status |
|-------|-----|--------|
| Arduino Uno Q | STM32U585 (Cortex-M33) | Tested |
| Arduino Nano 33 BLE | nRF52840 (Cortex-M4F) | Untested |
| Arduino Giga R1 WiFi | STM32H747 (Cortex-M7) | Untested |
| Arduino Portenta H7 | STM32H747 (Cortex-M7) | Untested |

CMSIS-NN accelerated ops work on any board with an ARM Cortex-M processor
with DSP extensions. Portable ops work on any architecture.

## Quick Start

### 1. Build the Arduino library

```bash
cd examples/arduino
./build_arduino_library.sh
```

This copies the required ExecuTorch sources from the repository into
`arduino_lib/ExecuTorchArduino/`, ready for Arduino.

### 2. Install the library

Copy the generated library into your Arduino libraries folder:

```bash
cp -r arduino_lib/ExecuTorchArduino ~/Arduino/libraries/
```

Or with `arduino-cli`:

```bash
arduino-cli lib install --zip-path arduino_lib/ExecuTorchArduino
```

### 3. Export a model

Export a PyTorch model to `.pte` format with Cortex-M quantization, then
convert to a C header:

```bash
python export_model.py --model my_model.pt --target cortex-m33 --output model.h
```

Or use one of the pre-exported models in the `examples/` directory.

### 4. Write a sketch

```cpp
#include <ExecuTorchArduino.h>
#include "model.h"

using executorch::extension::BufferDataLoader;
using executorch::runtime::Error;
using executorch::runtime::HierarchicalAllocator;
using executorch::runtime::MemoryAllocator;
using executorch::runtime::MemoryManager;
using executorch::runtime::Method;
using executorch::runtime::MethodMeta;
using executorch::runtime::Program;
using executorch::runtime::Result;
using executorch::runtime::Span;

alignas(16) uint8_t method_pool[64 * 1024];
alignas(16) uint8_t temp_pool[8 * 1024];

void setup() {
Serial.begin(115200);
delay(2000);

executorch::runtime::runtime_init();

auto loader = BufferDataLoader(model_pte, model_pte_size);
Result<Program> program = Program::load(&loader);
if (!program.ok()) {
Serial.println("Failed to load program");
return;
}

// ... load method, set inputs, execute, read outputs
// See examples/ for complete working sketches.
}

void loop() {
// Run inference periodically
delay(2000);
}
```

The sketch uses the **native ExecuTorch C++ API** — the same API used on
Linux, Android, and bare-metal targets. No wrapper layer, no
Arduino-specific abstractions.

### 5. Compile and upload

```bash
arduino-cli compile --fqbn arduino:zephyr:unoq MySketch
arduino-cli upload --fqbn arduino:zephyr:unoq -p /dev/ttyUSB0 MySketch
```

## What is inside the library

The `build_arduino_library.sh` script assembles these components from
the ExecuTorch repository:

| Component | Source in repo | Purpose |
|-----------|---------------|---------|
| ET Runtime | `runtime/executor/`, `runtime/core/`, `runtime/kernel/`, `runtime/platform/` | Model loading, memory management, op dispatch |
| Portable Ops | `kernels/portable/` | Software op implementations (any CPU) |
| Cortex-M Ops | `backends/cortex_m/ops/` | CMSIS-NN accelerated int8 ops |
| CMSIS-NN | fetched by cmake / Zephyr module | ARM's optimized DSP kernels |
| flatcc | `third-party/flatcc/` | .pte file parsing |
| flatbuffers | `third-party/flatbuffers/` | Schema headers |
| c10 | `runtime/core/portable_type/c10/` | Core type definitions |

The library uses no external dependencies beyond what the Arduino board
core provides.

## Arduino-specific patches

The build script applies these patches to make ExecuTorch compile under
Arduino's build system:

1. **`#include <exception>` before `<variant>`** — Arduino's custom
`<new>` header omits `<exception>`, breaking `std::bad_variant_access`.

2. **`cmake_macros.h` stub** — c10/torch headers expect a cmake-generated
file. The stub defines `C10_USING_CUSTOM_GENERATED_MACROS`.

Comment on lines +165 to +167
3. **`platform_stubs.c`** — provides `_Exit()` and `fprintf()` for the
LLEXT environment on boards that lack them.

## Development

### Updating the library

After modifying ExecuTorch sources, regenerate the library:

```bash
./build_arduino_library.sh # rebuild
./build_arduino_library.sh --clean # remove generated output
```

### Testing

```bash
arduino-cli compile --fqbn arduino:zephyr:unoq examples/HelloExecuTorch
arduino-cli upload --fqbn arduino:zephyr:unoq -p /dev/ttyUSB0 examples/HelloExecuTorch
arduino-cli monitor -p /dev/ttyUSB0 --config baudrate=115200
```

### Publishing to Arduino Library Manager

The library is published by adding its repository URL to the
[Arduino Library Registry](https://github.com/arduino/library-registry).
After the initial registration, new git tags are picked up automatically.

## Build Validation

Tested on Arduino Uno Q (STM32U585, Cortex-M33 @ 160 MHz):

- **390+ source files** compile with zero errors
- **Flash**: 106 KB used (13% of 786 KB)
- **RAM**: 91 KB used (69% of 131 KB)
Comment on lines +200 to +202
- **Model**: DS-CNN keyword spotting, int8 quantized via CMSIS-NN, 52.6 KB
Loading
Loading