Created:
Published:
18 Nov 2020 by Vassilis Serasidis
7 January 2022


Inroduction

The usual way to control a sensor from a web page is to refresh the web page every let's say 1 sec. The main disadvantages are:

  • There is no Real Time monitor of the sensor
  • The traffic is usually high, because you have to reload the web page to see the new values

All these obstacles can be circumvented by using the
Socket.IO which is a protocol that allows two-way communication of real-time events between Clients (Web Browsers) and Server (Raspberry Pi). On the Raspberry Pi side I choose Flask which is a micro web framework written in Python and used to visualize the sensor data on the web page.
The example circuit consists of:

  • an LED
  • a button
  • a humidity and temperature sensor (AM2302 / DHT22)


Schematic diagram


Figure 1: The schematic diagram of the circuit.



How it works


Figure 2: The web interface in a desktop browser

Figure 3: The web interface in a mobile browser


By pressing the button on the board, the "Button" indicator on the web page will light up. When the button is released, the "Button" indicator will turn off. Subsequently, when the "LED" button on the web page is pressed, the led on the board will light up. When the "LED" is pressed again, the led on the board will turn off. The "Refresh" button on the webpage reads the data from the AM2302 (DHT22) sensor and refreshes the "Temperature" and "humidity" values of the analog gauge.



The Source Code

The source code is divided into two parts. The first part is the python code (
app.py) and the second part (index.html) is the html code.The app.py is for creating the web server and is used to communicate the Raspberry Pi with the sensors. The index.htm is the user interface that visualizes the incoming data on the web page and sends the user's commands to the Raspberry Pi.
The
RaphaelJS v2.1.4 JavaScript Library is used to create the analog gauges used to display temperature and humidity.


app.py
'''
  Humidity and temperature monitor

  In addition, an LED and a button have been added for testing purposes
  Tested on Raspberry Pi Zero W
  
  Connections:
  +-------------------------------+ 
  |      PCB           PRi Zero W |
  +-------------------------------+
  |   - Button --------> GPIO 27  |
  |   - LED <----------- GPIO 17  |
  |   - DHT-22 (Data) -> GPIO  4  |
  |   - Vcc (3.3V) <---- Pin   1  |
  |   - GND <----------- Pin   9  |
  +-------------------------------+
  
   Author: Vassilis Serasidis
  Created: 18 Nov 2020 

'''

import RPi.GPIO as GPIO
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
import Adafruit_DHT
import datetime

app = Flask(__name__)
socketio = SocketIO(app)

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

#Button setup
button_pin   = 27
button_state = 'off'
GPIO.setup(button_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP )   

#LED setup
led_pin   = 17
led_state = 'off'
GPIO.setup(led_pin, GPIO.OUT)
GPIO.output(led_pin, GPIO.LOW)

#DHT-22 setup
sensor = Adafruit_DHT.AM2302
pin         = 4
humidity    = -1
temperature = -1
counter = 0

  
def my_callback(channel):  
  #print("-> ", channel) #Print the pin number
  if GPIO.input(channel): 
    socketio.emit('server_message', {'message':'btn_off'})
    button_state = "off"
    print ("OFF")  
  else: 
    socketio.emit('server_message', {'message':'btn_on'})
    button_state = "on"
    print ("ON")

GPIO.add_event_detect(button_pin, GPIO.BOTH, callback=my_callback)  

@app.route("/", methods=['GET', 'POST'])
def main():
  global humidity, temperature
  global button_state, led_state
  
  if humidity == -1: #Initialize humidity and temperature content
    humidity, temperature = read_dht()

  print("\n")
  if request.method=='GET':
    print("-= GET =-")
    
    req = request.args.get("led")
    print("LED-REQ: ", req)
    
    if req == 'on':
      led_state = 'off'
      GPIO.output(led_pin, GPIO.LOW)
      socketio.emit('server_message', {'message':'led_off'})
    else:
      led_state = 'on'
      GPIO.output(led_pin, GPIO.HIGH)
      socketio.emit('server_message', {'message':'led_on'})
    
  
    req = request.args.get("btn")
    print("BUT-REQ: ", req)
    if req == 'on':
      button_state = 'off'
      socketio.emit('server_message', {'message':'btn_off'})
    else:
      button_state_state = 'on'
      socketio.emit('server_message', {'message':'btn_on'})
  
    req = request.args.get("dht")
    print("DHT-REQ: ", req)
    if req == "ref":
      humidity, temperature = read_dht() 
      
      
  templateData = {
    'temperature' : temperature,
    'humidity': humidity,
    'led' : led_state,
    'btn' : button_state
  }
  
  return render_template('index.html', **templateData)
  
@app.route('/favicon.ico')
def favicon():
    return app.send_static_file('pic/favicon.ico')

@socketio.on('client_message')
def receive_message (client_msg):
    emit('server_message', client_msg)

def scheduleTask():
    global humidity, temperature
    dt_now = datetime.datetime.now().strftime("%d/%m/%Y, %H:%M:%S")
    humidity, temperature = read_dht()
    socketio.emit('server_message',
    {'message':'sensor',
     't_val':temperature,
     'h_val':humidity,
     'dt_val':dt_now
    })
    print(dt_now)
 
def read_dht():
#read_retry(sensor, pin, retries=15, delay_seconds=0.5, platform=None)
    t=t2=h=h2= 0
    for s in range (3): #Read 3 times the DHT2302 (DHT-22)
      h, t = Adafruit_DHT.read_retry(sensor, pin, 15, 0.5)
      t2 += t
      h2 += h
      
    t2 /= 3
    h2 /= 4
    print('Temp={0:0.1f}*  Humidity={1:0.1f}%'.format(t2, h2))
    return h2, t2
    
    
if __name__ == "__main__":
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)


index.html
<!DOCTYPE html>
<head>
  <meta name="viewport" content="width=device-width">
  <title>RPi Web Server</title>
  <link rel="stylesheet" href="static/css/style.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
  <script src="static/js/socket.io.min.js"></script>

	<script>
		const socket = io();
		socket.on('server_message', function (data) {
      if (data.message == 'btn_on') {
				document.getElementById('btn').src = "static/pic/led_on.png";
			}
      if (data.message == 'btn_off') {
				document.getElementById('btn').src = "static/pic/led_off.png";
			}
      if (data.message == 'sensor') {
        g1.refresh(data.t_val);
        g2.refresh(data.h_val);
        document.getElementById('dat1').innerHTML  = data.dt_val;
			}
		});
    
    function loading() {
      document.getElementById("spin2").className = "fa fa-spinner fa-spin";
    }
	</script>
  
</head>
   <body><br> 
    <table class="main_box">
      <tr>
        <td colspan="2">
          <h1>RPi Weather Station</h1><hr>
        </td>
      </tr>
      <tr>
        <td><div id="g1"></div></td>
        <td><div id="g2"></div></td>
      </tr>
      <tr>
        <td colspan="2">
          <table style="vertical-align: middle; margin-left:auto; margin-right:auto;">
            <tr>
              <td colspan="2" style="font-size: 18px; background-color: rgb(58, 164, 226)";>STATUS</td><br>
            </tr>
            <tr>
              <td>Button</td>
              <td>
                <img id="btn" src="static/pic/led_off.png">
              </td>
            </tr>
            <tr>
              <td>LED</td>
              <td>
                <form><button class="button" name="led" type="submit" value="{{ led }}">{{ led }}</button></form>
              </td>
            </tr>
            <tr>
              <td>DHT-22 values</td>
              <td><form><button class="button" name="dht" type="submit" value="ref" onclick="loading();"><span id="spin2" class=""></span>Refresh</button></form></td>
            </tr>  
          </table>
        </td>
      </tr>
      <tr>
        <br><td colspan="2"><br><hr><br>(c) 18 Nov 2020 by <strong>Vassilis Serasidis</strong><br><a href="https://www.serasidis.gr">https://www.serasidis.gr<br><br><br</a></td>
      </tr>
    </table>
    
    <script src="static/js/raphael-2.1.4.min.js"></script>
    <script src="static/js/justgage.js"></script>  
    <script>
      var g1, g2;
      document.addEventListener("DOMContentLoaded", function(event) {
          g1 = new JustGage({
              id: 'g1',
              value: {{temperature}},
              min: 0,
              max: 100,
              symbol: ' °C',
              title: "Temperature",
              pointer: true,
              gaugeWidthScale: 0.6,
              customSectors: [{
                color: '#ff0000',
                lo: 25,
                hi: 100
              }, {
                color: '#00ff00',
                lo: 18,
                hi: 25
              },{
                color: '#0000ff',
                lo: 0,
                hi: 25
              }],
              counter: true
            });
          
          g2 = new JustGage({
            id: 'g2',
            value: {{humidity}},
            min: 0,
            max: 100,
            symbol: ' %',
            title: "humidity",
            pointer: true,
            gaugeWidthScale: 0.6,
            customSectors: [{
              color: '#ff0000',
              lo: 50,
              hi: 100
            }, {
              color: '#00ff00',
              lo: 0,
              hi: 50
            }],
            counter: true
          });     
      });
    </script> 
  </body>
</html>


Download the full project




Installation

Assuming the Raspberry Pi username is <
pi>, extract the rpi_dht22_flask.zip file to the desktop or use the following command in the terminal.

unzip /home/pi/Desktop/rpi_dht22_flask.zip


Download the package list from the repositories, install Python 3 and the Python-3 requirements

sudo apt-get update
sudo apt-get install python3-pip
sudo apt-get install python3-dev python3-rpi.gpio
sudo python3 -m pip install --upgrade pip setuptools wheel
pip3 install -r /home/pi/Desktop/rpi_dht22_flask/requirements.txt


Run the
app.py file

python3 /home/pi/Desktop/flask/app.py


If all goes well, you will see the following message, that the web server is running on port
5000. For example, if your Raspberry Pi has a static IP of 192.168.1.50, then you should type in the browser: http://192.168. 1.50:5000 to see the web interface.

pi@raspberry:~/flask $ python3 app.py
WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: on
*
Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.
* Debugger is active!
* Debugger PIN: 174-435-583





The circuit in action


(c) 2022 by Vassilis Serasidis