Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use wypes to define host functions #17

Merged
merged 4 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
116 changes: 66 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![Mechanoid logo](https://mechanoid.io/images/logo-blue.png)
# ![Mechanoid](https://mechanoid.io/images/logo-blue.png)

Mechanoid is a framework for WebAssembly applications on embedded systems.

Expand All @@ -22,13 +22,13 @@ Mechanoid includes a command line interface tool that helps you create, test, an

- Install the [Mechanoid command line tool](./cmd/mecha/README.md)

```
```bash
go install github.com/hybridgroup/mechanoid/cmd/mecha@latest
```

- Create a new project

```
```bash
mecha new example.com/myproject
```

Expand Down Expand Up @@ -61,7 +61,7 @@ flowchart LR

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
Expand All @@ -81,15 +81,15 @@ func pong()

//go:export ping
func ping() {
pong()
pong()
}

func main() {}
```

Compile this program to WASM using Mechanoid:

```
```bash
$ mecha build
Building module ping
code data bss | flash ram
Expand All @@ -101,64 +101,80 @@ Building module ping
This is the Go code for the Mechanoid host application that runs 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 (
_ "embed"
"time"
"bytes"
_ "embed"

"github.com/hybridgroup/mechanoid/engine"
"github.com/hybridgroup/mechanoid/interp/wasman"
"github.com/hybridgroup/mechanoid"
"github.com/hybridgroup/mechanoid/engine"
"github.com/hybridgroup/mechanoid/interp/wazero"
"github.com/orsinium-labs/wypes"
)

//go:embed ping.wasm
var pingModule []byte

func main() {
println("Mechanoid engine starting...")
eng := engine.NewEngine()

println("Using interpreter...")
eng.UseInterpreter(&wasman.Interpreter{})

println("Initializing engine...")
eng.Init()

println("Defining func...")
if err := eng.Interpreter.DefineFunc("hosted", "pong", pongFunc); err != nil {
println(err.Error())
return
}

println("Loading module...")
if err := eng.Interpreter.Load(pingModule); err != nil {
println(err.Error())
return
}

println("Running module...")
ins, err := eng.Interpreter.Run()
if err != nil {
println(err.Error())
return
}

for {
println("Calling ping...")
ins.Call("ping")

time.Sleep(1 * time.Second)
}
err := run()
if err != nil {
println(err.Error())
}
}

func pong() wypes.Void {
println("pong")
return wypes.Void{}
}

func pongFunc() {
println("pong")
func run() error {
mechanoid.Log("Mechanoid engine starting...")
eng := engine.NewEngine()

mechanoid.Log("Using interpreter...")
interp := wazero.Interpreter{}
eng.UseInterpreter(&interp)

mechanoid.Log("Initializing engine...")
err := eng.Init()
if err != nil {
return err
}

mechanoid.Log("Defining func...")
modules := wypes.Modules{
"hosted": {
"pong": wypes.H0(pong),
"ponger": wypes.H0(pong),
},
}
err = interp.SetModules(modules)
if err != nil {
return err
}

mechanoid.Log("Loading module...")
if err := eng.Interpreter.Load(bytes.NewReader(pingModule)); err != nil {
return err
}

mechanoid.Log("Running module...")
ins, err := eng.Interpreter.Run()
if err != nil {
return err
}

mechanoid.Log("Calling ping...")
_, err = ins.Call("ping")
if err != nil {
return err
}
return nil
}
```

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 this command:

```
```bash
$ mecha flash -m pybadge
code data bss | flash ram
101572 2044 6680 | 103616 8724
Expand All @@ -179,7 +195,7 @@ pong
```

There are more examples available here:
https://github.com/hybridgroup/mechanoid-examples
<https://github.com/hybridgroup/mechanoid-examples>

## How it works

Expand Down
26 changes: 11 additions & 15 deletions devices/hardware/adc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,26 @@ package hardware

import (
"github.com/hybridgroup/mechanoid/engine"
"github.com/orsinium-labs/wypes"
)

var _ engine.Device = &ADC{}

type ADC struct {
Engine *engine.Engine
}
type ADC struct{}

func (a *ADC) Init() error {
// this is where the host machine's GPIO would be initialized
// and all the hosted functions setup
if a.Engine == nil {
return engine.ErrInvalidEngine
}
func (ADC) Init() error {
return nil
}

if err := a.Engine.Interpreter.DefineFunc("machine", "__tinygo_adc_read", ADCRead); err != nil {
println(err.Error())
return err
func (ADC) Modules() wypes.Modules {
return wypes.Modules{
"machine": wypes.Module{
"__tinygo_adc_read": wypes.H1(ADCRead),
},
}

return nil
}

func ADCRead(pin Pin) uint16 {
func ADCRead(pin wypes.UInt8) wypes.UInt16 {
// Not implemented
return 0
}
50 changes: 20 additions & 30 deletions devices/hardware/gpio.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package hardware

import (
"github.com/hybridgroup/mechanoid/engine"
"github.com/orsinium-labs/wypes"
)

var _ engine.Device = &GPIO{}
Expand All @@ -20,53 +21,42 @@ type PinConfig struct {
Mode PinMode
}

type GPIO struct {
Engine *engine.Engine
}
type GPIO struct{}

const moduleName = "env"
func NewGPIODevice() *GPIO {
return &GPIO{}
}

func NewGPIODevice(e *engine.Engine) *GPIO {
return &GPIO{
Engine: e,
}
func (GPIO) Init() error {
return nil
}

func (g *GPIO) Init() error {
func (GPIO) Modules() wypes.Modules {
// this is where the host machine's GPIO would be initialized
// and all the hosted functions setup
if g.Engine == nil {
return engine.ErrInvalidEngine
}

if err := g.Engine.Interpreter.DefineFunc(moduleName, "__tinygo_gpio_configure", PinConfigure); err != nil {
println(err.Error())
return err
return wypes.Modules{
"env": wypes.Module{
"__tinygo_adc_read": wypes.H1(ADCRead),
"__tinygo_gpio_configure": wypes.H2(PinConfigure),
"__tinygo_gpio_set": wypes.H2(PinSet),
"__tinygo_gpio_get": wypes.H1(PinGet),
},
}

if err := g.Engine.Interpreter.DefineFunc(moduleName, "__tinygo_gpio_set", PinSet); err != nil {
println(err.Error())
return err
}

if err := g.Engine.Interpreter.DefineFunc(moduleName, "__tinygo_gpio_get", PinGet); err != nil {
println(err.Error())
return err
}

return nil
}

func PinConfigure(pin int32, config int32) {
func PinConfigure(pin wypes.Int32, config wypes.Int32) wypes.Void {
pinConfigure(Pin(pin), PinConfig{Mode: PinMode(config)})
return wypes.Void{}
}

func PinSet(pin int32, value int32) {
func PinSet(pin wypes.Int32, value wypes.Int32) wypes.Void {
v := value != 0
pinSet(Pin(pin), v)
return wypes.Void{}
}

func PinGet(pin int32) int32 {
func PinGet(pin wypes.Int32) wypes.Int32 {
if pinGet(Pin(pin)) {
return 1
}
Expand Down
47 changes: 19 additions & 28 deletions devices/hardware/i2c.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,47 @@ package hardware

import (
"github.com/hybridgroup/mechanoid/engine"
"github.com/orsinium-labs/wypes"
)

var _ engine.Device = &I2C{}

type I2CConfig struct{}

type I2C struct {
Engine *engine.Engine
type I2C struct{}

func NewI2CDevice() *I2C {
return &I2C{}
}

func NewI2CDevice(e *engine.Engine) *I2C {
return &I2C{
Engine: e,
}
func (I2C) Init() error {
return nil
}

func (i2c *I2C) Init() error {
func (i2c *I2C) Modules() wypes.Modules {
// this is where the host machine's I2C would be initialized
// and all the hosted functions setup
if i2c.Engine == nil {
return engine.ErrInvalidEngine
}

if err := i2c.Engine.Interpreter.DefineFunc("machine", "__tinygo_i2c_configure", I2CConfigure); err != nil {
println(err.Error())
return err
}

if err := i2c.Engine.Interpreter.DefineFunc("machine", "__tinygo_i2c_set_baud_rate", I2CSetBaudRate); err != nil {
println(err.Error())
return err
}

if err := i2c.Engine.Interpreter.DefineFunc("machine", "__tinygo_i2c_transfer", I2CTransfer); err != nil {
println(err.Error())
return err
return wypes.Modules{
"machine": wypes.Module{
"__tinygo_i2c_configure": wypes.H3(I2CConfigure),
"__tinygo_i2c_set_baud_rate": wypes.H2(I2CSetBaudRate),
"__tinygo_i2c_transfer": wypes.H5(I2CTransfer),
},
}

return nil
}

func I2CConfigure(bus uint8, scl Pin, sda Pin) {
func I2CConfigure(bus wypes.UInt8, scl wypes.UInt8, sda wypes.UInt8) wypes.Void {
// Not implemented
return wypes.Void{}
}

func I2CSetBaudRate(bus uint8, br uint32) {
func I2CSetBaudRate(bus wypes.UInt8, br wypes.UInt32) wypes.Void {
// Not implemented
return wypes.Void{}
}

func I2CTransfer(bus uint8, w *byte, wlen int, r *byte, rlen int) int {
func I2CTransfer(bus wypes.UInt8, w wypes.Byte, wlen wypes.Int, r wypes.Byte, rlen wypes.Int) wypes.Int {
// Not implemented
return 0
}
Loading