ISLabMaterial

Lab 11: Automatic alert system with GUI


Introduction

In previous labs, we have learned how to monitor the temperature and humidity of the environment and send the data to the cloud. In this lab, we will learn how to retrieve the data from the MongoDB and send an alert to the user when the temperature or humidity is out of the normal range. We will also learn how to use a GUI to display the data and the alert.

Lab Objectives

Learning Outcomes

Revisiting last lab on Rock Pi

Let’s revisit the code we wrote in Lab 10. Please run the code below in you Rock Pi. It will read the temperature and humidity data from the sensor and send it to the MongoDB every 60 seconds.

You will need to replace the MongoDB URI with your own URI.

import datetime
import time
from pymongo import MongoClient
from smbus2 import SMBus

bus = SMBus(7)
client = MongoClient('mongodb+srv://db:<password>@cluster0...:27017/')
db = client.database

import pyttsx3

engine = pyttsx3.init()
engine.say("Program started")
engine.runAndWait()

from periphery import GPIO
import time
red_led = GPIO(146, "out")
red_led.write(True)
time.sleep(3)
red_led.write(False)

# Exercise code: turn on the green LED for 3 seconds
green_led = GPIO(150, "out")
green_led.write(True)
time.sleep(3)
green_led.write(False)


while True:

    bus.write_i2c_block_data(0x38, 0xAC, [0x33, 0x00])
    time.sleep(0.5)
    data = bus.read_i2c_block_data(0x38, 0x00)
    
    temp = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]
    humi = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4
    
    temperature = temp / 1048576 * 200 - 50
    print(u'Temperature: {0:.1f}°C'.format(temperature))
    engine = pyttsx3.init()
    engine.say(u'Temperature is now {0:.1f}°'.format(temperature))
    engine.runAndWait()

    # Exercise code: Read the humidity data from the sensor
    humidity = humi / 1048576 * 100
    print(u'Humidity: {0:.1f}%'.format(humidity))
    # Exercise code: Output humidity data with speech
    engine = pyttsx3.init()
    engine.say(u'Humidity is now {0:.1f}%'.format(humidity))
    engine.runAndWait()
    
    record = {
        "sensor_id": 1,
        "temp": temperature,
        "humi": humidity,
        "date": datetime.datetime.now(),
    }
    
    db.sensors.insert_one(record)
    
    if temperature > 28:
        print("Temperature is too high")
        red_led.write(True)
    else:
        print("Temperature is normal")
        red_led.write(False)
    
    time.sleep(60)

Please run the code above in your Rock Pi. You should see the temperature and humidity data printed on the screen every 60 seconds. You should also see the red LED is on when the temperature is higher than 28°C. If you would like to stop the program, press Ctrl + C on your keyboard.

Display data from the MongoDB on the PC or Rock Pi

In the previous lab, we have learned how to send an alert to the user when the temperature is higher than 28°C. However, the user will not be able to know the temperature and humidity of the environment when the user is not in front of the Rock Pi. In this lab, we will learn how to retrieve the data from the MongoDB and display it on the PC. We will also send an alert to the user when the temperature or humidity is out of the normal range with a GUI.

Please create a new file called lab11.py. We will write our code in this file.

Create a new Python program

Please open the VS Code and create a new file called lab11.py. We will write our code in this file.

Connect to the MongoDB

First, we need to connect to the MongoDB. We will use the MongoClient class to connect to the MongoDB like what we did in previous labs. The MongoClient class takes the path of the MongoDB as the parameter.

from pymongo import MongoClient

# Connect to the MongoDB
mongoPath = [insert your MongoDB path here]
client = MongoClient(mongoPath)
db = client.database
collection = db.sensors

Please replace the [insert your MongoDB path here] with your own MongoDB path, which you could copy from previous labs.

Retrieve data from the MongoDB

To retrieve data from the MongoDB, we need to use the find() method. To get the latest data, we need to sort the data by the date and get the first record.

# Get the last record from the collection (sorted by "date")
lastRecord = collection.find().sort("date", -1).limit(1)[0]

The Temperature has a lot of decimal places. We can limit the number of decimal places to 1 by formatting the string.

Change the code to the following:

print("Temperature: {:.1f} °C".format(lastRecord['temp']))

Exercise 1: Retrieve the humidity data from the MongoDB

Please retrieve the humidity data from the MongoDB and print it on the console.

Building a GUI application with pygame

First, we need to import the libraries we need. We will use the pygame library to create the GUI. We will also keep the libraries we imported before.

Find where you imported the libraries before and add the following line to import the pygame library:

import pygame # add this line to import the pygame library

pygame.init() # Initialize the pygame

Create a window

Next, we need to create a window. We will use the pygame.display.set_mode() method to create a window. The set_mode() method takes a tuple as the parameter. The tuple contains the width and height of the window.

# Create a window
window = pygame.display.set_mode((400, 300))

If you run the code now, you should see a window pop up. But the window will close immediately. We need to tell the window to update every frame, like a video.

First, we need to create a clock. We will use the pygame.time.Clock() method to create a clock.

# Create a window
window = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock() # create a clock

Then we can use a while loop to keep updating the window. We will use the pygame.display.update() method to update the window. We will also use the clock.tick(60) method to limit the frame rate to 60 frames per second.

# Create a window
window = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()

# Update the window every frame
while True:
    
    # Main code here
    
    # update the window 1 time per second
    pygame.display.update()
    clock.tick(1) 

If you run the code now, you should see a window that doesn’t close. But the window is still black. We need to draw something on the window.

You can close the window by pressing pressing Ctrl + C in the terminal.

Draw the background

We just need to fill the window with a color. We will use the window.fill((255, 255, 255)) method to fill the window with a color. The fill() method takes a tuple as the parameter. The tuple contains the RGB value of the color.

Use your favorite color to fill the window. You can find the RGB value of the color on the internet.

# Create a window
window = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()

# Update the window every frame
while True:
    
    # Main code here
    lastRecord = collection.find().sort("date", -1).limit(1)[0] # get the last record from the collection
    window.fill((255, 255, 255)) # fill the window with white
    
    # update the window 1 time per second
    pygame.display.update()
    clock.tick(1)

We also include the code to retrieve the data from the MongoDB in the while loop, making sure the data is updated every frame. If you run the code now, you should see a white window.

Drawing temperature value in the window

To draw text on the window, we need to do the following steps in the Main code section of the while loop.

  1. Create a font.

    To create a font, we need to use the pygame.font.SysFont() method. The SysFont() method takes two parameters, the font name and the font size. The font name is a string. The font size is an integer.

     # create a font using system font style "Arial" with size 30
     font = pygame.font.SysFont("Arial", 30)
    
  2. Create a text surface for showing temperature

    A text surface is a surface that contains the text we want to draw. There two text surfaces in this case, one for the temperature and one for the humidity.

     # Create a text surfaces
     tempText = font.render("Temperature: {:.1f} °C".format(lastRecord['temp']), True, (0, 0, 0))
    
  3. Draw the text surface on the window.

    We will use the window.blit() method to draw the text surface on the window. The blit() method takes two parameters, the text surface and the position of the text surface. The position is a tuple that contains the x and y position of the text surface.

     # Draw the text surfaces on the window
     window.blit(tempText, (10, 10))
    

    The code above will draw the text surfaces on the window at the position (10, 10) and (10, 50).

The full code for this section is as follows:

# Create a window
window = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()

# Create a font
font = pygame.font.SysFont("Arial", 30) # create a font called "Arial" with size 30

# Update the window every frame
while True:
    
    # Main code here
    lastRecord = collection.find().sort("date", -1).limit(1)[0] # get the last record from the collection
    window.fill((255, 255, 255)) # fill the window with white
    # Create a text surfaces
    tempText = font.render("Temperature: {:.1f} °C".format(lastRecord['temp']), True, (0, 0, 0))
    # Draw the text surfaces on the window
    window.blit(tempText, (10, 10)) 

    # update the window 1 time per second
    pygame.display.update()
    clock.tick(1) 

If you run the code now, you should see the temperature data on the window.

Exercise 2: Drawing humidity value in the window

Now you know how to draw text on the window. Try to draw the humidity data on the window. Make it under the temperature data.

Alerting the user with background color

Now we have the temperature and humidity data. We can check if the temperature and humidity are out of the normal range. If the temperature is higher than 28°C, or the humidity is higher than 80%, we will alert the user by changing the background color of the window.

Please replace window.fill((255, 255, 255)) with the following code:

if lastRecord['temp'] > 28:
    window.fill((255, 0, 0))
else:
    window.fill((255, 255, 255))

The above code will change the background color to red if the temperature is higher than 28°C. Otherwise, it will change the background color to white.