Introduction
Bootstrap is the most popular HTML, CSS and JS library to build responsive websites. Using this library we will be able to create a very well looking website that will show the data from our Arduino based PLCs.Â
So if you are tired of the typical and boring look of your websites made with basic http, take a look at this post that will show you a new way to represent your website dashboards. Â
Requirements
Internet access
Internet access
Application Architecture
Bootstrap web server requires that the client has internet access to visualize the web. This requirement is because the Arduino based PLC, in this case, an M-Duino, will pass some script to the client and the client has to collect from an external server. If the client doesn't have internet access then the webserver won't look as expected.Â
Arduino HTTP server library will be able to create the HTTP content in our M-Duino. This content is an HTML dynamic file that is placed in our controller into the SRAM as a string. Of course, this content has some limitations. The content will be limited for the SRAM.Â
Software
Below is showed the final code for our industrial Arudino based controller:
/*
  Copyright (c) 2019 Boot&Work Corp., S.L. All rights reserved
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU Lesser General Public License for more details.
  You should have received a copy of the GNU Lesser General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef DEBUG
#define DEBUG 0
#endif
#ifdef MDUINO_PLUS
#include <Ethernet2.h>
#else
#include <Ethernet.h>
#endif
#include <HttpServer.h>
byte mac[] = {0xAF, 0xBE, 0xCD, 0xDC, 0xEB, 0xFA};
IPAddress ip(10, 10, 11, 16);
HttpServer http;
uint32_t counter = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
 Serial.begin(9600UL);
#if DEBUG
 while (!Serial);
#endif
 Ethernet.begin(mac, ip);
 attachInterrupt(digitalPinToInterrupt(I0_6), count, RISING);
#if DEBUG
 Serial.print("IP address: ");
 Serial.println(Ethernet.localIP());
#endif
 http.begin();
#if DEBUG
 Serial.println("HTTP server started");
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
 http.update();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void count() {
 ++counter;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void httpServerEvent(const HttpRequest &req, HttpResponse &res) {
#if DEBUG
 Serial.print("method: ");
 Serial.println(req.method);
 Serial.print("route: ");
 Serial.println(req.route);
 Serial.print("query string: ");
 Serial.println(req.queryString);
 Serial.print("body: ");
 Serial.println(req.body);
#endif
 if (req.route == "/") {
  serveRoot(req, res);
 } else if (req.route == "/status") {
  serveStatus(req, res);
 } else if (req.route == "/js") {
  serverScript(req, res);
 } else if (req.route == "/var/counter/reset") {
  counter = 0;
  res.text(String(counter));
 } else if (req.route == "/q0_0/HIGH") {
  digitalWrite(Q0_0, HIGH);
  res.text("ON");
 } else if (req.route == "/q0_0/LOW") {
  digitalWrite(Q0_0, LOW);
  res.text("OFF");
 } else if (req.route == "/q0_1/HIGH") {
  digitalWrite(Q0_1, HIGH);
  res.text("ON");
 } else if (req.route == "/q0_1/LOW") {
  digitalWrite(Q0_1, LOW);
  res.text("OFF");
 } else {
  // Send an error
  res.send("Not Found", "text/plain", 404, "Not Found");
 }
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void serveRoot(const HttpRequest &req, HttpResponse &res) {
  // Send HTML content
  String html = "<!doctype html>"
  "<html>"
  "<head>"
  "<meta charset='utf-8'>"
  "<meta name='viewport' content='width=device-width,initial-scale=1,shrink-to-fit=no'>"
  "<title>Web monitor by Industrial Shields</title>"
  "<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'>"
  "</head>"
  "<body>"
  "<div class='container'>"
  // TOOLBAR
  "<nav class='navbar navbar-expand-lg'>"
  "<a class='navbar-brand href='#'>Web monitor by Industrial Shields</a>"
  "</nav>"
  // VALUES TABLE
  "<div class='row'>"
  "<h3>Values</h3>"
  "</div>"
  "<div class='row'>"
  "<table class='table table-striped'>"
  "<thead>"
  "<tr>"
  "<th scope='col'>Name</th>"
  "<th scope='col' class='text-right'>Value</th>"
  "<th scope='col'>Actions</th>"
  "</tr>"
  "</thead>"
  "<tbody>"
  "<tr>"
  "<td>Millis</td>"
  "<td class='text-right' id='millis'></td>"
  "<td></td>"
  "</tr>"
  "<tr>"
  "<td>I0.5</td>"
  "<td class='text-right' id='i0_5'></td>"
  "<td></td>"
  "</tr>"
  "<tr>"
  "<td>I0.6 counter</td>"
  "<td class='text-right' id='counter'></td>"
  "<td><button type='button' class='btn btn-primary' onclick='resetCounter()'>Reset</td>"
  "</tr>"
  "</tbody>"
  "</table>"
  "</div>"
  // OUTPUTS ACTIONS
  "<div class='row'>"
  "<h3>Outputs</h3>"
  "</div>"
  "<div class='row'>"
  "<div class='col-4'>"
  "RED LED <span class='badge badge-primary' id='q0_0'></span>"
  "</div>"
  "<div class='col-4'>"
  "<button type='button' class='btn btn-danger' onclick='setQ0_0(\"HIGH\")'>ON</button>"
  "<button type='button' class='btn btn-secondary' onclick='setQ0_0(\"LOW\")'>OFF</button>"
  "</div>"
  "</div>"
  "<div class='row'>"
  "<div class='col-4'>"
  "GREEN LED <span class='badge badge-primary' id='q0_1'></span>"
  "</div>"
  "<div class='col-4'>"
  "<button type='button' class='btn btn-success' onclick='setQ0_1(\"HIGH\")'>ON</button>"
  "<button type='button' class='btn btn-secondary' onclick='setQ0_1(\"LOW\")'>OFF</button>"
  "</div>"
  "</div>"
  "</div>"
  "<script src='https://code.jquery.com/jquery-3.3.1.min.js'></script>"
  "<script src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js'></script>"
  "<script src='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js'></script>"
  "<script src='/js'></script>"
  "</body>"
  "</html>";
  res.html(html);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void serverScript(const HttpRequest &req, HttpResponse &res) {
 String content = "function update() {"
  "$.get('/status', function(d){"
  "var s = d.split(' ');"
  "document.getElementById('millis').innerText = s[0];"
  "document.getElementById('i0_5').innerText = s[1];"
  "document.getElementById('counter').innerText = s[2];"
  "document.getElementById('q0_0').innerText = s[3];"
  "document.getElementById('q0_1').innerText = s[4];"
  "});"
  "}"
  "function resetCounter() {$.post('/var/counter/reset', function(d) {document.getElementById('counter').innerText = d;});}"
  "function setQ0_0(v) {$.post('/q0_0/' + v, function(d) {document.getElementById('q0_0').innerText = d;});}"
  "function setQ0_1(v) {$.post('/q0_1/' + v, function(d) {document.getElementById('q0_1').innerText = d;});}"
  "setInterval(update, 1000);";
 res.send(content, "application/javascript");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void serveStatus(const HttpRequest &req, HttpResponse &res) {
 String content;
 content += String(millis());
 content += ' ';
 content += String(digitalRead(I0_5) ? "ON" : "OFF");
 content += ' ';
 content += String(counter);
 content += ' ';
 content += String(digitalRead(Q0_0) ? "ON" : "OFF");
 content += ' ';
 content += String(digitalRead(Q0_1) ? "ON" : "OFF");
 res.text(content);
}
Using Bootstrap toolkit for Arduino based PLC's