Using Bootstrap toolkit for Arduino based PLC's

developing with HTML, CSS, and JS
March 20, 2019 by
Using Bootstrap toolkit for Arduino based PLC's
Bernat Garcia
| 1 Comment

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

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);
}

 
 

​Search in our Blog

Using Bootstrap toolkit for Arduino based PLC's
Bernat Garcia March 20, 2019

Looking for your ideal Programmable Logic Controller?

Take a look at this product comparison with other industrial controllers Arduino-based. 

We are comparing inputs, outputs, communications and other features with the ones of the relevant brands.


Industrial PLC comparison >>>