Modern Approach to Embedding a Web Server in a Device

TL;DR
The two minute video and examples at the end of the article show how it works.

In this article, we will look into how we can modernize embedded web based device management by using reactive programming techniques, not only for the user interface, but also for the complete solution, including changes propagated in real time from the device to all connected browsers. A modern reactive web based application is commonly assembled as a Single Page Application (SPA) and many tools facilitate reactive SPA development, including AngularJS, ReactJS, and Vue.js. We will move on to designing a reactive multiuser based solution for controlling shared resources in the device. The fully functional example we will present can be downloaded and loaded in your browser and run without having to install any software.

Reactive programming is a programming paradigm oriented around data flows and the propagation of change. In a web based device management system, the propagation of change may come from a user clicking a button, but also from reactive events in the device. These events must be propagated to all connected browser user interfaces.

Old Style (Traditional Solution)

Let's start by looking at a traditional embedded web device management approach for turning a light bulb on or off by using a web server embedded in a device, which in turn controls the light bulb's on/off switch.

Embedded Web Server Controlling a Light Switch

Figure 1: Traditional web based device management interface.

The above figure shows a traditional web design solution for controlling a light bulb. An HTML file is loaded from the embedded web server into the browser and a web form similar to the following is embedded in the HTML page:

<form method="post">
   Light On/Off: <input name="lightbulb" type="checkbox">
   <input type="submit">
</form>

The user must first check or uncheck the checkbox and then press the submit button. The browser then sends this as a POST request to the server, where server side logic processes the request and turns the light bulb either on or off.

We can apply a quick fix to the user interface and make it more aesthetic and modern looking by simply applying cascading style sheet (CSS) to the checkbox. Most modern browsers support the latest CSS3 technology. By using CSS only (no images), we can quickly modernize the user interface and make it look like the UI shown in the following figure:

Embedded Web Server Controlling a Light Switch - Styled Version

Figure 2: Traditional web based device management interface with modern CSS.

Note that we have, at this point, only applied modern CSS. The application works identical to the one shown in figure 1. The user must still perform the two step procedure for turning the light bulb on or off by first clicking the light switch (checkbox) followed by clicking the submit button.

We can simplify and further modernize the application and replace the requirement for using a submit button by using JavaScript and AJAX. We can install a JavaScript callback function that triggers as soon as the user clicks the checkbox. The JavaScript callback function receives the checkbox's on/off state as soon as the user clicks the checkbox and uses AJAX for sending this information to the server.

AJAX makes it easy to send asynchronous events from a browser to the device and may seem like the ideal solution for our web based on/off light bulb switch. However, AJAX provides only asynchronous events from browser to device, not the other way around. This immediately becomes a problem if a second user opens the same light bulb switch UI in another browser. The server receives an asynchronous on/off event from one browser, but is unable to notify the second browser and the two user interfaces become out of sync and the one not updated will provide stale information to the user.

Reactive WebSockets for Multiuser Real Time Device Management of Shared Resources

We can fix the limitation in AJAX technology by using WebSockets, a newer technology supported by all modern browsers. WebSockets provides a full-duplex asynchronous (reactive) communication channel over a single TCP connection.

The fact that Websockets provides a bi-directional communication channel between the browser and server immediately opens up some very interesting opportunities for web based device management applications. Because the connection is persistent, the embedded web server can now initiate communication with the browser. The embedded web server can send alerts, updates, notifications, etc.. This adds a whole new dimension to the types of web based device management applications that can be constructed.

In our basic light switch application, we will simply use WebSockets for keeping any number of browser user interfaces connected to the server synchronized. The following figure illustrates the sequence of events. When a user clicks the light switch in the browser, a "Set Event" is sent to the server, which in turn sets the light bulb state. The server finishes the request by sending an "Update Event" to all connected browsers.

Synchronized and Reactive Web User Interfaces

Figure 3: Two synchronized browser user interfaces managing a shared resource controlled by the server

The Set Event/Update Event behaves similar to an AJAX call, but with one major difference. The response to the request, the Update Event, is sent not only to the originator, but to all connected browsers. The AJAX protocol can easily be built using Websockets technology, and Websockets can be seen as a superset of AJAX. WebSockets enables us to respond to user inputs in real time and reactively update all connected browser user interfaces simultaneously.

We can directly use the WebSocket API provided by the browser and the API provided by the server. However, using a raw WebSocket connection is similar to using a raw TCP connection. A raw WebSocket connection does not provide an easy to use high level API for sending messages. What we need is a protocol that sits on top of WebSockets and that provides an easy to use messaging API.

The "Update Event" message shown in figure 3 requires the one-to-many design pattern and a publish/subscribe (pub/sub) protocol provides this feature. A pub/sub protocol makes it easy to send messages to all connected browsers. Publish/subscribe protocols typically require a broker. Many embedded application servers, such as the Barracuda Application Server, includes a broker as an integral part of the server solution. The following figure illustrates how a server side custom light bulb app can communicate with all connected browsers via the broker.

Embedded Device and Broker

Figure 4: An embedded pub/sub broker serves as the browser to device communication manager

Example Program

A working example is worth a thousand words, however, how can one provide an easy to use light bulb device management demo without real hardware? The solution we came up with was to decouple the Light Bulb App from the device and to implement the Light Bulb App as a web application. This slightly complicates the design since the solution must now be able to not only manage one light bulb, but 0 to N light bulbs. In other words, the solution must be able to manage any number of browsers that have loaded the Light Bulb App.

In fact, our example program simulates a common industrial device management setup where a main controller is in charge of managing N microcontrollers. The main controller typically includes an embedded web server that enables an operator to manage all connected microcontrollers via the main controller. The following figure illustrates how such an industrial setup may function.

Embedded Device IoT Solution

Figure 5: A common industrial setup where a main controller is managing N microcontrollers.

In our example program, we selected Vue.js for the Light Switch app and JQuery for the simulated Light Bulb App. We will not focus much on the simulated Light Bulb App since this application would typically be part of the server (Fig. 4) or microcontrollers (Fig. 5) in a real application.

New to Vue.js?

Checkout Net Ninja's Vue.js video tutorials.

Why Vue.js?

There is no shortage of web frameworks. The main reason we believe Vue.js is a good choice for device management is its small size. Many of the other frameworks come with major baggage and complicates the embedded device lifecycle management. There are additional benefits in using Vue.js besides its size. The Vue.js comparison page explains this in detail.

The Vue.js Light Switch App

We designed the Light Switch App such that it can be downloaded and run in any browser without having to install any software. Simply drag and drop the app into any browser, and it will connect to a server/broker we have running online.

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src='https://simplemq.com/rtl/smq.js'></script>
.....
<script>
// Connect to online test broker
var smq = SMQ.Client("wss://simplemq.com/smq.lsp");
.....
</script>

The first code line above includes Vue.js from a hosted service, a so called CDN repository. The second line above includes the client side SMQ JavaScript stack from the online test server. SMQ is the pub/sub protocol we use in this example. SMQ is similar to other pub/sub protocols such as MQTT, however, SMQ also provides some features not found in typical pub/sub protocols such as one-to-one messaging and the sender's address. These extended pub/sub features are used in this example when sending a "Set Event" to a specific light bulb and when discovering new Light Switch Apps and Light Bulb Apps. SMQ, touted as an IoT protocol, has been specifically designed to enable developers to easily design multiuser reactive device management applications.

Notice how we use the hard coded secure WebSocket URL wss://simplemq.com/smq.lsp in the above example. This construction makes it possible to run this example without having to install any web server software. In a real application, the URL would normally be to the origin server -- i.e. to the server from where the web page was loaded.

The main Vue.js application is as declared as follows:

<div id="app">
  <switchcomp v-for="bulb in bulbs" v-bind:bulb="bulb"
     v-bind:key="bulb.etid">
  </switchcomp>
</div>

The above HTML looking code is managed by Vue.js. The <switchcomp> element is a custom Vue.js component declared in the application. The <switchcomp> element is expanded/emitted by Vue.js for each Light Bulb App that connects to the broker. Vue.js' components enable us to create HTML snippets that can be dynamically inserted and removed in a running Single Page Application.

The HTML template for the <switchcomp> element is as follows:

<div class="switch">
  <div>
    <input type="checkbox" v-on:change="lightswitch" v-model="checked" />
    <div data-unchecked="On" data-checked="Off"></div>
  </div>
</div>

Notice the checkbox in the above HTML template. The div's and the checkbox renders the HTML Light Switch as shown in figure 2. Thanks to modern CSS, the template renders as an element that resembles a real light switch.

The above two HTML snippets probably look like gobbledygook if you are an embedded engineer or if you are a web developer that is new to reactive frameworks such as Vue.js. It is outside the scope of this article to explain how Vue.js works and how the Vue.js part of this example works. You can find many good written tutorials and video tutorials online.

If you are an embedded computer programmer working for a company and you find Vue.js difficult to understand, a recommendation for you is to focus on the server side and use a Vue.js developer for the client side application.

When the Light Switch application has connected to the online broker, the app publishes the following empty message to the pub/sub topic "/switchui/hello":

smq.publish("", "/switchui/hello");

The simulated Light Bulb App subscribes to the same topic:

smq.subscribe("/switchui/hello", {
    onmsg:function(msg,ptid) {
        smq.pubjson({on:on}, ptid, "/switch/set");
    }
});

The 'onmsg' is a callback that gets called when the Light Bulb App receives a "hello" message from a Light Switch App. The callback responds by sending a one-to-one message with the light bulb's on/off state back to the sender of the message. One of the features of the SMQ protocol is to provide a unique address for each connected client. This is referred to as the ephemeral topic ID or etid for short. The acronym ptid means the publisher's etid and is simply the ID of the sender. The smq.pubjson() call above sends the on/off state as a JSON message back to the sender -- i.e. back to the Light Switch App that just connected.

The Light Switch App receives the one-to-one message sent by the Light Bulb App as follows:

smq.subscribe("self", "/switch/set", {datatype:"json", onmsg:onSet});

Subscribing to 'self' means subscribing to messages sent directly to the application and not via a standard named topic; in other words, the application subscribes to messages sent to the application's etid.

We register the callback function onSet above in the subscribe call. This function gets called when a Light Bulb App sends it's on/off state.

function onSet(msg, ptid) {
    var bulb = app.bulbs[ptid];
    if(bulb)
        bulb.lightSwitch.checked=msg.on;
    else
    {
        Vue.set(app.bulbs, ptid, {on:msg.on,etid:ptid});
        smq.observe(ptid, function() {
            Vue.delete(app.bulbs, ptid);
        });

    }
};

As we mentioned above, each SMQ client gets a unique id referred to as etid. The onSet function receives the publisher's etid as the second argument. We use this etid as a key for looking up the Light Switch's Vue.js component corresponding to the etid. If it is found, we set the checked element on the checkbox. Vue.js reactive logic will then automatically update the user interface to the correct on/off state. We have a new Light Bulb if the Light Bulb's etid is not found and If so, we use Vue.js to dynamically create a new light switch.

Notice the smq.observer() call above. This construction makes the Light Switch remove the visual Light Switch in the user interface should the Light Bulb App close i.e. -- if you close the Light Bulb Apps' browser window, the Light Switch is automatically removed from the UI.

In addition to sending a direct one-to-one message from a Light Bulb to a Light Switch when the Light Switch first starts, the Light Bulb must also send a one-to-many message when it's light on/off state changes. This makes sure all Light Switch Apps stay synchronized.

The Light Bulb publishes to the "/switch/set" topic:

smq.pubjson({on:on}, "/switch/set");

All Light Switch Apps subscribe to this one-to-many message:

smq.subscribe("/switch/set", {datatype:"json", onmsg:onSet});

Notice that we use the same onSet callback for the above named topic name subscription as we do for subscribing to the etid (self) in the previous code snippet above.

Run or Download the Examples:

You may download the complete code or run the Light Switch and Light Bulb App directly by clicking the links below, but checkout the following video first. The video shows how to use the two apps.

  1. Start Light Bulb App
  2. Start Light Switch App
  3. Download the Light Switch and Light Bulb App

For more information on SMQ, see the online SMQ documentation and, in particular, the documentation for the SMQ JavaScript client used in the above code snippets. SMQ can also be used as a general IoT protocol. For more information, check out the online IoT cluster example.

Posted in Whitepapers