All Stack Rust

Samuel Schüpbach
6 min readDec 29, 2020

Web development has worked its way into many unexpected aspects of programming, including embedded systems. While one would think that web development is as far from firmware as possible, as an embedded developer I still find myself writing webpages frequently, whether for a GUI or a configuration page.

While I find it refreshing to switch between language stacks once in a while, the Javascript web development stack is not my favorite to work with. Instead, I’ve started exploring Rust due to amazing recent improvements in the embedded and web development spaces. So I wondered, is it possible to conveniently write both the firmware and the website in Rust? If so, what are the benefits and drawbacks?

To explore this approach and to get a little more Rust experience, I decided to create a project in which all stacks from the firmware to the website leverage the Rust ecosystem and language.

If you want to check out the whole project, you can find it here. For the write up, keep reading.

Proof of concept

To test the approach, I implemented a web server on a micro controller which is able to serve a website through HTTP, as well as data over a websocket to a browser.

I used a STM32F407 discovery board as the micro controller, since that is what I had available.

Overview of the firmware architecture

Unfortunately, the board doesn’t have a out of box Ethernet/Wifi connection available, so the communication is done over UART.

Once the website is loaded and the websocket connection is established, the board starts sending ADC data to the website, which calculates a furrier transformation (to do something interesting with the data) and subsequently plots the result.

Client website

The simple client website is written with the Yew framework, which is compiled down to a .wasm and a small wrapper/loader .js file with the help of the wasm-pack tool.

It doesn’t do much more than allow the user to open the websocket, which starts the data aggregation, the furrier transform calculation, and the plotting.

The website that will be loaded from the device

Since the board also doesn’t have a SD-card slot, the website is directly flashed together with the firmware. That's quite handy thanks to Rust’s include_bytes! macro, which includes a local file as a static byte array in the build output.

The files to be flashed are the index.html as well as the generated .js and .wasm files. If you’re feeling fancy, you can also include .css and .ico files.

Communication protocol

A data exchange protocol is required for the websocket communication. This is where Rust with Serde really shines.

Adding a protocol for websites/embedded combinations can be cumbersome with C/Javascript, but with Serde it’s little more than choosing a protocol and a call to serialize or deserialize.

The data to be exchanged and some wrapper functions to the serialization and deserialization calls are located in a separate submodule, which is included in both the client and the firmware.

As for the protocol, I chose postcard, since it’s optimized for embedded systems and works in no_std/no alloc environments.

Being able to implement code which can be run on both systems can drastically reduce development times. This is where a system written in the same language really shines. The shared code will just have to be included in the target project, and will then be automatically compiled with their respective toolchains. Moreover, it only has to be written one time instead of once in Javascript and once in C.

Shared code setup

Only the protocol code is shared in this example, which you could also technically do with Protobuf/NanoPB to some extent. But this approach isn’t limited to just protocol. Basically anything that is implemented with the no_std gate can be used in both the client and the firmware builds.

Firmware

As previously mentioned, I picked a STM32F407 discovery board as target hardware and used the corresponding board support package crate to control the ADC and the UART.

The firmware consists of a webserver, which is based on the embedded websocket crate, together with an ADC to provide measured data and RTIC to tie everything together.

Firmware details view

The UART task listens for incoming HTTP packages and forwards them to the server, which either serves the requested files or opens the websocket.

The ADC collects samples with a frequency of 1kHz and sends them to the server task as well. There they will be serialized, wrapped in websocket packages, and sent to the browser.

The whole software is static and alloc free, which is partially ensured due to the use of the heapless crate providing a collection of alloc-free collections.

To compile the firmware, it was sufficient to install the corresponding toolchain (thumbv7em-none-eabihf) and to flash, the probe.rs (specifically cargo-embed) tools which can be used for embedded flashing and debugging and generally make the embedded development a breeze.

Setting it up

Since the browser cannot connect itself to an UART port in any standard way I know of, a UART to TCP translation needs to be used.

For this I used the ser2net tool. It allows for the opening of a TCP port that forwards all data in both directions.

The tool can be started as follows to get the communication up and running:

ser2net -d -t 2 -C "4242:raw:600:/dev/PATH:115200 8DATABITS NONE 1STOPBIT LOCAL -RTSCTS max-connections=3"

Using the page

Once the website is built, the hardware is flashed, and the communication is running, the website can be loaded and used.

To do so, open http://localhost:4242 in the browser.

The whole system in operation

I connected the audio output of my laptop via Aux to the ADC and started a frequency sweep (shown the gif) to collect meaningful data.

Aux signal generator setup

A warning: Since UART is ridiculously slow in comparison to Ethernet/Wifi, it takes quite a while (around 40 seconds) to load the page. Progress can be tracked in the network tab of the developer tools.

Loading times of the website over UART

Conclusion

The project confirms that it is possible to target from embedded all the way up to the website entirely with Rust. But as with most technological developments, there are both benefits and drawbacks.

In my opinion, one major benefit is certainly that everything can be done with the same tech stack and language. You can do almost everything straight out of Cargo and with Rust. That’s incredibly convenient since you don’t have to deal with two languages and various different tools and ecosystems.

Another potentially useful benefit is that you can share code between the two layers. This can come in handy down the road since you can create one library for all your needs, which besides reducing development times, could even be used for other systems that are neither web nor embedded related.

The only real drawback that I found was that the Rust ecosystem is not quite as mature as C/Javascript. You are not going to find the variety of libraries and frameworks for Rust on embedded or on the web as you will for either C or Javascript. But this will improve over time.

There are surely many more platform-specific drawbacks and benefits that are beyond the scope of this project.

So should you use Rust next time you have to put a website on a micro controller? If you already have established web and embedded teams or large and battle-proven code bases, it might be too much work to switch over. But if you prefer Rust over C/Javascript and want to use it in a new project, then this might be an option for you.

No matter what, I am convinced with the speed Rust enters into these domains, we will see similar approaches more frequently in the near future.

--

--

Samuel Schüpbach

Junior electrical engineer specialized in embedded systems. Passionate about using emerging technologies like Rust in combination with Linux to improve embedded