Part I: WebSockets for Real-Time Web and IoT Applications

carlos-muza-900pxOpt

Have you ever wanted to display your real-time sensor data on a webpage that can be viewed on any device with a modern web browser? How about a web page that serves as a dashboard for controlling and monitoring your embedded device in real-time? With WebSockets, you can do exactly that! In this article, we will briefly review what a WebSocket is, how it works, its benefits, and dive into a system-monitoring, dashboard-type application tutorial.

Try Our ARM® Embedded IoT Dev Kit

Netburner ARM Cortex M7 embedded Development Kit for IoT product development and industrial automation.

Or, learn more about NetBurner IoT.

I. What is a WebSocket?

A WebSocket is a low-latency (real-time), full-duplex (bidirectional), long-running (persistent), single connection (TCP) between a client and server. WebSockets provide a huge benefit for real-time, event-driven web applications. It is used for real-time data updates and synchronization, live text chat, video conferencing, VOIP, IoT control and monitoring. These capabilities enable apps in gaming, social networks, logistics, finance and home, vehicle and industrial automation, to name a few. Most major web browsers currently support their use.

WebSockets vs HTTP & AJAX

The full-duplex aspect of WebSockets allows a server to initiate communication with a client whenever it would like, which is contrary to other protocols such as HTTP and AJAX in which the client must initiate communication. For us embedded folks, we can liken the WebSocket protocol to an interrupt based application instead of a polled application. In web-speak we are breaking from an HTTP based paradigm and trappings where a client must periodically initiate a connection to the server and then place a request to “pull” new data or state information. Furthermore, in HTTP, the server cannot “push” new data to the client as states change, but must first wait for a request from the client. This can be detrimental for real-time applications especially as client request frequencies scale.

WebSockets takes a different approach by establishing a persistent, bidirectional connection that allows the server to update the client application without an initiating request from the client. This not only allows for low-latency communication between a server and client, but it also reduces network traffic by eliminating the need for a client to send a packet just to request data from the server – The server can just send data as soon as it’s available or as states have changed. No request necessary. That’s a good reason why WebSockets are commonly used for applications like updating real-time stock prices, traffic reports, browser based multiplayer games, etc.

Connect with HTTP-Compatibility

The WebSocket protocol was designed to work well with the legacy web infrastructure. It uses an HTTP-compatible handshake to establish a connection between the client and server. The process to start a WebSocket connection begins with the client sending an HTTP GET with the WebSocket “Upgrade” field. After a connection is initialized, communication switches to a bidirectional binary protocol unlike the HTTP protocol.

GET /INDEX HTTP/1.1
Host: 192.168.1.220
Connection: Upgrade
Upgrade: websocket
Origin: http://192.168.1.220

If the server supports WebSockets, it responds with a HTTP UPGRADE response.

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

At this point the TCP socket used for the HTTP communication is recycled to be used for the WebSocket connection, and either party can begin sending data. WebSockets use the same ports as HTTP – 80 for HTTP, and 443 for HTTPS. To list yet another benefit of WebSockets, a WebSocket header is only 2 to 6 bytes long.

As a comparison, Google’s SPDY research reports HTTP request headers can vary in size from ~200 bytes to over 2KB. Think of all the network traffic and communication latency you can reduce by switching from an HTTP-based implementation to a WebSocket implementation!

II. Display Real-Time DIP Switch States with JavaScript and WebSockets

We have several examples included in our NetBurner Network Development Kit (NNDK) tools to help you get started with WebSockets. We’ve added the following example to help you display a local volatile variable on a webpage in real-time – all hosted on your NetBurner. This example also uses JavaScript to establish a WebSocket connection from the web page, and JSON (JavaScript Object Notation) to communicate the value of the local variable from the server (NetBurner) to the client (web browser).

1. Set up Project in IDE

To get started, you’ll need to download the example from the NetBurner GitHub repository, create a new project in the NBEclipse IDE, and import the source files that you downloaded. This process is described in detail in the NBEclipse Getting Started Guide that can be found on our docs webpage or in \nburn\docs\NetBurner\Developer\ of your NNDK install.

2. Starting an HTTP Server

Let’s begin by reviewing how to start the HTTP server and setup the WebSocket C++ backend. In main.cpp of your new project you will see the following code, assuming you imported the Git repo correctly.

OSSemInit( &SockReadySem, 0 );
StartHTTP();
TheWSHandler = MyDoWSUpgrade;
OSSimpleTaskCreate(InputTask, MAIN_PRIO - 1);

Starting an HTTP server is as simple as calling the StartHTTP() function. This will allow us to access the webpage defined in the /html directory of our source files by directing our web browser to the IP address of our NetBurner. We will use a semaphore to communicate between tasks that a WebSocket connection has been established.

TheWSHandler is a callback function used by the HTTP task whenever a HTTP GET request with the WebSocket “Upgrade” field is received. This callback function oversees handling the WebSocket upgrade process and it must be defined by the user. Last, we create a task, called InputTask, to read from the WebSocket whenever data is available and handle errors.

2. Upgrading to a WebSocket connection with C++

Let’s take a look at the callback function that handles the WebSocket upgrade. A snippet is shown in the code block below or you can look at it in your project’s main.cpp. This function gets passed in the URL of which the upgrade is being requested. Our HTML webpage, located in the /html directory of the source files, is named index.html. Therefore, we parse the source URL for the “INDEX” string.

If you decide to add additional webpages that each use WebSockets, this is where you would parse the separate URLs so that you can have separate WebSockets per webpage.

int MyDoWSUpgrade( HTTP_Request *req, int sock, PSTR url, PSTR rxb ){
  if( httpstricmp( url, "INDEX" ) ){
    if( ws_fd < 0 ){
      int rv = WSUpgrade( req, sock );
      if( rv >= 0 ){
        ws_fd = rv;
        NB::WebSocket::ws_setoption( ws_fd, WS_SO_TEXT );
        OSSemPost( &SockReadySem );
        return 2;
      }
      else{
        return 0;
      }
    }
  }
  NotFoundResponse( sock, url );
  return 0;
}

WSUpgrade() takes care of upgrading the HTTP connection. It will return the file descriptor value for the new WebSocket. We store this value, ws_fd, as a global variable so that all tasks can write, read, or close the connection.

3. Using C++ to write a JSON object to a WebSocket

void SendSwitchesReport( int ws_fd )
{
  SMPoolPtr pq;
  ParsedJsonDataSet JsonOutObject;

  // Assemble JSON object
  JsonOutObject.StartBuilding();
  JsonOutObject.AddObjectStart( "dipSwitches" );
  JsonOutObject.Add( "dip1", dipStates[0] );
  JsonOutObject.Add( "dip2", dipStates[1] );
  JsonOutObject.Add( "dip3", dipStates[2] );
  JsonOutObject.Add( "dip4", dipStates[3] );
  JsonOutObject.Add( "dip5", dipStates[4] );
  JsonOutObject.Add( "dip6", dipStates[5] );
  JsonOutObject.Add( "dip7", dipStates[6] );
  JsonOutObject.Add( "dip8", dipStates[7] );
  JsonOutObject.EndObject();
  JsonOutObject.DoneBuilding();

  // If you would like to print the JSON object to serial to see the format, uncomment the next line
  //JsonOutObject.PrintObject( true );

  // Print JSON object to a buffer and write the buffer to the WebSocket file descriptor
  int dataLen = JsonOutObject.PrintObjectToBuffer( ReportBuffer, REPORT_BUF_SIZE );
  writeall( ws_fd, ReportBuffer, dataLen );
}

After getting the state of the DIP Switches from the NetBurner MOD-DEV-70 carrier board and populating the dipStates array, we call SendSwitchesReport() to send the states in a JSON object. Writing to a WebSocket can be done just like writing to any file descriptor. In this case, we use writeall() so that we can send the whole JSON object in one packet. Otherwise, the JSON object might be sent as several smaller packets, which doesn’t play nice with JavaScript on the browser side.

4. Using JavaScript to establish a WebSocket connection and parse JSON

function MakeDataSocket(){
    if( "WebSocket" in window ){
        if( ( ws == null ) || ( ws.readyState == WebSocket.CLOSED ) ){
            ws = new WebSocket( "ws://" + window.location.hostname + "/INDEX" );
            ws.onopen = function(){};
            ws.onmessage = function( evt ){
                var rxMsg = evt.data;
                report = JSON.parse( rxMsg );

                dip1 = report.dipSwitches.dip1;
                dip2 = report.dipSwitches.dip2;
                dip3 = report.dipSwitches.dip3;
                dip4 = report.dipSwitches.dip4;
                dip5 = report.dipSwitches.dip5;
                dip6 = report.dipSwitches.dip6;
                dip7 = report.dipSwitches.dip7;
                dip8 = report.dipSwitches.dip8;

                updateCheckBox();
            };
            ws.onclose = function(){};
        }
    }
}

The custom JS function MakeDataSocket is called when the web page is loaded. It’s used to establish a WebSocket connection between the browser and the NetBurner server. It defines an event listener, or callback function, that is called upon receiving data from the WebSocket. That is the onmessage event listener. We use this event listener to parse the JSON object that is sent by SendSwitchesReport() in section 3. This allows us to separate the state of each DIP switch into its own variable that can then be used to display the states of each switch.

5. Display and toggle a CSS switch with JavaScript

function updateCheckBox(){
    if( dip1 === "Off" ){
        document.getElementById( "cbox1" ).checked = false;
    }
    else if(dip1 === "On"){
        document.getElementById( "cbox1" ).checked = true;
    }
}

Now that each DIP switch state has been parsed into its own variable, we can use that variable to toggle a CSS switch (a stylized HTML checkbox) as a cool animation on the webpage. The onmessage event listener calls updateCheckBox() upon receiving a message, which then updates the CSS switch that is displayed on the webpage.

 

  <input id="cbox1" type="checkbox" class="cbox" disabled />
  <label for="cbox1" class="lbl">Switch 1: 

In the HTML code, each CSS toggle switch has an ID that can be used to change the state of the CSS switch. In the above excerpt of the HTML source code, we define a CSS switch with an ID of “cbox1.” The “cbox1” CSS switch is set to true or false by updateCheckBox() – and updateCheckBox() received the state of the MOD-DEV-70 DIP switches by parsing the JSON object in the onmessage event listener.

After all of that is said and done, you get a webpage that displays the real-time state of the MOD-DEV-70 DIP switches with CSS toggle switches. Go ahead and flip your DIP switches on your hardware and watch the webpage CSS switches toggle away!

III. Secure WebSockets

In our recent NetBurner 3.0 release of the NNDK tools, we’ve added support for WebSockets over an SSL/TLS connection. We’ve also updated this series to get you comfortable with implementing WebSockets Secure with your NetBurner. In part III of this tutorial series we’ll show you how to set that up, building upon what we have done already.

IV. Wrapping it up

You can see how this makes for a very powerful method for real-time monitoring of your NetBurner. By way of extension we hope it is all clear how you would monitor your sensors, controls, and process that might also be integrated with your NetBurner hardware. But if not, we’d love to see your comments and questions. What is particularly advantageous with the WebSockets approach is that you can update the state of your webpage or dashboard without reloading the page – much like AJAX but in some ways much better for real-time and distributed applications since it also reduces the overhead and burden on the server and clients themselves.

Move on to our WebSockets Part II article where we provide an example of a monitoring AND control application using a NetBurner Dev Kit as well as more information on support for WebSocket over SSL/TLS!

Share this post

Subscribe to our Newsletter

Get monthly updates from our Learn Blog with the latest in IoT and Embedded technology news, trends, tutorial and best practices. Or just opt in for product change notifications.

3 thoughts on “Part I: WebSockets for Real-Time Web and IoT Applications

  1. Why are websockets not used for IoT control more broadly? Is there a ‘cost’?

    1. Great question! My initial feeling is that websockets is really great if you want to use and speed up the HTTP/HTTPS paradigm for bidirectional (full duplex) real-time connections between a browser and a server. This is great for updating browser based dashboards (running javascript) with near-real-time IoT field data. The relatively new HTTP/2 which offered full duplex connection may repalce websockets for many use cases.

      For many, applications that are more machine to machine (M2M) in nature you can use a more simple approach like MQTT (although there are other options) and send data of over port 1883/8883 instead of HTTP/HTTPS. Something like this would usually be more appropriate than websockets for reading edge sensors and controlling actuator end-points in low latency automation and resource constrained IoT and IIoT applications in my view. One of the big benefits here is that it may be even more lightweight than websockets.

      I really feel that it all comes down to the use case etc. as to why you would use one protocol over the other. It may be that you use a composite of a few protocols to get the job done.

      More on MQTT >> https://www.netburner.com/learn/mqtt-how-to-use-the-protocol-and-why-it-s-great-for-distributed-iot/

    2. There are a few reasons one might choose not to use websockets: Low-power devices that need to sleep might not be able to maintain a websocket connection. A device located out in the field with an intermittent cellular network connection might be better suited for a pub/sub protocol. Some devices might only need to report sensor data at long intervals (daily, hourly) in which case websockets would be overkill. If you’re hosting a server that is communicating with millions of IoT devices using websockets, you’ll need to make sure you have the infrastructure to handle those millions of connections simultaneously, which could be costly.

Leave a Reply
Click to access the login or register cheese