base on Mechanoid is a framework for WebAssembly applications on embedded systems and IoT devices. # ![Mechanoid](https://mechanoid.io/images/logo-blue.png) Mechanoid is a framework for WebAssembly applications on embedded systems and IoT devices. ## What is Mechanoid? Mechanoid is an open source framework for building and running WebAssembly applications on small embedded systems and tiny IoT devices. It is intended to make it easier to create applications that are secure and extendable, and take advantage of all of the latest developments in both WebAssembly and embedded development. Mechanoid includes a command line interface tool that helps you create, test, and run applications on either simulators or actual hardware. You can write WASM modules for Mechanoid using any language that can compile to WebAssembly, including [TinyGo](https://tinygo.org/), [Rust](https://www.rust-lang.org/), and [Zig](https://ziglang.org/). Mechanoid itself is written using [Go](https://go.dev/) and [TinyGo](https://tinygo.org/). ## Why run WebAssembly on embedded devices? - Hardware devices that are extensible. Think app stores, downloadable add-ons, or end-user programming. - WASM runtime environment is sandboxed for better device security. - All devices need updates. By only updating the WASM code "bricking" the device is less likely. Also WASM code is compact, so is well suited for slow/high latency connections. - Device code that is portable. Develop code in Go/Rust/Zig or any language that can compile to WASM. - Application specific APIs for hardware. From games, to industrial control systems. ## Getting started - Install the [Mechanoid command line tool](./cmd/mecha/README.md) Use `go install` to install the `mecha` CLI ```bash go install github.com/hybridgroup/mechanoid/cmd/mecha@latest ``` - Create a new project ```bash mecha new example.com/myproject ``` - Make something amazing! Take a look at our examples repo at <https://github.com/hybridgroup/mechanoid-examples> ## Example Here is an example of an application built using Mechanoid. It consists of a host application that runs on a microcontroller, and a separate WebAssembly module that will be run by the host application on that same microcontroller. The host application loads the WASM and then executes it, sending the output to the serial interface on the board. This way we can see the output on your computer. ```mermaid flowchart LR subgraph Computer end subgraph Microcontroller subgraph Application Pong end subgraph ping.wasm Ping end Ping-->Pong Application-->Ping end Application--Serial port-->Computer ``` Here is how you create it using Mechanoid: ```bash mecha new project -template=simple example.com/myproject cd myproject mecha new module -template=ping ping ``` ### WebAssembly guest program This is the Go code for the `ping.wasm` module. It exports a `ping` function, that calls a function `pong` that has been imported from the host application. ```go package main //go:wasmimport hosted pong func pong() //go:export ping func ping() { pong() } func main() {} ``` You can compile this program to WASM using the `mecha build` command: ```bash $ mecha build Building module ping code data bss | flash ram 9 0 0 | 9 0 ``` Want to see the same `ping` module but written in Rust? See our examples repo here <https://github.com/hybridgroup/mechanoid-examples/tree/main/filestore/modules/pingrs> Want to see the same `ping` module but written in Zig? See our examples repo here <https://github.com/hybridgroup/mechanoid-examples/tree/main/filestore/modules/pingzig> ### Mechanoid host application This is the Go code for the Mechanoid host application that runs directly on the hardware. It loads the `ping.wasm` WebAssembly module and then runs it by calling the module's `Ping()` function. That `Ping()` function will then call the host's exported `Pong()` function: ```go package main import ( "bytes" _ "embed" "time" "github.com/hybridgroup/mechanoid/engine" "github.com/hybridgroup/mechanoid/interp" "github.com/orsinium-labs/wypes" ) //go:embed modules/ping.wasm var wasmCode []byte func main() { time.Sleep(2 * time.Second) println("Mechanoid engine starting...") eng := engine.NewEngine() eng.UseInterpreter(interp.NewInterpreter()) println("Initializing engine using interpreter", eng.Interpreter.Name()) if err := eng.Init(); err != nil { println(err.Error()) return } println("Defining host function...") modules := wypes.Modules{ "hosted": wypes.Module{ "pong": wypes.H0(pongFunc), }, } if err := eng.Interpreter.SetModules(modules); err != nil { println(err.Error()) return } println("Loading and running WASM code...") ins, err := eng.LoadAndRun(bytes.NewReader(wasmCode)) if err != nil { println(err.Error()) return } for { println("Calling ping...") if _, err := ins.Call("ping"); err != nil { println(err.Error()) } time.Sleep(1 * time.Second) } } func pongFunc() wypes.Void { println("pong") return wypes.Void{} } ``` You can compile and flash the application and the WASM program onto an Adafruit PyBadge (an ARM 32-bit microcontroller with 192k of RAM) with the `mecha flash` command: ```bash $ mecha flash -i wazero -m pybadge Building module ping Done. code data bss | flash ram 9 0 0 | 9 0 Application built. Now flashing... code data bss | flash ram 328988 66056 7112 | 395044 73168 Connected to /dev/ttyACM0. Press Ctrl-C to exit. Mechanoid engine starting... Initializing engine using interpreter wazero Defining host function... Loading and running WASM code... Calling ping... pong Calling ping... pong Calling ping... pong ... ``` There are more examples available here: <https://github.com/hybridgroup/mechanoid-examples> ## How it works See [ARCHITECTURE.md](./ARCHITECTURE.md) for more information. ## Supported Runtime Interpreters - [wazero](https://github.com/tetratelabs/wazero) - [wasman](https://github.com/hybridgroup/wasman) - requires the https://github.com/hybridgroup/wasman fork ## Goals - [X] Able to run small WASM modules designed for specific embedded runtime interfaces. - [X] Hot loading/unloading of WASM modules. - [X] Local storage system for WASM modules. - [ ] Allow the engine to be used/extended for different embedded application use cases, e.g. CLI, WASM4 runtime, others. - IN PROGRESS - [ ] Configurable system to allow the bridge interface to host capabilities to be defined per application. - IN PROGRESS ", Assign "at most 3 tags" to the expected json: {"id":"8624","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"