|
| 1 | +# ExecuTorch Arduino Library |
| 2 | + |
| 3 | +Run PyTorch models on Arduino microcontrollers using ExecuTorch. |
| 4 | + |
| 5 | +This directory contains everything needed to package ExecuTorch as an |
| 6 | +Arduino library. A build script vendors the runtime sources from this |
| 7 | +repository into a self-contained library that Arduino users install |
| 8 | +through the Library Manager or by copying into their libraries folder. |
| 9 | + |
| 10 | +## How It Works |
| 11 | + |
| 12 | +``` |
| 13 | +PyTorch Model ──► torch.export ──► .pte file ──► model.h (C array) |
| 14 | + │ |
| 15 | + Arduino Sketch (.ino) |
| 16 | + #include <ExecuTorchArduino.h> |
| 17 | + #include "model.h" |
| 18 | + │ |
| 19 | + arduino-cli compile ──► Upload ──► Runs on board |
| 20 | +``` |
| 21 | + |
| 22 | +### The three pieces |
| 23 | + |
| 24 | +1. **The library** (`arduino_lib/ExecuTorchArduino/`) — the ExecuTorch |
| 25 | + runtime, CMSIS-NN kernels, and portable ops packaged for the Arduino |
| 26 | + build system. Generated by `build_arduino_library.sh`; not checked in. |
| 27 | + |
| 28 | +2. **The model** (`model.h`) — a `.pte` file converted to a C byte array. |
| 29 | + Each user brings their own model, exported from PyTorch with the |
| 30 | + Cortex-M backend. |
| 31 | + |
| 32 | +3. **The sketch** (`.ino`) — a standard Arduino program that loads the |
| 33 | + model, feeds it input, and reads the output. Uses the native |
| 34 | + ExecuTorch C++ API (`Program::load`, `Method::execute`, etc.). |
| 35 | + |
| 36 | +## Supported Boards |
| 37 | + |
| 38 | +| Board | MCU | Status | |
| 39 | +|-------|-----|--------| |
| 40 | +| Arduino Uno Q | STM32U585 (Cortex-M33) | Tested | |
| 41 | +| Arduino Nano 33 BLE | nRF52840 (Cortex-M4F) | Untested | |
| 42 | +| Arduino Giga R1 WiFi | STM32H747 (Cortex-M7) | Untested | |
| 43 | +| Arduino Portenta H7 | STM32H747 (Cortex-M7) | Untested | |
| 44 | + |
| 45 | +CMSIS-NN accelerated ops work on any board with an ARM Cortex-M processor |
| 46 | +with DSP extensions. Portable ops work on any architecture. |
| 47 | + |
| 48 | +## Quick Start |
| 49 | + |
| 50 | +### 1. Build the Arduino library |
| 51 | + |
| 52 | +```bash |
| 53 | +cd examples/arduino |
| 54 | +./build_arduino_library.sh |
| 55 | +``` |
| 56 | + |
| 57 | +This copies the required ExecuTorch sources from the repository into |
| 58 | +`arduino_lib/ExecuTorchArduino/`, ready for Arduino. |
| 59 | + |
| 60 | +### 2. Install the library |
| 61 | + |
| 62 | +Copy the generated library into your Arduino libraries folder: |
| 63 | + |
| 64 | +```bash |
| 65 | +cp -r arduino_lib/ExecuTorchArduino ~/Arduino/libraries/ |
| 66 | +``` |
| 67 | + |
| 68 | +Or with `arduino-cli`: |
| 69 | + |
| 70 | +```bash |
| 71 | +arduino-cli lib install --zip-path arduino_lib/ExecuTorchArduino |
| 72 | +``` |
| 73 | + |
| 74 | +### 3. Export a model |
| 75 | + |
| 76 | +Export a PyTorch model to `.pte` format with Cortex-M quantization, then |
| 77 | +convert to a C header: |
| 78 | + |
| 79 | +```bash |
| 80 | +python export_model.py --model my_model.pt --target cortex-m33 --output model.h |
| 81 | +``` |
| 82 | + |
| 83 | +Or use one of the pre-exported models in the `examples/` directory. |
| 84 | + |
| 85 | +### 4. Write a sketch |
| 86 | + |
| 87 | +```cpp |
| 88 | +#include <ExecuTorchArduino.h> |
| 89 | +#include "model.h" |
| 90 | + |
| 91 | +using executorch::extension::BufferDataLoader; |
| 92 | +using executorch::runtime::Error; |
| 93 | +using executorch::runtime::HierarchicalAllocator; |
| 94 | +using executorch::runtime::MemoryAllocator; |
| 95 | +using executorch::runtime::MemoryManager; |
| 96 | +using executorch::runtime::Method; |
| 97 | +using executorch::runtime::MethodMeta; |
| 98 | +using executorch::runtime::Program; |
| 99 | +using executorch::runtime::Result; |
| 100 | +using executorch::runtime::Span; |
| 101 | + |
| 102 | +alignas(16) uint8_t method_pool[64 * 1024]; |
| 103 | +alignas(16) uint8_t temp_pool[8 * 1024]; |
| 104 | + |
| 105 | +void setup() { |
| 106 | + Serial.begin(115200); |
| 107 | + delay(2000); |
| 108 | + |
| 109 | + executorch::runtime::runtime_init(); |
| 110 | + |
| 111 | + auto loader = BufferDataLoader(model_pte, model_pte_size); |
| 112 | + Result<Program> program = Program::load(&loader); |
| 113 | + if (!program.ok()) { |
| 114 | + Serial.println("Failed to load program"); |
| 115 | + return; |
| 116 | + } |
| 117 | + |
| 118 | + // ... load method, set inputs, execute, read outputs |
| 119 | + // See examples/ for complete working sketches. |
| 120 | +} |
| 121 | + |
| 122 | +void loop() { |
| 123 | + // Run inference periodically |
| 124 | + delay(2000); |
| 125 | +} |
| 126 | +``` |
| 127 | + |
| 128 | +The sketch uses the **native ExecuTorch C++ API** — the same API used on |
| 129 | +Linux, Android, and bare-metal targets. No wrapper layer, no |
| 130 | +Arduino-specific abstractions. |
| 131 | + |
| 132 | +### 5. Compile and upload |
| 133 | + |
| 134 | +```bash |
| 135 | +arduino-cli compile --fqbn arduino:zephyr:unoq MySketch |
| 136 | +arduino-cli upload --fqbn arduino:zephyr:unoq -p /dev/ttyUSB0 MySketch |
| 137 | +``` |
| 138 | + |
| 139 | +## What is inside the library |
| 140 | + |
| 141 | +The `build_arduino_library.sh` script assembles these components from |
| 142 | +the ExecuTorch repository: |
| 143 | + |
| 144 | +| Component | Source in repo | Purpose | |
| 145 | +|-----------|---------------|---------| |
| 146 | +| ET Runtime | `runtime/executor/`, `runtime/core/`, `runtime/kernel/`, `runtime/platform/` | Model loading, memory management, op dispatch | |
| 147 | +| Portable Ops | `kernels/portable/` | Software op implementations (any CPU) | |
| 148 | +| Cortex-M Ops | `backends/cortex_m/ops/` | CMSIS-NN accelerated int8 ops | |
| 149 | +| CMSIS-NN | fetched by cmake / Zephyr module | ARM's optimized DSP kernels | |
| 150 | +| flatcc | `third-party/flatcc/` | .pte file parsing | |
| 151 | +| flatbuffers | `third-party/flatbuffers/` | Schema headers | |
| 152 | +| c10 | `runtime/core/portable_type/c10/` | Core type definitions | |
| 153 | + |
| 154 | +The library uses no external dependencies beyond what the Arduino board |
| 155 | +core provides. |
| 156 | + |
| 157 | +## Arduino-specific patches |
| 158 | + |
| 159 | +The build script applies these patches to make ExecuTorch compile under |
| 160 | +Arduino's build system: |
| 161 | + |
| 162 | +1. **`#include <exception>` before `<variant>`** — Arduino's custom |
| 163 | + `<new>` header omits `<exception>`, breaking `std::bad_variant_access`. |
| 164 | + |
| 165 | +2. **`cmake_macros.h` stub** — c10/torch headers expect a cmake-generated |
| 166 | + file. The stub defines `C10_USING_CUSTOM_GENERATED_MACROS`. |
| 167 | + |
| 168 | +3. **`platform_stubs.c`** — provides `_Exit()` and `fprintf()` for the |
| 169 | + LLEXT environment on boards that lack them. |
| 170 | + |
| 171 | +## Development |
| 172 | + |
| 173 | +### Updating the library |
| 174 | + |
| 175 | +After modifying ExecuTorch sources, regenerate the library: |
| 176 | + |
| 177 | +```bash |
| 178 | +./build_arduino_library.sh # rebuild |
| 179 | +./build_arduino_library.sh --clean # remove generated output |
| 180 | +``` |
| 181 | + |
| 182 | +### Testing |
| 183 | + |
| 184 | +```bash |
| 185 | +arduino-cli compile --fqbn arduino:zephyr:unoq examples/HelloExecuTorch |
| 186 | +arduino-cli upload --fqbn arduino:zephyr:unoq -p /dev/ttyUSB0 examples/HelloExecuTorch |
| 187 | +arduino-cli monitor -p /dev/ttyUSB0 --config baudrate=115200 |
| 188 | +``` |
| 189 | + |
| 190 | +### Publishing to Arduino Library Manager |
| 191 | + |
| 192 | +The library is published by adding its repository URL to the |
| 193 | +[Arduino Library Registry](https://github.com/arduino/library-registry). |
| 194 | +After the initial registration, new git tags are picked up automatically. |
| 195 | + |
| 196 | +## Build Validation |
| 197 | + |
| 198 | +Tested on Arduino Uno Q (STM32U585, Cortex-M33 @ 160 MHz): |
| 199 | + |
| 200 | +- **390+ source files** compile with zero errors |
| 201 | +- **Flash**: 106 KB used (13% of 786 KB) |
| 202 | +- **RAM**: 91 KB used (69% of 131 KB) |
| 203 | +- **Model**: DS-CNN keyword spotting, int8 quantized via CMSIS-NN, 52.6 KB |
0 commit comments