TOPLLab / WARDuino

📟 A dynamic WebAssembly VM for embedded systems
https://topllab.github.io/WARDuino/
Mozilla Public License 2.0
82 stars 8 forks source link

Q: Paradigm for community drivers and libraries #144

Open Beanow opened 1 year ago

Beanow commented 1 year ago

This is mostly a documentation and discussion point.

Currently WARDuino itself does the work of exposing framework-level capabilities. Such as GPIO and MQTT, and you can imagine how this approach would extend to something like I2C or UART capabilities of the chip.

However exposing those as primitives is one thing. You can surely start implementing some of those 2-page datasheet simple I2C/UART protocols from scratch with a little elbow grease.

Where it gets interesting is the non-trivial libraries and drivers. For example https://github.com/lexus2k/lcdgfx#documentation One does not simply rewrite in Rust WASM the work of implementing a complex protocol through an efficient and ergonomic abstraction (GFX engine in this case). Not to mention it's also the huge amount of module-specific quirks that it handles for you that make replacing/rewriting a library like this a massive undertaking.

So my question starts there, is there a "hybrid" / extension model intended where you make use of C/CPP libraries and expose high level functionality to be used in WASM? Or some model where a new "pure WASM" community library can be created over time?

tolauwae commented 1 year ago

Hi @Beanow, thanks for the question.

So my question starts there, is there a "hybrid" / extension model intended where you make use of C/CPP libraries and expose high level functionality to be used in WASM?

I'm not sure exactly what you have in mind, but this is what the primitives in the VM do. The primitives in WARDuino are CPP functions that can be called as WASM primitives. So there is no need to reimplement C/CPP libraries in WASM. You do however, need to write a bit of CPP code that calls the libraries functions. This is a bit of duplicate work, but you only need to do this once. For instance, we do this for the Adafruit NeoPixel Library. The code gets the arguments from the WASM stack and passes them on to the pixels.setPixelColor library function.

https://github.com/TOPLLab/WARDuino/blob/19c34341ecbc94dffd507da76cf0bea6168995d5/src/Primitives/arduino.cpp#L548-L557

We would like to make the implementation of primitives faster and easier in the future with a more structure approach, but there are no short-term plans currently.

Or some model where a new "pure WASM" community library can be created over time?

Unfortunately, WARDuino doesn't support multiple modules yet. But once we do, you should be able to load WASM "libraries" alongside your program. Until then, you'll have to include the libraries as part of your program.

In fact if your CPP library is so large that writing primitives would be too much work, it should be possible to compile the C/CPP library to WASM, and load it in the VM together with your program.

Beanow commented 1 year ago

I'm not sure exactly what you have in mind,

It's an open ended question. To get a sense of what the approach would be and where WARDuino is headed in this regard :]

I have example hobby projects, which are pretty library reliant to be done in a reasonable amount of weekends. Like MQTT connected air quality sensor, connecting 2 sensors and an OLED screen over a shared I2C connection, plus UART based sensor. Another one effectively a smart alarm clock, also MQTT controlled, including OLED, audio module, various sensors...

Enough moving parts that rewriting libraries would be.. no longer fun as a hobby :joy:

You do however, need to write a bit of CPP code that calls the libraries functions.

Yes so this is an interesting thing about primitives as it is. Glue code, besides taking some time to make, are potential for performance, security or generally issues.

The library I mentioned for instance has a pretty substantial API surface to glue. For example the canvas. https://github.com/lexus2k/lcdgfx/blob/master/src/canvas/canvas.h

Because it essentially moves from the commands the hardware support, into manipulating framebuffers that get flushed for you. You'll be making a lot of calls to these. And need to glue a lot of these.

At some point the easier approach would be to move your use-case into CPP code. For example you implement all the drawing logic for a particular UI view in CPP, and only send the parameters across from WASM. You would essentially build a primitive per UI view, rather than to expose the library itself.

As a result though, you're now writing logic in CPP again :shrug: so this seems like a slippery slope.

In fact if your CPP library is so large that writing primitives would be too much work, it should be possible to compile the C/CPP library to WASM, and load it in the VM together with your program.

Now that sounds interesting :eyes: are there any limitations / gotchas? And could it address that slippery slope problem?

tolauwae commented 1 year ago

Now that sounds interesting :eyes: are there any limitations / gotchas? And could it address that slippery slope problem?

I've never tried this myself, but I would expect the biggest limitation to be the memory of the constraint devices. Compiling an entire library to WASM might result in the code quickly being too large to run on small embedded devices.

It would address the slipperly slope in a way, since you would load the library directly into WASM, provided you get it to work. Because it might not be so straightforward :upside_down_face:. Best case scenario, the WASM code you get from compiling you library will export functions that correspond exactly with the library API (no guaranteed though). In that case, you should be able to call the wasm functions straight from Rust, Javascript, AssemblyScript, ... Although you probably still want some glue code in those language to have a nicer interface. (Getting you back to the slippery slope ...)

It's an open ended question. To get a sense of what the approach would be and where WARDuino is headed in this regard :]

It would be cool to have something like "community modules" in the future. We have been playing around with a few ideas in this area. We would want a more structured approach to implementing primitives, although I don't believe we can ever eliminate the slippery slope you identified. The structured approach would aim at reducing the chances of bugs being introduced in the glue code. The primitives would ideally also be structured into modules that can be easily installed as add-ons to the vm. Enabling community libraries seems like a small step from there.

If you have any clear expectations of how community modules could work, or how the interface for implementing / porting libraries could work, I'd be happy to hear your suggestions.