Introduction: ESP32 – Web Server -- Google Gauge – Restaurant Bin Monitoring

This experimentation is about creating an ESP32 (Station Mode), stand-alone HTTP web server and processing / populating decremented counter value at regular interval on HTTP client interface. The web page is developed by using HTML, AJAX and CSS as the subject of “Restaurant Bin Monitoring”. It uses Google Gauge for indicating the status / warning to end user.

Visit the "VESZLE - The Art Of Electronics" you tube channel for further information and videos.

https://www.youtube.com/channel/UCY4mekPLfeFinbQHp...

ESP32 – Web Server -- Google Gauge – Bin Monitoring implementation You tube Video

Step 1: Reference

Please refer the below instruct-able for Component, Schematic, web-server, web-client, HTTP, CSS and AJAX description.

“ESP32 – Getting Started MicroPython -- on Board Blink LED” Instruct-able / You-tube by PugazhM

https://www.instructables.com/ESP32-Getting-Start...

“ESP32-HTTP-Web-Server-HTML-CSS-Simple-Counter” Instruct-able / You-tube by PugazhM

https://www.instructables.com/ESP32-Getting-Start...

“ESP32-Web-Server-HTML-AJAX-Hospital-Visitors-Counter” Instruct-able / You-tube by PugazhM

https://www.instructables.com/ESP32-Getting-Start...

MicroPython Network TCP sockets

https://www.instructables.com/ESP32-Getting-Start...

Step 2: ESP32 HTTP Web Server System Overview

A simple counter implementation is deployed on “web-server” as illustrated above

ESP32 act as “web-server” and Google Chrome running on a laptop act as a “web-client”

timer_1 is initiated and toggles the on-board LED at regular interval, via “BlinkLED()” call back function

timer_2 is initiated and updates the meat, egg, fruit, beverage and vegetable variables at a regular interval, via “UpdateCounter()” call back function, validates the counters for lower limit. If counter value exceeds lower limit, then the python program resets the counters to max value.

Whenever the “web-client” requests data via “HTTP Client Page Request” method, then the “web-server”, will construct the complete web page and then sends back to “web-client”.

Whenever the “web-client” requests data via “HTTP Client Data Request” method, then the “web-server”, will construct the data and then sends back to “web-client”.

The “web-client” refreshes the “web page” at 1 second’s interval.

Step 3: ESP32 HTTP Web Server GUI

A simple counter deployment on a ESP32 “web-server”, with “Restaurant Bin Monitoring” as theme is illustrated above.

The web page is constructed by using “Font Awesome” fonts and material icons. It also uses google charts.

Font Awesome icons are designed to be simple, modern, friendly, and sometimes quirky. Each icon is reduced to its minimal form, expressing essential characteristics.

The style sheet uses “https://use.fontawesome.com/releases/v5.7.2/css/all.css”, for constructing a web page.

Google chart tools are powerful, simple to use, and free. It provides our rich gallery of interactive charts and data tools.

The “web-server” uses “google chart” Java script “https://www.gstatic.com/charts/loader.js”, for creating gauge tool.

The web page is divided into 5 equal vertical columns. Totally 5 boxes.

A box type drawing which uses icon, fonts and Google Gauge for formulating the “Restaurant Bin Monitoring” GUI.

Google Gauge is used for indicating the status / warning of “bin usage”.

Meat / Fish: - Filled in bin 50 kg. Warning indication between 10 kg to 20 kg. Red alert indication for below 10 kg.

Egg: - Filled in bin 800 unit. Warning indication between 100 unit to 250 unit. Red alert indication for below 100 unit.

Fruits: - Filled in bin 180 kg. Warning indication between 30 kg to 75 kg. Red alert indication for below 30 kg.

Beverages: - Filled in bin 150 lit. Warning indication between 30 lit to 60 lit. Red alert indication for below 30 lit.

Vegetable: - Filled in bin 450 kg. Warning indication between 30 kg to 100 kg. Red alert indication for below 30 kg.

Step 4: ESP32 Web Server MicroPython Program

The timer_one is initialized and callbacks the “BlinkLED” functionality for toggling on board LED at 200mS duration. (frequency = 5)

The timer_two is initialized and callbacks the “UpdateCounter” functionality for updating the system variables at 500mS duration. (frequency = 2)

The MicroPython program, first creates and configures the ESP32 as Station Mode, and then connects to the Wi-Fi router using user provided SSID and Password

§ ssid = 'WiFi Router SSID'

§ password = 'WiFi Router Password'

§ station = network.WLAN(network.STA_IF)

§ station.active(True)

§ station.connect(ssid, password)

§ # Wait until the ESP32 get connected to the router

§ while station.isconnected() == False:

§ pass

The MicroPython program creates a stream TCP web-server socket and then binds it into HTTP port 80. Listens from maximum five web clients

§ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

§ s.bind(('', 80))

§ s.listen(5)

The system checks the web-client request at endless while loop.

Whenever the web-server receives the “HTTP Client page request”, then it reconstruct the web page and sends the “HTTP Server Page Response” to web-client.

Whenever the web-server receives the “HTTP Client data request”, then it reconstruct the data and sends to web-client.

'''
 Demonstrates HTML and CSS ESP32 Web server implementation
 Simple Counter usage as "Restaurant Bin Monitoring"
 Simple counter example for counting consumption at regular interval
 ESP32 Web server detection event happens at 0.5 seconds interval,
 And validates / resets against the MIN value
 Web client request at 3 Seconds interval.
 Google Gauge for status / warnning indication
 
 * The ESP32 onboard LED pin connections
 * On board LED pin to GPI2
 
 Name:- M.Pugazhendi
 Date:-  08thOct2021
 Version:- V0.1
 e-mail:- muthuswamy.pugazhendi@gmail.com
'''

from machine import Pin, Timer
import network
import time
import socket

import esp
esp.osdebug(None)

import gc
gc.collect()

ssid = 'WiFi Router SSD'
password = 'WiFi Router Password'

station = network.WLAN(network.STA_IF) # Congigure WLAN as station

station.active(True) # Activate the Station
station.connect(ssid, password) # Connect to the router using SSID and password

# Wait until the ESP32 get connected to the router
while station.isconnected() == False:
  pass

print('Connection successful')
print(station.ifconfig())

led = Pin(2, Pin.OUT)
timer_one = Timer(0) # Initialize timer_one. Used for toggling the on-board LED
timer_two = Timer(1) # Initialize timer_two. Used for updating counter variables

toggle = 1
meat = 50
egg = 800
fruit = 180
beverage = 150
vegetable = 450

def UpdateCounter(timer_two):
    global meat
    global egg
    global fruit
    global beverage
    global vegetable
    
    meat = meat - 1
    if meat <= 0:
        meat = 50
  
    egg = egg - 10
    if egg <= 0:
        egg = 800
        
    fruit = fruit - 5
    if fruit <= 0:
        fruit = 180

    beverage = beverage - 5
    if beverage <= 0:
        beverage = 150
        
    vegetable = vegetable - 25
    if vegetable <= 0:
        vegetable = 450

def BlinkLED(timer_one):
    global toggle
    if toggle == 1:
        led.value(0)
        toggle = 0
    else:
        led.value(1)
        toggle = 1

def ConstructWebPage():
  html = """<html>   
<head>   
    <!-- add the following google font / icon to the webpage -->  
	<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" 
	 integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
	<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
	<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
	 
<style>   
    * {   
        box-sizing: border-box;   
    }
    
    body {  
    /* background-color: #76ff03; */ /*Green */
	background-color: #76ff03;
    background-size: cover;  
     }
      
    .column {   
        /* box toward left of the window screen */  
        float: left;   
        /*divide each counter box in equal size*/  
        /* 4 counters of 20% of screen size*/  
        width: 20%;   
        /*spacing between each box */  
        padding: 4px;   
    }
    #count
    {
        padding: 3px;   
        background-color: #db1b60;   
        color: white;
    }
	#bin_gauge
	{
	    padding: 3px;   
        background-color: blue;   
        color: white;
	}
          
    .row {   
        /* Specify the margin for the row class */  
        margin: 3px;  
    }   
          
    /* Style the class named block*/  
    .block {   
        padding: 3px;   
        text-align: center;   
        /* background-color: #0100ca; */
		background-color: #1d00db; 		
        color: white;   
        }   
    .fa {   
        font-size: 50px;   
    }
	.center {
		position: relative;
		left: 50%;
		top: 50%;
		transform: translate(-60%, +1%);
	}
    </style>
    <script>
    
        var meat=50;
        var egg=800;
        var fruit=180;
        var beverage=150;
        var vegetable=450;
		
       var ajaxRequest = new XMLHttpRequest();   
       function ajaxLoad(ajaxURL)  
       {  
        ajaxRequest.open('GET',ajaxURL,true);  
        ajaxRequest.onreadystatechange = function()  
        {  
         if(ajaxRequest.readyState == 4 && ajaxRequest.status==200)  
         {  
            var ajaxResult = ajaxRequest.responseText;  
            var tmpArray = ajaxResult.split("|");
            document.getElementById('meat_span').innerHTML = tmpArray[0];
			document.getElementById('egg_span').innerHTML = tmpArray[1];			
			document.getElementById('fruit_span').innerHTML = tmpArray[2];
			document.getElementById('beverages_span').innerHTML = tmpArray[3];
			document.getElementById('vegetable_span').innerHTML = tmpArray[4];
			meat = tmpArray[0];
			egg = tmpArray[1];
			fruit = tmpArray[2];
			beverage = tmpArray[3];
			vegetable = tmpArray[4];
         }  
        }  
        ajaxRequest.send();  
       }  
       
      google.charts.load('current', {'packages':['gauge']});
      google.charts.setOnLoadCallback(drawChart);
      
      function drawChart() {

        var bin1_data = google.visualization.arrayToDataTable([
          ['Label', 'Value'],
          ['kilogram', 50],
        ]);
		var bin2_data = google.visualization.arrayToDataTable([
          ['Label', 'Value'],
          ['unit', 800],
        ]);
        var bin3_data = google.visualization.arrayToDataTable([
          ['Label', 'Value'],
          ['kilogram', 180],
        ]);
		var bin4_data = google.visualization.arrayToDataTable([
          ['Label', 'Value'],
          ['litre', 150],
        ]);
		var bin5_data = google.visualization.arrayToDataTable([
          ['Label', 'Value'],
          ['kilogram', 450],
        ]);
        var bin1_options = {
          width:800, height: 240,
		  min: 0, 
		  max: 50,
          redFrom: 0, redTo: 10,
          yellowFrom:10, yellowTo: 25,
          minorTicks: 5
        };
        var bin2_options = {
          width:800, height: 240,
		  min: 0, 
		  max: 800,
          redFrom: 0, redTo: 100,
          yellowFrom:100, yellowTo: 250,
          minorTicks: 5
        };
        var bin3_options = {
          width:800, height: 240,
		  min: 0, 
		  max: 180,
          redFrom: 0, redTo: 30,
          yellowFrom:30, yellowTo: 75,
          minorTicks: 5
        };
        var bin4_options = {
          width:800, height: 240,
		  min: 0, 
		  max: 150,
          redFrom: 0, redTo: 30,
          yellowFrom:30, yellowTo: 60,
          minorTicks: 5
        };
		var bin5_options = {
          width:800, height: 240,
		  min: 0, 
		  max: 450,
          redFrom: 0, redTo: 30,
          yellowFrom:30, yellowTo: 100,
          minorTicks: 5
        };
        var chart1 = new google.visualization.Gauge(document.getElementById('chart_div1'));
		var chart2 = new google.visualization.Gauge(document.getElementById('chart_div2'));
		var chart3 = new google.visualization.Gauge(document.getElementById('chart_div3'));
		var chart4 = new google.visualization.Gauge(document.getElementById('chart_div4'));
		var chart5 = new google.visualization.Gauge(document.getElementById('chart_div5'));

		bin1_data.setValue(0, 1, meat);
		bin2_data.setValue(0, 1, egg);
		bin3_data.setValue(0, 1, fruit);
		bin4_data.setValue(0, 1, beverage);
		bin5_data.setValue(0, 1, vegetable);
        chart1.draw(bin1_data, bin1_options);
		chart2.draw(bin2_data, bin2_options);
		chart3.draw(bin3_data, bin3_options);
		chart4.draw(bin4_data, bin4_options);
		chart5.draw(bin5_data, bin5_options);
      }
      
      function UpdateVisitorCounters()   
       {   
         ajaxLoad('getVisitorCounters');
         drawChart();
       }  
      setInterval(UpdateVisitorCounters, 1000);
	  /* setInterval(drawChart, 1000); */
		
    </script>
      
</head>

<body>   
<center>  
    <h1 style="color:black"> ESP32 HTTP Web Server </h1>
    <h1 style="color:black"> Restaurant Bin Monitoring </h1>
    <h3> HTML, AJAX and CSS formulated GUI on ESP32 Web server </h3>   
     <div class="row">   
			<div class="column">   
				<div class="block" >
					<h3> Meat-Fish Bin </h3>
					<h3> In Kilogram  </h3>
					<p><i class="fas fa-fish fa-5x" style="color:#FFFFFF;"></i></p>
				</div>
				<div id="count">
					<h1><span id='meat_span'>--</span></h1>	
				</div>
				<div id="bin_gauge">		
					<table class="center" id="chart_div1"></table>  
				</div>
			</div>  
				  
			<div class="column">   
				<div class="block"> 
				<h3> Egg Bin </h3>
				<h3> In Units </h3>
				<p><i class="fas fa-egg fa-5x" style="color:#FFFFFF;"></i></p>
				</div>
				<div id="count">		
					<h1><span id='egg_span'>--</span></h1>   
				</div>
				<div id="bin_gauge">		
					<table class="center" id="chart_div2"></table>  
				</div>
			</div>   
				  
			<div class="column">   
				<div class="block">
				<h3> Fruits Bin </h3>
				<h3> In Kilogram </h3>
				<p><i class="fas fa-apple-alt fa-5x" style="color:#FFFFFF;"></i></p>       
				</div>
				<div id="count">		
					<h1><span id='fruit_span'>--</span></h1>   
				</div>
				<div id="bin_gauge">		
					<table class="center" id="chart_div3"></table>  
				</div>
			</div>   
		  
			<div class="column">   
				<div class="block">
				<h3> Beverages Bin </h3>
				<h3> In Litre </h3>
				<p><i class="fas fa-wine-bottle fa-5x" style="color:#FFFFFF;" style="font-size:90px;"></i> </p>
				</div>		
				<div id="count">		
					<h1><span id='beverages_span'>--</span></h1>   
				</div>
				<div id="bin_gauge">		
					<table class="center" id="chart_div4"></table>  
				</div>
			</div>
			
			<div class="column">   
				<div class="block">
				<h3> Vegetables Bin</h3>
				<h3> In Kilogram </h3>
				<p><i class="fas fa-carrot fa-5x" style="color:#FFFFFF;" style="font-size:90px;"></i> </p>
				</div>		
				<div id="count">		
					<h1><span id='vegetable_span'>--</span></h1>   
				</div>
				<div id="bin_gauge">		
					<table class="center" id="chart_div5"></table>  
				</div>
			</div>	
    </div>
</center>  
</body>   
</html> 
"""
  return html

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Create Stram TCP web-server socket
s.bind(('', 80)) # Assign and bind port 80 for HTTP web-server socket
s.listen(5) # Configure for listening maximum 5 web-clients

# Timer one initialization for on board blinking LED at 200mS interval
timer_one.init(freq=5, mode=Timer.PERIODIC, callback=BlinkLED)

# Timer two initialization for updating the counter variables at 500mS interval
timer_two.init(freq=2, mode=Timer.PERIODIC, callback=UpdateCounter)

while True:
    try:
        if gc.mem_free() < 102000:
          gc.collect()
        conn, addr = s.accept() # Call accpept method for collecting the client address
        conn.settimeout(3.0)
        print('Got a connection from %s' % str(addr)) # Print the client address
        request = conn.recv(1024) # Collect client request
        conn.settimeout(None)
        request = str(request)
        print('Content = %s' % request) # Print the web-client request
        update = request.find('/getVisitorCounters')
        if update == 6:
            a1 = meat
            a2 = egg
            a3 = fruit
            a4 = beverage
            a5 = vegetable
            response = str(a1) + "|"+ str(a2) + "|"+ str(a3) + "|"+ str(a4)+"|"+ str(a5)
        else:
            response = ConstructWebPage() # Construct the web page
  
        conn.send('HTTP/1.1 200 OK\n') # Send the web-server response to web-client
        conn.send('Content-Type: text/html\n')
        conn.send('Connection: close\n\n')
        conn.sendall(response)
        
        # Clsoe connection
        conn.close()
        print("RRRR")
        print(response)
        
    except OSError as e:
        conn.close()
        print('Connection closed')

Step 5: Conclusion

The project is successfully completed with ESP32 development board, Wi-Fi router and Tablet.

The web-server handles HTML-AJAX request / response at regular interval to web-client running on tablet, android mobile and laptop google chrome, and updates the end user GUI.

Step 6: Results and Video Links

Please Visit You Tube for implementation video:,

Visit the "VESZLE - The Art Of Electronics" you tube channel for further information and videos.

https://www.youtube.com/channel/UCY4mekPLfeFinbQH...

ESP32 – Web Server – Google Gauge - Restaurant Bin Monitoring You tube Video