Design web-site on Linux and running on a STM32 MCU

web-site on STM32 MCU

With the advent of various kinds of smart devices into our lives, the need for websites on MCUs has become undeniable. Due to the lwIP project (and its smaller brother uIP) such functionality is not surprising for anyone. But since lwIP is aimed at minimizing resources, in terms of design, functionality, as well as usability and development, such sites lag far behind those we are used to. Even for embedded systems, compare, for example, with an administration site on the cheapest routers. In this article, we describe how to develop a site on Linux for some smart device and run it on a STM32 MCU.

To run on an MCU, we will use Embox. This RTOS includes a CGI-enabled HTTP server. We will use the built-in HTTP server python as an HTTP server on Linux.

python3 -m http.server -d <site folder>

Static site

Let’s start with a simple static site consisting of one or more pages. Everything is easy, we just create a folder and develop ‘index.html’. This file would be downloaded by default if only the site address was specified in the browser.

$ ls website/
em_big.png index.html

The site also contains the Embox logo, the “em_big.png” file, which we embed in the HTML.

Launch HTTP server

python3 -m http.server -d website/

Open ‘localhost:8000’ in a browser

a simple site on a host

Now let’s insert our static site to the Embox file system. This can be done by copying the site folder to the ‘rootfs/’ folder in a template (the current template is in the ‘conf/rootfs’ folder). Or create a module specifying files for ‘rootfs’ in it.

$ ls website/
em_big.png index.html Mybuild

Mybuild with the module definition:

package embox.demomodule website {
source "index.html",

For simplicity’s sake, we put our site directly in a root folder (@InitFs annotation with no parameters)

We also need to include our site in the ‘mods.conf’ configuration file and add the HTTP server itself there.


Also, let’s start the server with our website during system startup. To do this, add the following line to the conf/ file

"service httpd /",

Of course, all these manipulations need to be done with the config for the target board. After that, we build and launch Embox. We type in the browser the address of your board. In my case, it is

And we see the same page as for the local site

a simple site on a MCU

We are not web developers but we have heard that various frameworks are used to create modern beautiful websites. For example, AngularJS is often used. Therefore, we will give further examples using it, but without details. And we apologize in advance if we have messed with web design somewhere.

Whatever static content we put in the site folder, for example, js or css files, we can use it without any additional effort.

Let’s add app.js to our site (the site is actually in angular). We put a couple of tabs in it. For these tabs, we put them in the ‘partials’ folder. We put the images in the ‘images’ folder. And css files we put in the ‘css’ folder.

$ ls website/
app.js css images index.html Mybuild partials

Launch the site

a static site wit AngularJS

It looks much more familiar and pleasant, isn’t it?

All this is done on the browser side. As we said, the entire context is still static. Therefore we can design it on the host like a regular website.

Of course, you can use all the common web development tools. So by opening the console in the browser, we found an error message that the ‘favicon.ico’ was missing

a browser debug console

We found out that this is an icon that is displayed in a browser tab. Of course, we can put the file with this name, but sometimes you don’t want to spend on this place. I want to remind that we also want to run on MCU where there is little memory.

A quick search on the Internet showed that you can fix this without adding a file, you just need to add the line ‘<link rel = “shortcut icon” href = “#”>’ in the head section of HTML. Although the error was not critical, making the site a little better is always nice. And most importantly, we made sure that the usual web-developer tools are quite applicable to the proposed approach.

Dynamic content


Let’s move on to dynamic content. ‘Common Gateway Interface’ (CGI) is an interface for interaction between a web server and command-line utilities that allows you to create dynamic content. In other words, CGI allows you to use the output of utilities to generate dynamic content.

Let’s take a look at some of CGI scripts

First, an HTTP header is printed to standard ‘output’, and then data is printed. The ‘output’ can be redirected anywhere. You can simply run this script from the console and will see the following

./cgi-bin/gettimeHTTP/1.1 200 OKContent-Type: application/jsonConnection: Connection: close"Fri Feb  5 20:58:19 2021"

And if you use a socket instead of standard ‘output’, the browser receives this HTTP data.

CGI is often implemented using scripts. It is due to the fact that in scripting languages it is faster and more convenient to do such things. But it is not necessary to do it in scripting languages. A utility providing CGI can be implemented in any language. And since we focus on MCU and take care of saving resources, let’s do it in C.

If we compile and run this code, we see exactly the same output as in the case of the script above.

We add a handler to call a CGI script from the ‘app.js’ for one of our tabs

A small detail for running on Linux using the built-in python server: we need to add the — cgi argument to our launch line to support CGI

python3 -m http.server --cgi -d .
simple CGI demo

Automatic dynamic content refreshing

Let’s take a look at another very important property of a dynamic site — automatic content refreshing. There are several mechanisms for it:

  • Server Side Includes (SSI)
  • Server-sent Events (SSE)
  • WebSockets
  • And others

Server Side Includes (SSI)

Server Side Includes (SSI) is a simple language for dynamically ‘assembling’ web pages on the server-side from separate parts. Usually, files using SSI have a .shtml extension.

SSI has control directives, ‘if’ ‘else’, and so on. But in most of the MCU examples we found, it is used as follows. The .shtml page inserts a directive that reloads the entire page. This can be for example:

<meta http-equiv="refresh" content="1">


<BODY onLoad="window.setTimeout(&quot;location.href='runtime.shtml'&quot;,2000)">

Content is generated in one way or another, for example, by setting a special handler.

The advantage of this method is its simplicity and minimal resource requirements. But here’s an example of how it looks.

FreeRTOS web-site demo

It is a standard example from FreeRTOS. The page refresh (see tab) is very noticeable. And reloading the entire page looks like an excessive action.

Server-sent Events (SSE)

Server-sent Events (SSE) is a mechanism that allows a half-duplex (one-way) connection between a client and a server. The client in this case opens a connection and the server uses it to transfer data to the client. Unlike classic CGI scripts, the purpose of which is to generate and send a response to the client, and then exit, SSE works in a ‘keep-alive’ (continuous) mode. That is, the server can send as much data as necessary until it either finishes itself or the client closes the connection.

There are some minor differences from regular CGI scripts. First, the HTTP header will be slightly different

"Content-Type: text/event-stream\r\n"
"Cache-Control: no-cache\r\n"
"Connection: keep-alive\r\n"

Instead of ‘close’ type ‘Connection’ must be ‘keep-alive’, that is, a continuous connection. To prevent the browser from caching data, it is needed to specify ‘no-cache’ for ‘Cache-Control’. Finally, you need to specify that the special data type ‘text/event-stream’ as ‘Content-Type’ is used.

‘text/event-stream’ means that a special format for SSE is used:

: this is a test streamdata: some textdata: another message
data: with two lines

In our case, the data needs to be packed into the following line

data: { “time”: “<real date>”}

Our CGI script would be following

If we run it we see the following

$ ./cgi-bin/gettime
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
data: {"time" : "Fri Feb 5 21:48:11 2021"}data: {"time" : "Fri Feb 5 21:48:12 2021"}data: {"time" : "Fri Feb 5 21:48:13 2021"}

And so on going on 1 per second

The same in С:

And finally, we also need to tell angular that we use SSE, that is, modify the JS code:

Running and see the following

SSE date demo

It is noticeable that, unlike using SSI, the page is not periodically reloaded, and the data is refreshed smoothly and pleasing to the eye.


Of course, the given examples are not real because they are very simple. Their goal is to show the difference between the approaches used on MCU and on other systems.

We made a small demo with real tasks. Controlling LEDs, receiving real-time data from an angular rate sensor (gyroscope) and a tab with system information.

The site has been designed on the host. It was only necessary to make small plugs to emulate the LEDs and data from the sensor. Sensor data are just random values received through the standard RANDOM

The state of the LEDs was simply stored in a file.

The same is trivially implemented in C. If you wish, you can see the code in the repository (project/website folder).

For the MCU, we of course used other implementations that worked with real peripherals. But these were common utilities and drivers that were debugged separately. Therefore, the porting of the site to the MCU did not take time.

The screenshot running on the host

the web-site on the host

In a short video, you can see the work on a real MCU. Note that there is not only communication via browser, but also, for example, setting the date using NTP from the command line in Embox, and of course handling peripherals.

You can reproduce the results follow the instruction on Embox wiki


In the article, we showed that it is possible to design nice interactive sites and run them on MCU. And that you can do this easily and quickly using all the development tools on the host and then run it on the microcontrollers. Obviously, the development of the site can be done by a professional web designer, while the embedded developer will implement the logic of the device, which is very convenient and saves time to market.

Naturally, you have to pay for all this functionality. ‘SSE’ requires slightly more resources than ‘SSI’. But with the help of Embox, we easily fit the whole system into the STM32F4-discovery without optimization and used only 128 kb of RAM. So the overhead is not so much. But the convenience of the development and the quality of the site is much higher. And at the same time, of course, do not forget that modern MCUs have grown significantly and continue to do so. After all, devices are required to be more and more ‘smart’.

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