WebAssembly Micro Runtime (WAMR) library for ESP32/ESP32-S3 with Arduino and PlatformIO support.
This library packages the WebAssembly Micro Runtime (WAMR) from the Bytecode Alliance for easy use with ESP32 and ESP32-S3 in Arduino and PlatformIO projects.
WAMR is a lightweight WebAssembly runtime that allows you to:
- Run sandboxed WebAssembly code on your ESP32
- Dynamically load and execute code without reflashing
- Achieve near-native performance with AOT compilation
- Isolate untrusted code in a safe execution environment
- Update application logic over-the-air
- ✅ Fast Interpreter - Optimized WASM interpreter (~2X faster than classic)
- ✅ Small Footprint - Minimal memory overhead (50-100KB + module size)
- ✅ Arduino-Friendly - Simple C++ API with Serial debugging
- ✅ Thread-Safe API - Automatic pthread wrapping for Arduino compatibility
- ✅ PSRAM Support - Automatically uses PSRAM when available
- ✅ Native Functions - Call Arduino functions from WASM
- ✅ Built-in libc - Standard C library functions available to WASM
- ✅ ESP32 & ESP32-S3 - Supports Xtensa architecture
This library is configured for minimal interpreter-only build:
- Fast interpreter mode (enabled)
- AOT runtime (disabled - can be enabled)
- WASI (disabled - libc-builtin only)
- Multi-threading (disabled)
- JIT compilation (disabled)
Add to your platformio.ini:
[env:esp32]
platform = espressif32
board = esp32dev ; or your board
framework = arduino
lib_deps =
https://github.com/mlaass/wamr-esp32-arduino.git- Download this repository as ZIP
- In Arduino IDE: Sketch → Include Library → Add .ZIP Library
- Select the downloaded ZIP file
- Restart Arduino IDE
Clone or download this repository to your Arduino libraries folder:
cd ~/Arduino/libraries/ # or your libraries directory
git clone https://github.com/mlaass/wamr-esp32-arduino.git#include <WAMR.h>
// Your WASM module binary (embed or load from file)
const unsigned char my_wasm[] = { /* ... */ };
const unsigned int my_wasm_len = /* ... */;
WamrModule module;
void setup() {
Serial.begin(115200);
// Initialize WAMR runtime
if (!WamrRuntime::begin(128 * 1024)) { // 128KB heap
Serial.println("Failed to init WAMR!");
return;
}
// Load WASM module
if (!module.load(my_wasm, my_wasm_len)) {
Serial.println("Failed to load module!");
return;
}
// Call WASM function (safe - auto pthread-wrapped)
uint32_t args[2] = {42, 58};
if (module.callFunction("add", 2, args)) {
Serial.printf("Result: %u\n", args[0]);
}
}
void loop() {
// Your code here
}The library includes four example sketches:
Demonstrates:
- Runtime initialization
- Loading a simple WASM module
- Calling WASM functions with parameters
- Getting results
Demonstrates:
- Exporting Arduino functions to WASM
- Calling native functions from WASM
- Hardware control (LED blinking) from WASM code
Demonstrates:
- Memory allocation strategies
- PSRAM usage
- Heap size configuration
- Performance profiling
Demonstrates:
- Safe API with automatic pthread wrapping (recommended)
- Raw API for manual thread management (advanced)
- Performance comparison between APIs
- Custom thread stack configuration with setThreadStackSize()
- Building WASM Modules - How to compile C/C++ to WASM
- API Reference - Complete API documentation
- Troubleshooting - Common issues and solutions
- Minimum: 64KB runtime heap + 32KB module heap
- Recommended: 128KB runtime heap + 64KB module heap
- Can use 256KB+ for runtime heap
- PSRAM is automatically detected and used
- Better for larger WASM applications
- WAMR Runtime: ~50-100KB (depending on features)
- WASM Module: Varies by module size
- Stack: 16KB per execution (default)
- Heap: Configurable per module
- ESP32 (Xtensa dual-core)
- ESP32-S3 (Xtensa dual-core, PSRAM support)
- ESP32-C3 (RISC-V) - requires RISC-V build configuration
- ESP32-C6 (RISC-V) - requires RISC-V build configuration
You need the WASI SDK or Emscripten to compile C/C++ to WASM.
Simple example:
# Install WASI SDK
wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/wasi-sdk-21.0-linux.tar.gz
tar xf wasi-sdk-21.0-linux.tar.gz
# Compile C to WASM
wasi-sdk-21.0/bin/clang --target=wasm32 -O3 \
-nostdlib -Wl,--no-entry \
-Wl,--export=add \
-o add.wasm add.cSee docs/BUILDING_WASM.md for detailed instructions.
// Initialize runtime (call once in setup())
WamrRuntime::begin(heap_size);
// Shutdown runtime
WamrRuntime::end();
// Check if initialized
WamrRuntime::isInitialized();
// Print memory usage
WamrRuntime::printMemoryUsage();WamrModule module;
// Load WASM module
module.load(wasm_bytes, size, stack_size, heap_size);
// Call function (SAFE - recommended for Arduino)
// Automatically wraps in pthread context
uint32_t args[] = {arg1, arg2};
module.callFunction("function_name", num_args, args);
// Call function (RAW - advanced use only)
// Must be called from pthread context, crashes otherwise
module.callFunctionRaw("function_name", num_args, args);
// Configure pthread stack size for callFunction()
WamrModule::setThreadStackSize(64 * 1024); // 64KB stack
// Get result
uint32_t result = args[0]; // Result in first argument
// Get error
const char* error = module.getError();
// Unload module
module.unload();The library supports configurable logging to help with debugging and reduce serial output in production.
WAMR_LOG_NONE(0) - No logging outputWAMR_LOG_ERROR(1) - Only error messages (default)WAMR_LOG_DEBUG(2) - Error and debug messages
Define WAMR_LOG_LEVEL before including the WAMR header:
// Option 1: No logging (production builds)
#define WAMR_LOG_LEVEL WAMR_LOG_NONE
#include <WAMR.h>
// Option 2: Errors only (default - no need to define)
#include <WAMR.h>
// Option 3: Full debug output (development/troubleshooting)
#define WAMR_LOG_LEVEL WAMR_LOG_DEBUG
#include <WAMR.h>ERROR level includes:
- Runtime initialization failures
- Module loading/instantiation errors
- Function call failures
- Memory allocation errors
- Thread creation errors
DEBUG level includes all errors plus:
- Runtime initialization progress
- Module loading steps
- Function call traces
- Memory allocation details (PSRAM vs internal RAM)
- Thread stack configuration
With WAMR_LOG_DEBUG:
WAMR: Initializing runtime...
WAMR: Using PSRAM for heap
WAMR: Runtime initialized with 131072 bytes heap
WAMR: Loading module (1234 bytes)...
WAMR: Module loaded successfully
WAMR: Instantiating module (stack: 16384, heap: 65536)...
WAMR: Module instantiated successfully
WAMR: Module ready for execution
WAMR: Calling function 'add'...
WAMR: Function returned: 100
WAMR: Function 'add' completed successfully
With WAMR_LOG_ERROR (default):
(Only errors are shown if they occur)
With WAMR_LOG_NONE:
(No WAMR logging output)
Note: The log level is set at compile time, so there's zero runtime overhead when logging is disabled.
Typical performance characteristics:
- Function call overhead: ~10-50μs
- Execution speed: ~50-70% of native C (interpreter mode)
- Memory overhead: ~2X for fast interpreter vs classic
- Startup time: <100ms for small modules
Note: AOT (Ahead-of-Time) compilation can provide near-native performance but requires the wamrc compiler.
Current build limitations:
- Interpreter only (no AOT/JIT runtime included)
- No WASI support (libc-builtin only)
- No multi-threading support
- Xtensa only (RISC-V not yet configured)
These can be enabled by modifying src/wamr/build_config.h and adding the required source files.
Contributions are welcome! Please:
- Test on real hardware (ESP32/ESP32-S3)
- Follow the existing code style
- Update documentation as needed
- Add examples if introducing new features
This library is licensed under Apache License 2.0 with LLVM Exception, the same as WAMR.
See LICENSE for details.
- WebAssembly Micro Runtime (WAMR) by Bytecode Alliance
- Espressif ESP32 Arduino Core
- Library Version: 1.0.0
- WAMR Version: 2.4.1+ (see
src/wamr/WAMR_VERSION.txt)