Expedite Your Embedded Linux Web Interface Design

Learn how to design a web based device management application with a working menu system, including user authentication, and how to interact with real world objects in less than 30 minutes.

The following hands-on (Embedded) Linux getting started guide shows how to (cross) compile the server's C code, how to use a ready to use web based dashboard application as your web based device management foundation, and how to extend the server for interacting with real world objects.

Linux Logo Desktop Linux
or
Embedded Linux
Barracuda App Server Logo Barracuda App Server
C source code library including the Lua scripting engine
SQLite Logo SQLite is a C library that implements a small SQL database engine

Using Windows and not Linux?

Windows 10 and up can run Linux. You can install the Windows Subsystem for Linux (WSL) and run this hands-on tutorial in the WSL shell.

Prerequisite:

The following tools are required:

sudo apt install git gcc make swig

Compiling the C Source Code

As explained on the main page, the Mako Server requires the Barracuda App Server C Source Code Library. The following instructions show how to compile the Mako Server, compile the Barracuda App Server Library, and link the components together. The SQLite database engine is also included, but you can easily exclude SQLite from the build if not needed.

Mako Server Block Diagram

We provide a ready to run script that you can initially use for downloading all required code and for compiling and/or cross compiling the source code for your (embedded) Linux. Download and (cross) compile the code for (embedded) Linux as follows:

export CC=/path/2/compiler wget -O - https://raw.githubusercontent.com/RealTimeLogic/BAS/main/LinuxBuild.sh | bash

The script will use your default gcc host compiler if you do not set the CC environment variable. As an example, the following cross compiles the code using Toradex's iMX6 toolchain.

#set the CC environment variable using the toolchain script ./angstrom-glibc-x86_64-armv7at2hf-neon-v2017.12-toolchain.sh wget -O - https://raw.githubusercontent.com/RealTimeLogic/BAS/main/LinuxBuild.sh | bash

You can re-run the script at any time to re-compile for a different target. The script is intelligent and only downloads the code if not already downloaded.

If you cross compiled the code, copy the compiled binary 'mako' and the Mako Server's resource file 'mako.zip' to your embedded Linux system. You may also want to compile the server for your host computer and initially use the host version for the tutorials below (compile the code without setting the CC environment variable).

Test the compiled server as follows; press CTRL-C when done testing:

./mako

Web Based Device Management Foundation

The ready to use Light Dashboard App is a solid device management foundation with its ready to use navigation menu engine and authentication logic.

Web Based Device Management Dashboard

Execute the following to download all examples, including the Light Dashboard app, and to instruct the Mako Server to load the Light Dashboard app.

git clone https://github.com/RealTimeLogic/LSP-Examples.git ./mako -l::LSP-Examples/Light-Dashboard/www
  1. You should see printouts from the Mako Server, including the HTTP listening port number. This port number will most likely be 9357.
  2. Using your browser, navigate to http://localhost:9357/
  3. You should see a login page
  4. Authenticate using the username admin and password qwerty (the world's best username/password combo; Yay)
  5. When done testing, stop the server by typing CTRL-C in the console

The Light Dashboard app acts as a mini content management system, and you can easily modify, remove, and add new pages to the dashboard app. See the tutorial How to Build an Interactive Dashboard App for details.

Interacting with Real World Objects

Server side applications are preferably designed in Lua and a recommendation is to initially check out our Online Interactive Lua Tutorials if you are new to the Lua programming language. The server also provides an extensive C API; however, Lua is what will help you expedite your development 👍

Using Existing Lua APIs

Lua is easy to extend and you can add your own C code to the Mako Server and make it possible for Lua to call your C code API via a so called Lua binding. However, on Linux, the Mako Server includes many APIs that enable you to interact with other Linux processes using files, pipes, sockets, etc. In the following example, we will extract information produced by the Linux kernel without adding additional C code.

The Light Dashboard app includes a JavaScript powered gauge. We can add new code on the server side that sends real time events to update the guage running in all connected browsers. The following Lua code starts the command 'top' as a subprocess using the forkpty API and continuously reads data produced by the 'top' command. The Lua code extracts the CPU usage information and sends the data as an event to all connected browsers.

-- pty read callback. Reads data asynchronously from command: top -i -b
local function readTopData(pty)
   -- Wait for 'top' data
   local data = pty:read()
   while data do
      -- Extract data from the line:
      -- %Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
      local us,sy,ni=data:match"(%d+%.%d+)%s+us.-(%d+%.%d+)%s+sy.-(%d+%.%d+)%s+ni"
      if us then
         local total=tonumber(us)+tonumber(sy)+tonumber(ni)
         trace("CPU:",total)
         -- total is between 0 and 100. We need this value to be between 0 and 180.
         angle = math.tointeger(total*1.8)
         smq:publish({angle=angle},"slider")
      end
      data = pty:read()
   end
end

-- Run the Linux command 'top -i -b' as a subprocess
local pty,err = ba.forkpty({async=readTopData}, "/usr/bin/top", "-i", "-b")
if not pty then
   trace("Cannot open 'top'",err)
end

function onunload()
   if pty then pty:terminate() end
end

Example 1: Lua code for continuously reading data from 'top -i -b' and for converting the data to cpu load.

The above Lua code is designed to interact with the existing Lua code in the Light-Dashboard's .preload script, which sets up an SMQ IoT Broker and enables the WebSocket page (the gauge/slider page) to connect to the SMQ broker instance. SMQ uses WebSockets when communicating with browsers. The real-time gauge design follows the design pattern outlined in the tutorial Modern Approach to Embedding a Web Server in a Device.

  1. Copy the Lua code in Example 1
  2. Open the following file in an editor: ./LSP-Examples/Light-Dashboard/www/.preload
  3. Add (paste) the copied code at the end of the .preload script and save the file
  4. Load the modified dashboard using the Mako Server:
    ./mako -l::LSP-Examples/Light-Dashboard/www
  5. Using your browser, navigate to http://localhost:9357/ and login
  6. Go to the "WebSockets" page
  7. Introduce system load by, for example, running the online benchmark tool.
  8. You should see the gauge move as the system load changes.

Adding New Lua APIs

In a nutshell, Lua is a compact embeddable scripting engine with a flexible extension mechanism, making it easy to add new Lua functions that map to underlying C or C++ functions. This mapping is called a Lua binding and consists of either hand crafted or auto generated C/C++ code that interfaces Lua to C/C++ library functions, classes, and methods.

Let's say we want to make a Lua API for the following C function, which calculates the average CPU load by using data from "/proc/stat". The C function is designed to be polled at regular intervals, and a delta value is calculated from the total time provided by "/proc/stat" each time the function is called. See /proc/stat explained for details.

#include<stdlib.h>
#include<string.h>
#include<stdio.h>

int cpu(void)
{
   FILE* fp = fopen("/proc/stat","r");
   if(fp)
   {
      static long long prevTotal=0,prevIdle;
      static const char d[2] = " ";
      char str[100];
      char* token;
      long long total,idle=0;
      int i;
      if(fgets(str,100,fp));/* ignore return value */
      fclose(fp);
      token = strtok(str,d); /* Skip 'cpu' */
      for(i=0,total=0 ; NULL != (token = strtok(NULL,d)) && i< 7 ; i++)
      {
         if(3 == i)
            idle = atoll(token);
         else
            total += atoll(token);
      }
      if(prevTotal)
      {  /* if not the very first time this function is called */
         int deltaTotal = total - prevTotal;
         int deltaIdle = idle - prevIdle;
         i = deltaTotal * 100 / (deltaTotal + deltaIdle);
      }
      else
         i=0; /* We have no data */
      prevTotal=total;
      prevIdle=idle;
      return i; /* percentage CPU since last call */
   }
   return 0;
}

Example 2: C code for reading data from "/proc/stat" and for converting the data to cpu load.

Note that we could have easily implemented the above C function directly in Lua using Lua's IO functions and the irony is that this function would have been easier to implement in Lua. However, we simply need a C function that we can use for creating the same feature we provided by using the Lua code in Example 1 and at the same time show how to map Lua to C code.

A Lua binding can be hand crafted or created automatically. The following shows how to create bindings automatically, but before proceeding, check out the Online Interactive Lua Binding Tutorial which provides introductory information and shows how to manually create a basic Lua binding.

As the Online Interactive Lua Binding Tutorial explains, the Mako Server can dynamically load Lua bindings embedded in a shared library at runtime. In the following example, we will instead directly embed the 'cpu' function and the 'cpu' Lua binding in the Mako Server executable. You should also check out the tutorial Building C/C++ Modules if you are interested in loading Lua APIs dynamically.

Automatic Lua Binding Generation:

SWIG is an interface compiler that connects programs written in C and C++ with scripting languages, including Lua. It works by taking the declarations found in C/C++ header files and using them to generate the wrapper code (Lua bindings) needed to access the underlying C/C++ code from a Lua script. In addition, SWIG provides a variety of customization features that let you tailor the wrapping process to suit your application.

To use SWIG, you start by making a SWIG interface file as shown below:

%module cpu /* The module name used by your Lua code */
 
%{
   /* Put headers and other declarations here */
   int cpu(void);
%}

/* Put the same headers here, or just the function declarations you
   want exposed to Lua.
*/
int cpu(void);

Example 3: SWIG interface file for the cpu() function.

Compile Mako Server with the new 'cpu' C code:

  1. Copy the C code above (Example 2), create BAS/MyCustomBindings.c, paste the C code into this new file, and save the file.
  2. Copy the SWIG interface code above (Example 3), create BAS/MyCustomBindings.i, paste the interface code into this new file, and save the file.
  3. Execute the following commands in the console:
    cd BAS/ make -f mako.mk

The mako.mk makefile should run the command 'swig -lua MyCustomBindings.i', which produces the file MyCustomBindings_wrap.c. The makefile then compiles all code and links it together, producing a new Mako Server executable with our new 'cpu' Lua api. Note that you can also cross compile using the mako.mk makefile by either setting the CC environment variable or by directly adding the line CC=.... at the top of mako.mk.

You will find the following function in the auto generated C file MyCustomBindings_wrap.c.

int luaopen_cpu(lua_State* L);

The Mako Server C code BAS/examples/MakoServer/src/MakoMain.c must call this function at startup so the 'cpu' API gets registered. However, you do not need to modify MakoMain.c since it includes the following construction:

myCustomBindings(L);

The function is a macro that normally expands to nothing, but the makefile sets the following when compiling the code:
-DmyCustomBindings=luaopen_cpu

Open MakoMain.c in an editor and search for myCustomBindings(L). You can add additional APIs at the same code location.

How to use the new 'cpu' Lua binding:

  1. Open the following file in an editor: LSP-Examples/Light-Dashboard/www/.preload
  2. Delete the code you previosly added (the Lua code from Example 1); alternatively, revert the code using git
  3. Copy the Lua code in Example 4 below
  4. Add (paste) the copied code at the end of the .preload script and save the file
  5. Load the dashboard using the new Mako Server executable in the BAS/ directory:
    ./mako -l::../LSP-Examples/Light-Dashboard/www
  6. Using your browser, navigate to http://localhost:9357/ and login
  7. Go to the "WebSockets" page
  8. Introduce system load
  9. You should see the gauge move as the system load changes
local function pollCPU()
    -- Call the auto generated 'CPU' Lua binding, which subsequently
    -- calls the C function in Example 2.
   local cpu = cpu.cpu()
   trace("CPU:",cpu)
   -- cpu is between 0 and 100. We need this value to be between 0 and 180.
   angle = math.tointeger(cpu*1.8)
   smq:publish({angle=angle},"slider")
   return true -- interval timer
end

local timer = ba.timer(pollCPU)
timer:set(1000) -- poll every second

function onunload()
   timer:cancel()
end

Example 4: Lua code designed for polling the new CPU Lua binding every second.

When to automatically create bindings using SWIG:

The CPU Lua binding we auto created would have been super easy to hand craft since the cpu() function provides such a basic API. However, automatically generating Lua bindings using SWIG is easy and convenient when you have a large set of functions/methods/classes you want to make available to Lua. For example, we use SWIG when auto generating bindings for a huge set of GPIO functions for the Barracuda App Server ESP32 Tutorial. Check out our ESP32 SWIG interface file on GitHub. See the Lua specific SWIG documentation for details on how to use SWIG with Lua. You may also want to check out an old, but still relevant C++ SWIG Tutorial.

All Lua bindings in the Barracuda App Server library are hand crafted, which makes the code smaller. Hand crafted bindings provide options not possible with auto generated code, such as making a friendlier Lua API. Some bindings such as the forkpty API we used in Example 1 cannot be auto generated since the API provides an asynchronous event based mechanism for sending events from C code to the Lua world. Our advanced Lua binding tutorial explains how to call Lua asynchronously from C code.

Next Step

You can use what you have learning in this tutorial as a foundation for your IoT and/or web based device management application. Here is a collection of tutorials you may find useful when working with the Mako Server.

Mako Server Linux Tutorials:

Discover More:

Whether you are a maker, a startup, or a large business, we've got you covered. Please send us an email if you have any questions or if you are unsure on what product to select. We are here to help you find the best solution, and we'd really like to help you with your hardware/software project challenges.


OPC-UA

OPC-UA Client & Server

An easy to use OPC UA stack that enables bridging of OPC-UA enabled industrial products with cloud services, IT, and HTML5 user interfaces.

Edge Controller

Edge Controller

Use our user programmable Edge-Controller as a tool to accelerate development of the next generation industrial edge products and to facilitate rapid IoT and IIoT development.

On-Premises IoT

On-Premises IoT Platform

Learn how to use the Barracuda App Server as your On-Premises IoT Foundation.

Embedded Web Server

Barracuda Embedded Web Server

The compact Web Server C library is included in the Barracuda App Server protocol suite but can also be used standalone.

WebSocket Server

Microcontroller Friendly

The tiny Minnow Server enables modern web server user interfaces to be used as the graphical front end for tiny microcontrollers. Make sure to check out the reference design and the Minnow Server design guide.

WebDAV Server

Network File System

Why use FTP when you can use your device as a secure network drive.

HTTP Client

Secure HTTP Client Library

PikeHTTP is a compact and secure HTTP client C library that greatly simplifies the design of HTTP/REST style apps in C or C++.

WebSocket Client

Microcontroller Friendly

The embedded WebSocket C library lets developers design tiny and secure IoT applications based on the WebSocket protocol.

SMTP Client

Secure Embedded SMTP Library

Send alarms and other notifications from any microcontroller powered product.

Crypto Library

RayCrypto C Library

The RayCrypto engine is an extremely small and fast embedded crypto library designed specifically for embedded resource-constrained devices.

Embedded PKI Service

Automatic SSL Certificate Management for Devices

Real Time Logic's SharkTrust™ service is an automatic Public Key Infrastructure (PKI) solution for products containing an Embedded Web Server.

Modbus

Modbus TCP client

The Modbus client enables bridging of Modbus enabled industrial products with modern IoT devices and HTML5 powered HMIs.

Posted in Tutorials