Browse our Blog. You will find multiple applications, solutions, code examples. Navigate using the tag cloud or search using specific criteria
Using Bootstrap toolkit for Arduino based PLC's
developing with HTML, CSS, and JS
Introduction
Bootstrap is the most popular HTML, CSS and JS library to build responsive web sites. 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 on this post that will show you a new way to represent your web site 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 clinet have to collected from a external server. If the client doesn't have internet access then the web server 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 have 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);
}