Add Arduino library support for ExecuTorch#20221
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20221
Note: Links to docs will display an error until the docs builds have been completed. ❌ 7 New Failures, 4 Unrelated FailuresAs of commit 71df531 with merge base 63b4c4d ( NEW FAILURES - The following jobs have failed:
BROKEN TRUNK - The following jobs failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a
|
There was a problem hiding this comment.
Pull request overview
This PR adds tooling and example code to package ExecuTorch as an installable Arduino library, with support for building/running on Arduino-class microcontrollers (including a Zephyr-based Arduino Uno Q configuration).
Changes:
- Add
build_arduino_library.shto assemble a self-contained Arduino library by copying ExecuTorch sources + required third-party headers. - Introduce Arduino library metadata/header glue (
library.properties,ExecuTorchArduino.h) plus C-library stubs for the target environment. - Add Arduino example sketches and a Zephyr board config tuned for Uno Q.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| zephyr/samples/hello-executorch/boards/arduino_uno_q.conf | Adds Uno Q memory sizing defaults for ExecuTorch allocators/stack/heap. |
| examples/arduino/README.md | Documents the Arduino library packaging flow and usage. |
| examples/arduino/platform_stubs.c | Adds libc symbol stubs intended for Zephyr LLEXT-style environments. |
| examples/arduino/library.properties | Defines Arduino library metadata (name/version/architectures/includes). |
| examples/arduino/ExecuTorchArduino.h | Provides Arduino-specific compile configuration and exposes the native ExecuTorch C++ API. |
| examples/arduino/examples/KeywordSpotting/KeywordSpotting.ino | Adds a DS-CNN keyword spotting sketch using the native ExecuTorch API. |
| examples/arduino/examples/HelloExecuTorch/HelloExecuTorch.ino | Adds a minimal “runtime_init()” sanity-check sketch. |
| examples/arduino/build_arduino_library.sh | Adds the library assembly script (copies sources, generates stubs, applies patches). |
| examples/arduino/.gitignore | Ignores generated Arduino library output directory. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
7e528e1 to
771e65c
Compare
|
@claude review this PR |
|
Claude finished @rascani's task in 3m 29s —— View job Code Review: Arduino library support for ExecuTorch
Nice work — this is a substantial and well-organized contribution, and many of the earlier Copilot comments have already been addressed in the current state (weak stubs, schema fail-fast, Notable1. README "selective CMSIS-NN inclusion" contradicts the build script ( The README states:
But the script does
Given the LLEXT budget table claims the "all CMSIS-NN" build (269 KB) is too large and the "selective" build (112 KB) fits, this matters: as written, the packaged library copies everything, so a user following these steps may not get the size profile the table promises. Worth reconciling the script and the docs. 2. Examples won't compile out-of-the-box — All three example sketches Minor / robustness3. The sketch checks 4. Acceptable as a weak last-resort stub, but worth a one-line comment noting it intentionally discards output so a future debugger isn't confused why logging vanished on boards that fall through to it. 5. README ↔ PR-description number mismatch ( The PR body reports "106 KB flash (13%), 91 KB RAM"; the README's LLEXT table reports the DS-CNN selective build as 87 KB code + 25 KB data = 112 KB into a 131 KB budget. These describe different things (LLEXT partition vs flash/RAM), but a reader can't tell. A one-line note clarifying that the budget is the LLEXT region, not total STM32U585 flash/SRAM, would resolve the recurring confusion the earlier reviews flagged. 6.
Things I checked that look correct now
Fix model.h + CMSIS-NN doc issues → |
|
@claude re-review the PR |
|
Claude finished @psiddh's task in 1m 25s —— View job Re-review: Arduino library support for ExecuTorch
This is in good shape now — the vast majority of the earlier Copilot and review feedback has been addressed in the current tree. I re-verified each prior concern against the code and only one genuine (cosmetic) bug remains, plus a couple of optional polish items. Confirmed fixed since the last round
Remaining (one real, minor)1. Duplicated paragraph in README ( Optional polish (non-blocking)
Nice work iterating on this — it's a clean, self-contained contribution and the runtime-robustness concerns from the earlier passes are all closed out. Nothing here blocks merge beyond the one-line README dedup. |
|
@claude re review this PR one more time |
|
Claude finished @psiddh's task in 1m 13s —— View job Re-review: Arduino library support for ExecuTorch
I re-verified the full diff against Confirmed fixed since last review
Remaining (optional, non-blocking)
That's the only outstanding item, and it's an enhancement rather than a defect. This is a clean, self-contained, well-iterated contribution — LGTM from my side. 🚀 |
|
For Library Manager publication, we'll check in pre-generated model.h files (both AddModel and KeywordSpotting) in the dedicated meta-pytorch/executorch-arduino repo — that repo is a build artifact, so generated blobs are fine there. The main ET repo stays clean of generated headers. Both examples will compile out of the box for Library Manager users. |
| // To generate model.h from a PyTorch model: | ||
| // python -c " | ||
| // import torch | ||
| // from executorch.exir import to_edge | ||
| // from torch.export import export | ||
| // class AddModel(torch.nn.Module): | ||
| // def forward(self, x): return x + 1.0 | ||
| // et = to_edge(export(AddModel().eval(), (torch.tensor([1.,2.,3.]),))).to_executorch() | ||
| // open('model.h','w').write( | ||
| // '#pragma once\n#include <cstdint>\n#include <cstddef>\n' | ||
| // + 'alignas(16) static const uint8_t model_pte[] = {' | ||
| // + ','.join(f'0x{b:02x}' for b in et.buffer) + '};\n' | ||
| // + f'static const size_t model_pte_size = {len(et.buffer)};\n') | ||
| // " |
There was a problem hiding this comment.
Do we not have a script capable of generating this? If not, we should. It is a very common approach in firmware. One day we should be able to use #embed too.
There was a problem hiding this comment.
Good call ,replaced the inline Python blob with a two-step flow that uses the existing examples/arm/executor_runner/pte_to_header.py.
There was a problem hiding this comment.
I worry about the coupling here between the source tree layout and this packaging script. Long-term, we may want to look at using the cmake query apis to deduce source files we need for given targets rather then a script that copies them. Short-term, is there a way we can test this in CI?
From a repository point of view, my understanding is that we'll use this script to publish another repository. If we're going to be setting up another repository, would it make sense to move this script there, with executorch as a submodule?
There was a problem hiding this comment.
Agreed on all points. Added a NOTE to the script header acknowledging the coupling and the plan forward:
- Long-term: cmake query APIs to deduce sources for a given target
- Short-term: CI smoke test (will add as a follow-up)
| # - main stack: 8 KB | ||
| # - heap: 16 KB | ||
| CONFIG_EXECUTORCH_METHOD_ALLOCATOR_POOL_SIZE=65536 | ||
| CONFIG_EXECUTORCH_TEMP_ALLOCATOR_POOL_SIZE=8192 |
There was a problem hiding this comment.
The temp allocator pool shouldn't be needed any more, since all of the scratch buffer planning has been moved to AOT.
There was a problem hiding this comment.
Removed CONFIG_EXECUTORCH_TEMP_ALLOCATOR_POOL_SIZE from arduino_uno_q.conf and dropped the temp_pool / temp_allocator from all three Arduino sketches
| # Export with Cortex-M passes | ||
| cpu = CortexM.M33 if "m33" in target else CortexM.M55 | ||
| edge = to_edge( |
| return | ||
| model = train_model(args.data_dir, args.samples) | ||
| torch.save(model.state_dict(), args.output.replace(".h", ".pth")) |
| # Calibrate with real audio | ||
| quantizer = CortexMQuantizer() | ||
| prepared = prepare_pt2e(captured, quantizer) | ||
|
|
||
| print("Calibrating with real audio...") | ||
| labels = ["yes", "no", "up", "down", "left", "right", "on", "off", "stop", "go"] | ||
| for label in labels: | ||
| label_dir = os.path.join(calibration_dir, label) | ||
| wavs = sorted(f for f in os.listdir(label_dir) if f.endswith(".wav")) | ||
| for wav in wavs[50:55]: | ||
| prepared(wav_to_mfcc(os.path.join(label_dir, wav))) | ||
|
|
| # Download the dataset (one time, ~2.3 GB) — run from repo root: | ||
| python -c "import torchaudio; torchaudio.datasets.SPEECHCOMMANDS( | ||
| root='outputs/speech_commands', download=True)" | ||
|
|
| if not os.path.isdir(args.data_dir): | ||
| print(f"Dataset not found at {args.data_dir}") | ||
| print( | ||
| 'Download: python -c "import torchaudio; ' | ||
| "torchaudio.datasets.SPEECHCOMMANDS(root='outputs/speech_commands', " | ||
| 'download=True)"' | ||
| ) | ||
| return |
| #if __has_include("model.h") | ||
| #include "model.h" | ||
| #else | ||
| #error "model.h not found. Generate it with export_model.py (see README)." | ||
| #endif |
| } else { | ||
| Serial.println("ERROR: Model load failed"); | ||
| } |
Package ExecuTorch as an Arduino library installable via Library Manager. Includes build script that vendors runtime sources, DS-CNN keyword spotting example with CMSIS-NN acceleration, and pre-generated MFCC test inputs from Google Speech Commands. Tested on Arduino Uno Q (STM32U585, Cortex-M33).
Add tooling to package the ExecuTorch runtime as an Arduino library, enabling PyTorch model inference on Arduino microcontrollers. The library vendors ET runtime sources, CMSIS-NN kernels, and portable ops into a self-contained package that compiles under the Arduino build system.
Key components:
build_arduino_library.shassembles the distributable library from repository sources (no vendored copies checked in)ExecuTorchArduino.hconfigures the build environment for Arduino (fixes for std::variant, cmake_macros.h stub, build defines)platform_stubs.cprovides C library stubs for the LLEXT environmentValidated on Arduino Uno Q with DS-CNN keyword spotting model (int8, CMSIS-NN): 390+ source files compile, 106 KB flash (13%), 91 KB RAM.