Visual Programming with Embedded Rust? Yes we can with Apache Mynewt and Google Blockly!
I gotta admit — Embedded Programs are getting darned hard to code on modern microcontrollers, sensors and networks. Faced with the ultra-daunting task of coding a readable, reusable, open-source NB-IoT application for STM32, I asked myself… Could Visual Programming with Embedded Rust solve this problem? Like this…
Try it for yourself here:
Let’s talk about the components necessary for this visual Rust solution…
1️⃣ Google Blockly, the visual coding tool
2️⃣ Apache Mynewt, the embedded OS
3️⃣ And all the integration bits: Custom code blocks, code generation, safe wrappers, …
The source code is available here…
It’s easy to clone the Blockly repository, open in a web browser and test the visual programming tool ourselves. Unfortunately Blockly doesn’t generate Rust code today. We’ll discuss Rust code generation in a while.
Recall the problem we are solving… Creating Embedded Apps easily for Bare Metal microcontrollers (like STM32 Blue Pill). Our Embedded Rust Apps will have to access Bare Metal functions like GPIO, I2C, SPI, … And network peripherals like ESP8266, nRF24L01, BC95-G, …
Can we use Rust drivers to access these functions? Not quite — today we don’t have a comprehensive collection of Embedded Rust drivers. Given that the Embedded Rust platform is still in flux, it may be risky to adopt Embedded Rust drivers now.
So let’s pick a modern embedded real-time OS (built with C) and build Rust wrappers around it. I have chosen Apache Mynewt. We’ll talk about the Rust Safe Wrappers in a while. Meanwhile find out how I integrated Rust with Mynewt in this article.
A visual program in Blockly consists of Blocks connected together.
The screenshot here shows three GPIO Blocks that we have created specially for creating Embedded Rust apps.
For more details on Blockly Custom Blocks, see…
Define Blocks | Blockly | Google Developers
Here’s the Block Definition for the
digital toggle pin Block. The Block toggles a GPIO pin from Low to High and vice versa. The Block was exported by the Block Exporter.
The Block is defined with one input named
PIN: a dropdown for a list of pins (like
PA1), which will be dynamically populated in the real visual programming tool.
In the Block Factory, the Block was defined as
top+bottom connections. Which means that it’s meant to be used like a program statement, with (optional) Statement Blocks preceding and following the Block.
Each Block requires a Code Generation Function to generate the code for the Block. More about this…
Rust Code Generation
When this demo was created, Blockly did not have a Rust Code Generator. So I copied the Dart Code Generator into these locations…
generators/rust.js: Main Code Generator for Rust
generators/rust: Rust Code Generators for the Standard and Custom Blocks
We have seen this code earlier in the demo — this is the
main() function that’s generated before all other Blocks.
Code Generators are really simple text processors in Blockly. We see here that it’s appending the
main() function to another chunk of code that was generated by other Blocks…
code = [ code, … ].join(‘\n’);
forever will use a similar Code Generator that embeds an inner chunk of code.
Every Block in Blockly needs a Code Generator Function to emit Rust code for the Block, even Common Blocks like
for, …. Here’s the Code Generator Function for
Remember that the Block accepts one parameter
PIN, the GPIO pin to be toggled? Here’s how we fetch the parameter and embed it into the code. The output looks like this…
// Toggle the GPIO pin
gpio::toggle(MCU_GPIO_PORTC!(13)) ? ;
For more details on Blockly Code Generation, check this article.
We isolate any changes to the Blockly source code so that we may enjoy future Blockly upgrades (via
generators/rust contains the Rust Code Generator
demos/code is the Blockly demo in HTML that we have customised for Rust
demos/code/index.html has been updated to load our Rust Code Block Definitions and Rust Code Generator
4️⃣ The Blocks Menu in
demos/code/index.html has been updated with our Custom Blocks
demos/code/code.js has been updated to load the Custom Blocks
Safe Wrappers in Rust
What makes a great Embedded Programming experience? Safe and simple coding…
1️⃣ Safe Coding: We shall prevent programmers from falling into traps that they don’t know how to escape. Pointers in Embedded C is a very serious trap.
2️⃣ Simple Coding: Coding shall look intuitive, even though in reality it’s complicated. Like automatically ensuring that all strings passed from Rust to C functions are null-terminated.
The solution: Create Safe Wrappers in Rust to wrap up the risky, complex parts of the Mynewt API. It’s possible to generate Safe Wrappers for Mynewt APIs automatically via
bindgen and Rust Procedural Macros. Learn more about this here.
And once we have implemented Visual Programming in Embedded Rust, coding can’t get any simpler!
Rusty Round Trip
“When she’s coming round the mountain… will she be the same?”
Ever since the 1980s, software engineers have been concerned about Round-Trip Engineering… If we make any changes to the generated Rust source code, can the Visual Programming Tool apply the same changes back into the original Visual Program? Such that both Visual and Textual Programs are always synchronised?
It’s a difficult problem — there are so many Rust Programs that can’t possibly be expressed as a Visual Program!
But it’s easier to solve in Rust. In my previous article I wrote about the
syn Crate that parses Rust source code. This could be used to discover changes in the generated Rust source code and (hopefully) make (sensible) updates (automatically) to the Visual Program.
This might work by embedding
Why Visual Rust?
3️⃣ Embedded C, hosted on a suitable RTOS, has zero bloat but it’s too unsafe for today’s embedded programmers.
Visual Programming with Embedded Rust + Apache Mynewt is the perfect compromise: safe and simple coding, compiles into compact Arm machine code, very little bloat. Yet it’s sufficiently sophisticated to support Round-Tripping, NB-IoT with CoAP and CBOR, …
So why aren’t we doing Visual Rust Programming today and moving out of Embedded C forever…?
Run The Demo
To run the Visual Rust demo, click here:
To make your own changes, clone this repository…
Web-based visual programming editor for Apache Mynewt Embedded OS and Embedded Rust - lupyuen/blockly-mynewt-rust
Then edit the files in Visual Studio Code and open the local copy of
demos/code/index.html in a web browser.
To save the visual program, click the XML tab and save the contents into an XML file. To load the visual program, paste the XML from the XML file into the XML tab.
For this visual program…
The Rust code generated is below. The code calls the following C functions imported from Mynewt…
gpio::init_out: Configure a GPIO pin for output
gpio::write: Write to a GPIO pin
gpio::toggle: Toggle a GPIO pin from Low to High and vice versa
os::time_delay: Sleep a while and switch to other tasks
os::task_init: Start a background task
os::sysinit: Initialise Mynewt OS
os::eventq_run, os::eventq_dflt_get: Event loop for Mynewt OS
Are We There Yet?
Yes we are! Check out the latest article on Visual Embedded Rust here…
Visual Embedded Rust Programming with Visual Studio Code
Create and edit Embedded Rust programs for STM32 Blue Pill and Apache Mynewt… By dragging and dropping blocks!
Earlier articles on Embedded Rust and Apache Mynewt…