Most people have day jobs, they have to leave their homes unguarded, not only prone to intrusion or theft but also cases of fire, or simply things like water and electricity wastage. The rest who stay at home might just choose to have an application that helps control their home appliances by not moving at all. This project uses a raspberry pi and an application to control the lights and fans of your home. It can also detect the temperature of your home, detect smoke levels in your home, and access its security cameras to show live feed.
A home monitoring system is a pure IoT project that lets a user monitor their homes anytime, anyplace. It uses core python libraries to work. Tkinter for the application, socket programming, and TCP/IP protocol for communication between the home and the user, also for transferring data, cv2 for the live feed, pickle for decoding and encoding data, RPI.GPIO for accessing the general-purpose input/output pins of the raspberry pi connected to our home and some other sensor-specific libraries for accessing them and retrieving data.
Here are the schematic representation and workflow of the project. In this blog, we will go through each component of the project and code it.
Contents
Tkinter Application
What is Tkinter? It is python’s de-facto GUI library, with which one can easily make simple and useful applications. Also, all of its functions are easily readable and mostly self-explanatory. The link to the documentation is provided below for any references.
To start using Tkinter in python create a new python file and import it.
from tkinter import *
1. Background
We will start our coding of the application by giving a suitable background to it.
screen=Tk()
# only .png format can be read using tkinter.PhotoImage
back = PhotoImage(file="background1.png")
# assigning dimensions to our application
screen.geometry("300x500")
# gb stands for background color
screen.configure(bg='#525252')
panel1 = Label(screen, image=back)
while True:
panel1.pack(side='top', fill='both', expand='yes')
panel1.image = back
#the next 2 functions are used to display the label "back" continuously
screen.update_idletasks()
screen.update()
2. Welcome Message
After the background, we add a welcome message to personalize the app. Create anew function welcome to define and show the welcome message for each time a user opens the app.
import time
def welcome(wc_msg,start):
screen=Tk()
# only .png format can be read using tkinter.PhotoImage
back = PhotoImage(file="background1.png")
# assigning dimensions to our application
screen.geometry("300x500")
# gb stands for background color
screen.configure(bg='#525252')
panel1 = Label(screen, image=back)
welcome = Message(screen, text=wc_msg,bg='#353533', font =('Chilanka', 12, 'bold'), padx=70, pady=70 )
welcome.place(relx=0.5, rely=0.15, anchor=CENTER)
while True:
panel1.pack(side='top', fill='both', expand='yes')
welcome.pack_forget()
#time.sleep(5)
if(int(time.time()-start)>7):
break
panel1.image = back
screen.update_idletasks()
screen.update()
# Since the loop is exited, the present sscreen is to be destroyed
screen.destroy()
- Line 1 imports the time module in python, it calculates the time elapsed, through which we can move on from the welcome message after some seconds.
- Line 18 of the code, checks the same, start is the starting time, and time.time() gives the present time.
3. Buttons
Buttons form the core of Tkinter’s functions. We can easily customize and position them. They can also be easily styled using a package of Tkinter, ttk.
We start by importing it in our code
import tkinter.ttk as ttk
Now we will code the main function that deals with the application as a whole and make modifications to it as we go on.
def app(wc_msg): # takes the welcome message as input
start=time.time()
welcome(wc_msg,start)
# create a new screen to add the buttons
screen=Tk()
back = PhotoImage(file="background3.png")
screen.geometry("300x500")
panel1 = Label(screen, image=back)
panel1.pack(side='top', fill='both', expand='yes')
screen.configure(bg='#525252')
# we use ttk to add themed widegts
style = ttk.Style()
# some styles that can be added
style.configure('TButton', font =('Chilanka', 12, 'bold'), foreground = 'black', background="#383838", relief=FLAT )
cam_im=PhotoImage(file="camera.png")
camera=ttk.Button(screen, text="camera",image= cam_im,style = 'TButton',)
camera.place(relx=0.5, rely=0.165, anchor=CENTER)
temp_im=PhotoImage(file="temperature.png")
temperature=ttk.Button(screen, text="temperature",image =temp_im, style = 'TButton')
temperature.place(relx=0.16, rely=0.30, anchor=CENTER)
light_im=PhotoImage(file="lights.png")
lights=ttk.Button(screen, text="lights",image=light_im, style = 'TButton')
lights.place(relx=0.13, rely=0.505, anchor=CENTER)
fan_im=PhotoImage(file="fan.png")
fans=ttk.Button(screen, text="fans",image=fan_im ,style = 'TButton')
fans.place(relx=0.86, rely=0.51, anchor=CENTER)
smoke_im=PhotoImage(file="smoke.png")
smoke=ttk.Button(screen, text="smoke",image=smoke_im, style = 'TButton')
smoke.place(relx=0.86, rely=0.31, anchor=CENTER)
panel1.image = back
screen.mainloop()
- Line 3 calls the function to display a welcome message. Then the Tkinter screen is created.
- Line 13 uses the ttk to design themed widgets. Parameters like font color type of button can be defined.
- Line 17-20 initializes a single button “camera” which is used to define the security camera function. Similarly, each button describes a different function and is self-explanatory.
- A button can use either a text or an image as a medium, but images are preferred for the design.
- Adding Button.place() or Button.pack() or Button.grid() after button initialization is a must. These are used to position the images in the application.
- The main loop runs the above widgets of the app in a loop and is the key for coding in Tkinter.
4. Functions
To get results in our application, bind each button to a function. the bind function of Tkinter helps in doing so.
#Button-1 means left click of the mouse, 2 is a roll and 3 means right click
Button.bind("<Button-1>", function_name)
Socket Programming
Sockets are remote connections. They are used to communicate between various processes usually running on different systems, also to create two-way client-server environments. Communication takes place with the help of the IP Adress of the server and a specified port number. Together they form a socket, able to transfer data between its nodes. Sockets use TCP(Transmission Control Protocol)/IP(Internet Protocol) to transfer data. TCP/IP specifies how data is exchanged over the internet by providing end-to-end communications that identify how it should be broken into packets, addressed, transmitted, routed, and received at the destination.
1. Sockets in Python
Python makes socket programming simple for us. We use the “socket” library in python to establish connections and send data through sockets. To establish a server-client environment, the home must act as the server and user as the client.
server.py
import socket
def create_socket():
try:
global host
global s
global port
host=""
port=9999
s=socket.socket()
except socket.error as mag:
print("socket creation error",str(mag))
def bind_socket():
try:
global host
global s
global port
print("binding the socket")
s.bind((host,port))
s.listen(5)
except socket.error as mag:
print("socket binding error ",str(mag)," retrying")
bind_socket()
def accept():
conn,adress=s.accept()
print("ip : ",adress[0]," port : ",adress[1])
send_command(conn)
conn.close()
def send_command(conn):
None
def main():
create_socket()
bind_socket()
accept()
main()
- create_socket – creates a socket with a specified port number and IP address.
- bind_socket – binds the IP address and port to form a socket.
- accept – accepts the client’s request to form a connection
- send_command – function to decide the workflow.
client.py
The client code is pretty simple, the IP address must be defind and a common port established.
import socket
s=socket.socket()
host="IPV4 address"
port=9999
s.connect((host,port))
while(True):
None
2. pickle
Pickle is a python module which helps in serializing and de-serializing a Python object structure. It helps in data storage and data encoding. We must encode the data to be able to send it through a socket. Pickle object, NumPy array, strings are some formats of such data. To get started Import dumps and loads from Tkinter library. Encoding and decoding can be done by them respectively.
from pickle import loads, dumps
Code to send data from server to client-
welcome_msg = "WELCOME TO THE GABLINGS"
# to send a string, without pickle
conn.send(str.encode(elcome_msg))
# to send an integer or array or image
conn.send(dumps([1,2,3,4,5])
Code to recieve data at the client’s end-
# to recieve a string
message = (s.recv(1024)).decode("utf-8")
# to recieve an integer or array or image
array = loads(s.recv(40960000))
The number after recieve function determines the maximum limit in bytes of data that can be recieved.
Computer Vision
Computer Vision deals with the understanding, and comprehension of digital images. It is a field of science that deals with replicating the complexity of human vision with various algorithms and sensors. Python being an active community has a very commonly used computer vision library called OpenCV. It contains built-in functions for almost any processing that needs to be done on images or videos. We will be using this library to capture a video and show it on a screen.
1. Installation
Open terminal and type:
2. Video
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
# Display the frame
cv2.imshow('frame',frame)
# if q is pressed , the video is released
if cv2.waitKey(1) &amp;amp;amp;amp; 0xFF == ord('q'):
break
#When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
The above code seems simple enough. To implement it in our project, we must split the section which captures a frame from the camera to the one which shows it. Since our home captures the video from the security camera, it becomes the server while the app which shows the live feed becomes the client.
#SERVER SIDE
cap = cv2.VideoCapture(0)
#JPEG compression
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90]
while True:
ret,frame = cap.read()
res,frame = cv2.imencode('.jpg',frame,encode_param)
data = dumps(frame,0)
size = len(data)
conn.send(struct.pack("&amp;gt;L",size)+data)
op=(conn.recv(1024)).decode("utf-8")
if(op=="quit"):
break
cap.release()
- Line 4,8 use JPEG compression on the image to reduce its size. We do this to minimize the lag between the source and the destination.
- Line 11 packs the image and its size, then sends it to the client.
#CLIENT SIDE
def cam(event):
s.send(str.encode("camera"))
rec_size = struct.calcsize("&amp;gt;L")
while True:
data = b""
rec_size = struct.calcsize("&amp;gt;L")
while(len(data)&amp;lt;rec_size):
data+=s.recv(4096)
print("Started receiving data")
inp_msg_size = data[:rec_size]
data = data[rec_size:]
msg_size = struct.unpack("&amp;gt;L",inp_msg_size)[0]
while(len(data)&amp;lt;msg_size):
data+= s.recv(4096)
fr_data = data[:msg_size]
data = data[msg_size:]
frame = loads(fr_data,fix_imports = True,encoding = "bytes")
frame = cv2.imdecode(frame,cv2.IMREAD_COLOR)
cv2.imshow('frame',frame)
if cv2.waitKey(1) &amp;amp; 0xFF == ord('q'):
s.send(str.encode('quit'))
break
s.send(str.encode("0"))
cv2.destroyAllWindows()
- Create a new function in the client file to display camera output.
- Line 4 sends our home a command to give access to the security camera.
- In-Line 8, the size of data to be received is calculated.
- Variable data in Line 10 stores the image as well as its size.
- Data is Line 18 stores the image to be displayed.
- Line 20 decodes the from the JPEG compression.
- Line 25 is used to send pseudo-variable to transfer control to the server, to exit the loop when the if condition in Line 22 is fulfilled.
3. Output
Raspberry pi
Raspberry pi is a small computer. It has its own RAM, processor, and other components. It mostly uses Rasbian as its operating system but, as of today, it can support almost anything. This device has brought about a huge change in the field of IoT. From home automation to self-driving cars, it is used in almost every hardware project. We will use it as the central point of communication between our home and us.
Hardware
For our project we need some basic hardware components to work with:
- jumper wires
- LED
- Raspberry pi
- Raspberry pi camera v2
- Smoke sensor(MQ-135)
- Temperature sensor(DS18B20)
- breadboard
- Resistors
- ADC(Analog to Digital convertor)
Circuit
The basic server-client code is ready. Now we will work on the circuit diagram of our project. If you are missing one or two of these sensors it is all right. Comment out the respective function in the client file to avoid any errors. You can see a demo of the diagram below, with sensors connected to specific general-purpose input/output pins of the RPI. The code must be changed according to the connections made. The pi camera need not be a part of the circuit since it can easily be added to the camera port.
- Resistors are connected to avoid any overload.
- An external ADC is required for the smoke sensor
- Connections should be made wither using the RPI board numbering system or BCM numbering system.
Coding
Now that we are ready with the hardware, we can code all the sensors accordingly. Create a new python file to add functions for each sensor. We will code each sensor one by one. Since there is no need for external libraries for the pi camera, extra coding is not required.
Temperature
Importing python libraries
import time
import os
import glob # glob helps in accesing a particular file in our code
Creating function temperature(). The python code is given below, but before running it, some changes need to be made in the raspberry pi. The changes can be seen in the link below: https://pimylifeup.com/raspberry-pi-temperature-sensor/
def check_temperature():
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')
base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '28*')[0]
device_file = device_folder + '/w1_slave'
f = open(device_file, 'r')
lines = f.readlines()
f.close()
return lines
Smoke
There are a variety of os smoke sensors, each able to detect particular gases and their percentage. These are the MQ series sensors. Although thee detection capacity and functionality may differ, their connections and codes are almost the same.
Before we code, we must clone mq.py from github. Then we need to import it in our python file.
from mq import *
Now the code:
def smoke_level():
mq = MQ();
perc = mq.MQPercentage()
smk=[perc["GAS_LPG"], perc["CO"], perc["SMOKE"]]
return smk
LED
Coding the LED is the easiest. We only need the built-in GPIO functions of RPI. We need to create 2 functions for LED, one that checks if the LED is ON and the other which turns it ON/OFF.
#FUNCTION 1
def check_light():
Check if light is on
if(GPIO.digitalRead(3)==GPIO.HIGH and GPIO.digitalRead(2)==GPIO.LOW):
return "ON"
else:
return "OFF"
#FUNCTION 2
def lights(action):
if(action=="on"):
GPIO.output(3, GPIO.HIGH)
GPIO.output(2, GPIO.LOW)
elif(action=="off"):
GPIO.output(2, GPIO.HIGH)
GPIO.output(3, GPIO.LOW)
Compilation
We have covered the basic topics required to complete this whole project. Now we will work on compiling our work. We will start by completing the codes, followed by deploying the code on RPI and finally running it. Since we have already coded the camera, it will not be repeated in the next section.
Home
This is the server.py code that runs in the raspberry pi connected to our home. It must be running at all times with preferably a static IP address. We have worked on this when we coded the server file, now we need to give it a body by completing the function send_command. We must import our sensors code in this file to get started.
from home_system import *
Now, to the main function:
def send_command(conn):
conn.send(str.encode(wc_msg))
while True:
command=(conn.recv(1024)).decode("utf-8")
if(command=='temperature'):
temperature=check_temperature()
conn.send(str.encode(temperature))
if(command=='camera'):
# code for video capture in server side
if(command=="smoke"):
# smoke_level() returns value in percentage of threee gases
smokePercent=smoke_level()
conn.send(str.encode(smokePercent))
if(command=="lights"):
chk=check_light():
if(chk =="ON"):
conn.send(str.encode("Lights are on,should I turn them off"))
ans=(conn.recv(1024)).decode("utf-8")
if(ans=="yes"):
lights("off")
else:
conn.send(str.encode("Lights are off,should I turn them on"))
ans=(conn.recv(1024)).decode("utf-8")
if(ans=="yes"):
lights("on")
if(command=="fan"):
# None
- When you click one of the buttons in the application, it sends a command to the raspberry pi which in return performs the fuction. The if condition checks for the command, access the sensors respectively, and send the output to our user.
- The method for accessing the fan is left empty since it can be easily coded. The fan can be replaced by a simple motor. Also, the connections and coding are very similar to those of LEDs. Try it Yourself.
User
This is the client.py code that runs on the user’s side and acts as the GUI for controlling and monitoring our home. It needs your home’s IP address to connect. We will be completing the control While loop of this code and adding the receiving functions that are bound to the buttons and show data.
def cam(event):
# code for video in the client side
# function for lights
def light(event):
s.send(str.encode("light"))
msg=s.recv(1024)
print(msg.decode("utf-8"))
ans=input()
s.send(str.encode(ans))
#function for temperature
def temp(event):
s.send(str.encode("temperature"))
temperature=s.recv(1024)
print(temperature.decode("utf-8"))
# function for smoke level
def smokeLevel(event):
s.send(str.encode("smoke"))
smoke=s.recv(1024)
print(smoke.decode("utf-8"))
def welcome():
# coded in the previous section
def app():
#coded in the previous section
# main control loop
while(True):
wc_msg=s.recv(1024)
wc_msg=wc_msg.decode("utf-8")
print(wc_msg)
app(wc_msg)
- Each function called by a button takes a particular input: “event”. We must add it to the function parameter but it need not be used.
- The RPI has the name of the house coded. The While loop starts only after it receives the welcome message. It then calls the app which runs as long as the user wants.
Hardware
Our code is completely ready now. All we need to do is deploy the code in our raspberry pi. Make the appropriate circuit connections, and connect your raspberry pi to the internet. Clone the Server code to the RPI and install all the necessary libraries. To provide the RPI a static IP address, we can host it on cloud computing platforms like Google Cloud or Microsoft Azure. Run the code locally and cross-check the connections, before hosting. The server file must always be run first, then the client.
Output
For testing the hardware and checking for minute errors, both the codes can also be run on the same system. Run the server file first, then the client file.
Each button represents a single function. Once pressed, it shows the output in the terminal, which can also be moved to the app. This application currently supports only one person but can be made for multiple also with the help of multithreading.
With this, we come to the end of our tutorial for a home monitoring system using a Tkinter application and other core python libraries. If any doubts remain or errors pop up, try reading the respective documentation. The entire working code and original directory of this project can be found in this GitHub repository:https://github.com/Shaashwat05/home_controlling
We hope you enjoyed this tutorial , and have learnt something useful from it. Thank you.