Image for post
Image for post
Visual Programming with Embedded Rust on Super Blue Pill:

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…

Visual Programming with Embedded Rust

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…

Google Blockly

Blockly is an awesome open-source tool for visual coding, as shown in the above demo. It’s used by kids for BBC micro:bit programming, also used by makers for creating Embedded JavaScript apps on the MakeCode platform.

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.

Apache Mynewt

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.

Blockly Blocks

Image for post
Image for post

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.

Image for post
Image for post

How are Blocks created? Through the Block Factory, which looks like another Blockly app. The Block Library we have created is located at generators/rust/mynewt_library.xml

After building the Blocks, we click the Block Exporter to export the Blocks into two files…

1️⃣ generators/rust/mynewt_blocks.js: Block Definitions in JSON

2️⃣ generators/rust/mynewt_functions.js: Code Generator Functions for the Blocks


Block Definition

Image for post
Image for post

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.

Image for post
Image for post

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

The Main Code Generator for Rust looks like this (yes Blockly runs on JavaScript)…


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’);

Blocks like on_start and 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 if, loop, for, …. Here’s the Code Generator Function for digital_toggle_pin


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…

Image for post
Image for post
//  Toggle the GPIO pin
gpio::toggle(MCU_GPIO_PORTC!(13)) ? ;

For more details on Blockly Code Generation, check this article.

Customising Blockly

We isolate any changes to the Blockly source code so that we may enjoy future Blockly upgrades (via git pull)…

1️⃣ generators/rust contains the Rust Code Generator

2️⃣ demos/code is the Blockly demo in HTML that we have customised for Rust


3️⃣ demos/code/index.html has been updated to load our Rust Code Block Definitions and Rust Code Generator

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!

Image for post
Image for post
Safer and Simpler Coding: Why do this (left)… when we can do that (right)? From

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 syn as a WebAssembly module inside the Blockly JavaScript code! How cool is that?!

MakeCode has some support (though not perfect) for JavaScript Round-Tripping. Try it here

Why Visual Rust?

Why not Visual JavaScript? Visual MicroPython? Visual C?

I have experimented with Visual Embedded Programming in JavaScript (here, here, here, here, here, here and here)… It’s just too darned hard!

1️⃣ Embedded JavaScript (and Static TypeScript) is way too bloated to run on popular but constrained microcontrollers like STM32 Blue Pill. Same for MicroPython.

2️⃣ Embedded JavaScript (and MicroPython) needs to be hosted on a proper realtime operating system (RTOS). Which adds to the bloat.

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…

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…

Image for post
Image for post

The Rust code generated is below. The code calls the following C functions imported from Mynewt…

  1. gpio::init_out: Configure a GPIO pin for output
  2. gpio::write: Write to a GPIO pin
  3. gpio::toggle: Toggle a GPIO pin from Low to High and vice versa
  4. os::time_delay: Sleep a while and switch to other tasks
  5. os::task_init: Start a background task
  6. os::sysinit: Initialise Mynewt OS
  7. os::eventq_run, os::eventq_dflt_get: Event loop for Mynewt OS
Embedded Rust code generated for the visual program
Image for post
Image for post
Image for post
Image for post
NB-IoT programming in progress…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store