emWeb: User interface in the web browser

Recently at SEGGER, we released a brand-new product: the Flasher Hub. During development, it became clear that the Flasher Hub needed an easy user interface for monitoring and configuration. The device itself is headless: No display and limited resources. Years of experience with web interfaces used in our J-Links and Flashers made our decision easy.

Here’s why we decided to equip the Flasher Hub with a web user interface.

What exactly does a web user interface do?

A web user interface is a graphical user interface (GUI) presented to the user in a web browser. You are probably already familiar with web user interfaces from other devices, like your internet router.

When the user enters the address of the device (i.e. Flasher Hub) into the browser’s address bar, the browser establishes a connection to the device. In this scenario, the browser is the client, requesting information from the other end of the connection – the server.

In other words, a web user interface provides you with an easy way to interact with a device. This is the case even for devices without any interactive elements like a display, touchscreen, buttons, etc.

Just like with a “classical” GUI, i.e. J-Flash, a web user interface can provide information that is updated in real-time without having to refresh the page. Additionally, the user may adjust device configurations or options via the web user interface.

Browsers also offer fancy features like dragging and dropping files, as can be seen in Flasher Hub’s web interface file browser.

Taking a closer look: How does the web user interface work?

As already addressed above, a web user interface consists of a server and a client. Using HTTP (or HTTPS), they exchange data with each other. The client can be any web browser running on any platform. The web server runs on the device itself and accepts and handles incoming client connections. Flasher Hub, J-Link and Flasher, for instance, run SEGGER’s emWeb.

Simply put, a browser asks the web server for files to display and the web server sends them to the browser accordingly. Web user interfaces mainly consist of the following file types:

  • .html for GUI layout
  • .css for GUI styling
  • .js for behavior
  • .png, .jpg, .svg, … for pictures, images, drawings, etc.

In addition to that, server-sent events (SSE) make it possible to have live-updating content.

Example

In order to get a better understanding of how all of that works in detail, let us take a look at the example code snippets below.

Client (written in HTML and JavaScript)

<html>
  <body>
    <h1> <!--#exec cgi="Headline"--> </h1>
    <p id="INDEX_Txt">
      <!-- Filled by SSE + JS -->
    </p>
  </body>
  <script>
    //
    // First, initialize SSE and add event listener function
    //
    _hEventSrc = new EventSource("TabX_SSEOutput.cgi?INDEX");
    _hEventSrc.addEventListener("INDEX_SSE_PageData", _ShowPageData);
    
    /*********************************************************************
    *
    *       _ShowPageData
    *
    *  Function description
    *    Parses and handles data received via SSE.
    */
    function _ShowPageData(EventInfo) {
      var Data;
      var hElem;
      
      Data  = EventInfo.data;
      hElem = document.getElementById("INDEX_Txt");
      hElem.innerHTML = Data;
    }
  </script>
</html>

Server (written in C)

static void _cbCGIHeadline (WEBS_OUTPUT* pOutput, const char* sValue);
static int _PageLoadCnt;
static const SWIN_WEBS_CGI _aCGI[] = {
  //
  // Static CGIs which are called on page load
  //
  { "Headline", _cbCGIHeadline}
 ,{ NULL, NULL }  // NULL element marks end of list
};

/*********************************************************************
*
*       _cbCGIHeadline
*/
static void _cbCGIHeadline(WEBS_OUTPUT* pOutput, const char* sValue) {
  WEBS_USE_PARA(sValue);
  WEBSRV_Lock();
  IP_WEBS_SendFormattedString(pOutput, "Hello World: %d !", _PageLoadCnt);
  _PageLoadCnt++;
  WEBSRV_Unlock();
}

/*********************************************************************
*
*       _cbOnGetSSEContent
*
*  Function description
*    Periodically called by generic layer, to provide SSE events + data for the currently opened tab
*/
static void _cbOnGetSSEContent(WEBS_OUTPUT* pOutput) {
  SYS_TIME Time;

  IP_WEBS_SendString(pOutput, "event: INDEX_SSE_PageData\ndata:");   // Initiate SSE stream
  SYS_GetLocalTime(&Time);
  IP_WEBS_SendFormattedString(pOutput, "Current time: %02d:%02d:%02d", Time.Hour, Time.Minute, Time.Second);
  IP_WEBS_SendString(pOutput, "\n\n");  // Terminate SSE stream
}

The resulting frontend looks like this:

Now, let us break down the interesting parts:

<h1> <!--#exec cgi="Headline"--> </h1>

The server processes and replaces this CGI while sending it to the client, i.e. when the browser loads this page. The server looks for the given CGI name "Headline" in its list called _aCGI[]. The associated callback function _cbCGIHeadline() handles the CGI accordingly and sends the client a generated string "Hello World: <_PageLoadCnt> !" where <_PageLoadCnt> is the number of times the page has been loaded.

_hEventSrc = new EventSource("TabX_SSEOutput.cgi?INDEX");
_hEventSrc.addEventListener("INDEX_SSE_PageData", _ShowPageData);

This JavaScript code makes sure the client can receive server-sent events. It creates an EventSource instance and registers _ShowPageData as callback function for the server-sent event identifier "INDEX_SSE_PageData".
The _ShowPageData function simply replaces the contents of the paragraph (<p> tag) on the page with the data of the received event.

static void _cbOnGetSSEContent(WEBS_OUTPUT* pOutput) {
  SYS_TIME Time;

  IP_WEBS_SendString(pOutput, "event: INDEX_SSE_PageData\ndata:"); // Initiate SSE stream
  SYS_GetLocalTime(&Time);
  IP_WEBS_SendFormattedString(pOutput, "Current time: %02d:%02d:%02d", Time.Hour, Time.Minute, Time.Second);
  IP_WEBS_SendString(pOutput, "\n\n"); // Terminate SSE stream
}

The upper layer provided by emWeb periodically calls this C function. _cbOnGetSSEContent() sends the server's current local time to the client in the following format: "Current time: HH:MM:SS".

This way of updating contents after the client has already loaded the page is also referred to as asynchronous JavaScript and XML or "AJAX" for short.

Why a web interface is superior

These are the main reasons why you should go for a web-based user interface:

Flexibility

Nowadays, all PCs and "smart" devices like smartphones, tablets, smart TVs and even smart fridges come with a web browser. Therefore, users do not need to install any additional software. Additionally, most people are already familiar with browsers and web pages and know how to use them. Furthermore, most browsers come with a broad repertoire of built-in accessibility features like scaling or text-to-speech.

Another great feature is multi-instance support. A web server can handle multiple connections in parallel. You could open multiple tabs to see different information. Alternatively, multiple users could access the web user interface at the same time.

In short: Everyone will be able to use your GUI.

Development

The development efforts are far less for web user interfaces in comparison to alternatives.

First of all, you don't need to worry about supporting different operating systems, display sizes, resolutions, pixel densities and so on - browsers already take care of all of that. In the same way you'll save on time not having to write one or more installers.

Secondly, you do not need a GUI library or similar. Again, the browser takes care of this. As a consequence of that, you save the costs for developing  a library yourself or licensing a third party's solution.

Last but not least, HTML, CSS and JavaScript are fairly easy and have been around for decades. The majority of the WWW runs on these languages, and most developers are familiar with them to some degree. Even for developers completely new to web front-end development, there are hundreds of tutorials available online for free.
No need to dive into a complex, new GUI library or even programming language with a shallow learning curve.

Debugging

Even if you are facing issues with your implementation of the front-end,
most browsers come with extensive debugging capabilities. A fully-equipped JavaScript debugger as well as detailed and visualized breakdowns of HTML elements with their corresponding CSS styles make it easy to hunt down bugs in the front-end.

Load sharing

Graphical processing is rather "costly" in regard to resources like memory and CPU power. With a web user interface, the client renders and displays the GUI . Furthermore, the server can store its web server files in compressed form and tell the client to decompress where necessary.

This "load sharing" can make a big difference, especially on devices with very limited resources like embedded systems.

Security

It is also worth noting that a web user interface can be equipped with additional security by using authentication to restrict access to the user interface. Additionally, SSL/TLS (see emSSL) can be added to ensure a secure internet connection between client and server.

Conclusion

Looking at all the advantages a web-based user interface has to offer, it was our obvious choice for J-Link and Flasher in the past as well as the Flasher Hub very recently. The interface is easy to implement, easy to use, works out-of-the-box without any installations. It is flexible, resource-friendly and can even be secure, if necessary. It simply works.

What else could you ask for?

PS:
If, for whatever reason, a web user interface is still not your interface of choice and you are looking for a GUI library for embedded systems, you might want to check out emWin.