Multitasking With Raspberry Pi by Dogan Ibrahim
Multitasking With Raspberry Pi by Dogan Ibrahim
Dogan Ibrahim
Multitasking with Raspberry Pi
Multitasking with Raspberry Pi
Dogan Ibrahim
● All rights reserved. No part of this book may be reproduced in any material form, including
photocopying, or storing in any medium by electronic means and whether or not transiently or incidentally
to some other sue of this publication, without the written permission of the copyright holder except in
accordance with the provisions of the Copyright Designs and Patents Act 1988 or under the terms of a
licence issued by the Copyright Licencing Agency Ltd., 90 Tottenham Court Road, London, England W1P
9HE. Applications for the copyright holder's permission to reproduce any part of the publication should be
addressed to the publishers.
● Declaration
The author and publisher have used their best efforts in ensuring the correctness of the information
contained in this book. They do not assume, or hereby disclaim, any liability to any party for any loss or
damage caused by errors or omissions in this book, whether such errors or omissions result from negligence,
accident or any other cause..
● ISBN 978-1-907920-96-7
Elektor is part of EIM, the world's leading source of essential technical information and electronics products for pro
engineers, electronics designers, and the companies seeking to engage them. Each day, our international team develops
and delivers high-quality content - via a variety of media channels (including magazines, video, digital media, and social
media) in several languages - relating to electronics design and DIY electronics. www.elektormagazine.com
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.5 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.5.2 Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
●5
3.6 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.6 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
5.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.3.3 Project 3 – Setting the LED flashing rate from the keyboard . . . . . . . . . . . . 72
5.3.6 Project 6 – S
ynchronizing the parent and child processes -
multitasking event counter . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
●6
5.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6.2 Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6.4.3 Project 3 – Setting the LED flashing rate from the keyboard . . . . . . . . . . . . 97
6.4.7 Project 7 – S
quare waveform generator with 7-segment
LED display and keyboard . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
6.4.8 Project 8 – S
quare waveform generator with 7-segment
LED display and buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
6.4.10 Project 10 – Four-digit 7-segment display conveyor belt object counter . . . 131
●7
9.12.2 Project 2 – Setting the LED flashing rate from the keyboard . . . . . . . . . . . 166
9.12.9 Project 9 – Setting the flashing rate of an LED with keypad . . . . . . . . . . . 202
●8
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
●9
Preface
Microcontrollers have traditionally been programmed using the assembly language of the
target processor. Although assembly language is fast, it has a major disadvantage in that
it is difficult to develop and maintain large projects. Additionally, microcontrollers from
different manufacturers use different assembly language instruction sets making it very
time consuming for the programmers to learn new languages every time a different micro-
controller is to be used. Assembly code developed for one type of microcontroller cannot
be ported to another type of microcontroller. Nowadays microcontrollers are programmed
using high-level languages, such as C, C++, Pascal, Python, or Basic. Perhaps the biggest
advantage of using a high-level language is that developed code can easily be ported to
other types of microcontrollers. Additionally, it is easier to maintain a program developed
using a high-level programming language.
The topic of this book is multitasking. Multitasking has become one of the most impor-
tant topics in microcontroller-based systems, namely in automation applications. As the
complexity of the projects grows, more functionality is demanded from the projects and
such projects require the use of several inter-related tasks running on the same system
and sharing the CPU (or multiple CPUs) to implement the required operations. As a result
of this, the importance of multitasking operation in microcontroller-based applications has
been steadily growing over the last few years. Many complex automation projects nowa-
days make use of some form of a multitasking kernel. In this book, the Python 3 program-
ming language is used with the Raspberry Pi 4. Other models of Raspberry Pi can also be
used without any change to the code.
This book is project-based. Its main aim is to teach the basic features of multitasking using
Python on Raspberry Pi. Many fully tested projects are given in the book using the multi-
tasking modules of Python. Each project is fully described and in detail. Complete program
listings are provided for each project. Readers should be able to use the projects as they
are, or modify them to suit their own needs.
● 10
The following Python multitasking modules have been described and used in the projects:
• Fork
• Thread
• Threading
• Subprocess
• Multiprocessing
Knowledge of the Python programming language will be useful to the readers. Also, fa-
miliarity with at least one model of the Raspberry Pi computer will be an advantage. The
knowledge of assembly language programming is not required because all the projects in
the book are based on using the Python language.
This book is written for students, for practising engineers, and for hobbyists interested in
developing multitasking projects using the Python 3 programming language on the Rasp-
berry Pi computer.
I hope you like reading the book and base your next Raspberry Pi project on multitasking.
Dogan Ibrahim
London, 2020
● 11
1.1 Overview
In this chapter, we will learn how to install the latest Raspberry Pi operating system, Rasp-
bian Buster, on an SD card for the Raspberry Pi 4. Advanced users can skip this chapter if
they already have installed the latest operating system.
• Download the Buster image to a folder on your PC (e.g. C:\RPIBuster) from the
following link by clicking the Download ZIP under section Raspbian Buster with
desktop and recommended software (see Figure 1.1). At the time of writ-
ing this book, the file was called: 2019-07-10-raspbian-buster-full.img. You
may have to use 7Zip to unzip the download because some of the features are
not supported by older unzip software.
https://www.raspberrypi.org/downloads/raspbian/
● 12
• Put your blank micro SD card into the card slot on your computer. You may need
to use an adapter.
• Download the Etcher program on your PC to flash the disk image. The link is (see
Figure 1.2):
https://www.balena.io/etcher/
• Double click to open Etcher, and click Select image. Select the Raspbian Buster
file you just downloaded.
• Click Flash (see Figure 1.3). This may take several minutes. Wait until it is fin-
ished. The program will then validate and unmount the micro SD card. You can
remove your micro SD card after it is unmounted.
● 13
• You are now ready to use your micro SD card on your Raspberry Pi 4.
• Connect your Raspberry Pi 4 to a HDMI monitor (you may need to use an adapter
cable for mini HDMI to standard HDMI conversion), connect a USB keyboard, and
power up the Raspberry Pi.
• You will see the startup menu displayed on the monitor. Click Next to get started.
• Select the Wi-Fi network and enter the password of your Wi-Fi router
• Click on the Wi-Fi icon on the top right-hand side of the screen and note the
Wireless IP address of your Raspberry Pi (notice the IP address is not static and
it can change next time you power-up your Raspberry Pi).
• You should now be ready to use your Raspberry Pi 4 (see Desktop in Figure 1.4)
● 14
Notice that the IP address of your Raspberry Pi can also be seen in your router. You can also
get the IP address of your Raspberry Pi using your mobile phone. Several programs can be
installed on your mobile phone to show you the IP addresses of all the devices connected to
your router. In this section, the use of the Android app called Who's On My Wi-Fi – Net-
work Scanner by Magdalm is used to show how the IP address of your Raspberry Pi can
be displayed. Running this program will display the Raspberry Pi Wireless IP address under
the heading Raspberry Pi Trading Ltd. In addition to the IP address, other parameters
such as the MAC address, gateway address, IP mask, etc are all displayed by this program.
Go to the configuration menu and select Interface Options. Go down to P2 SSH (see
Figure 1.5) and enable SSH. Click <Finish> to exit the menu.
● 15
You should also enable VNC so that the Raspberry Pi can be accessed graphically over the
internet. This can be done by entering the following command on a terminal session:
Go to the configuration menu and select Interface Options. Go down to P3 VNC and en-
able VNC. Click <Finish> to exit the menu. At this stage, you may want to shut down your
Raspberry Pi by clicking the Applications Menu on the desktop and selecting Shutdown
https://www.putty.org/
Simply double click to run it and the Putty startup screen will be displayed. Click SSH and
enter the Raspberry Pi IP address, then click Open (see Figure 1.6). The message shown in
Figure 1.7 will be displayed the first time you access the Raspberry Pi. Click Yes to accept
this security alert.
● 16
You will be prompted to enter the username and password. Notice that the default user-
name and password are:
username: pi
password: raspberry
● 17
You now have a terminal connection with the Raspberry Pi and you can type in commands,
including super user sudo commands. You can use the cursor keys to scroll up and down
through the commands you've previously entered in the same session. You can also run
programs (not graphical).
• Restart Putty
• Set the Default Foreground and Default Bold Foreground colours to black
(Red:0, Green:0, Blue:0)
• Set the Default Background and Default Bold Background to white (Red:255,
Green:255, Blue:255)
• Set the Cursor Text and Cursor Colour to black (Red:0, Green:0, Blue:0)
• Select Appearance under Window and click Change in Font settings. Set the
font to Bold 12.
• Select Session and give a name to the session (e.g. RPI4) and click Save.
• Next time you restart Putty, select the saved session and click Load followed by
Open to start a session with the saved configuration
● 18
pi$raspberrypi:~ $ vncserver :1
The steps to install and use VNC Viewer on your PC are provided below:
• There are many VNC Viewers, but the one I recommend is TightVNC which can
be downloaded from the following web site:
https://www.tightvnc.com/download.php
• Download and install TightVNC for your PC. You will have to choose a password
during the installation.
• Start the TightVNC Viewer on your PC and enter the Raspberry Pi IP address
(see Figure 1.8) followed by :1. Click Connect to connect to your Raspberry Pi.
● 19
1.6 Summary
In this chapter, we learned how to install the latest Raspberry Pi operating system on an SD
card. Now that the software has been installed and our Raspberry Pi is working, in the next
chapter, we will look at some important commands of the Raspberry Pi operating system.
● 20
2.1 Overview
Linux is one of the most popular operating systems in use today. It is very similar to other
operating systems, such as Windows and UNIX. Linux is an open operating system based
on UNIX and has been developed collaboratively by many companies since 1991. In gener-
al, Linux is harder to manage than some other operating systems like Windows but offers
more flexibility and configuration options. There are several popular versions of the Linux
operating system such as Debian, Ubuntu, Red Hat, Fedora, and so on.
Linux instructions are text-based. In this chapter, we will look at some useful Linux com-
mands and discover how to manage your Raspberry Pi. Commands entered by the user are
in bold for clarity.
Current directory
To show the current working directory, enter the command:
pi@raspberrypi:~ $ pwd
/home/pi
pi@raspberrypi:~ $
Directory structure
To show the directory structure, enter the command:
pi@raspberrypi:~ $ ls /
bin dev home lost+found mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
pi@raspberrypi:~ $
pi@raspberrypi:~ $ ls
book Documents MagPi Music Pictures Scratch Videos
Desktop Downloads _mu_code mytest.txt Public Templates
pi@raspberrypi:~ $
Notice above that the subdirectories are displayed in blue. There is a text file in the working
directory called mytest.txt which is displayed in black.
● 21
Creating a subdirectory
Lets create a new subdirectory called myfiles in our working directory:
As shown in Table 2.1, the ls command can take several arguments. Some examples are
given below.
pi@raspberrypi:~ $ ls -1
book
Desktop
Documents
Downloads
MagPi
mu_code
Music
myfiles
mytest.txt
Pictures
Public
Scratch
Templates
Videos
pi@raspberrypi:~ $
To display the file type, enter the following command. Note that directories have a "/" after
their names, and executable files have a "*" character after their names:
pi@raspberrypi:~ $ ls –F
book/ Documents/ MagPi/ Music/ mytest.txt Public/ Templates/
Desktop/ Downloads mu_code/ myfiles/ Pictures/ Scratch/ Videos/
pi@raspberrypi:~ $
pi@raspberrypi:~ $ ls –m
book, Desktop, Documents, Downloads, MagPi, mu_code, Music, myfiles, mytest.txt,
Pictures, Public Scratch, Templates, Videos
pi@raspberrypi:~ $
● 22
pi@raspberrypi:~ $ ls –m –F
book/, Desktop/, Documents/, Downloads/, MagPi/, mu_code/, Music/, myfiles/,
mytest.txt, Pictures/, Public/, Scratch/, Templates/, Videos/
pi@raspberrypi:~ $
The first word pi in the example in Figure 2.1 shows who the user of the file (or directory)
is, and the second word, pi, shows the group name that owns the file. In this example,
both the user and the group names are pi.
The permissions can be analysed by breaking down the characters into four chunks: File,
User, Group, and World. The first character for a file is "-" and for a directory, it is "d".
Next comes the permissions for the User, Group, and World. The permissions are as follows:
• Read permission (r): the permission to open and read a file or to list a directory
• Write permission (w): the permission to modify a file, or to delete or create a file
in a directory
● 23
• Execute permission (x): the permission to execute the file (applies to executable
files), or to enter a directory
Three letters, rwx, are used as a group and if there is no permission assigned then a "-"
character is used.
As an example, considering the Desktop directory, we have the following permission codes:
d: it is a directory
rwx: user (owner) can read, write, and execute
r-x: group can read and execute, but cannot write (e.g. create or delete)
r-x: world (everyone else) can read and execute, but cannot write
-: it is a file
rw-: user (owner) can read and write, but cannot execute (this is not an exe-
cutable file)
r--: group can only read it, they cannot modify, delete, or execute the file
r--: everyone else (world) can only read it, they cannot modify, delete, or
execute the file
Argument Description
-F Put a symbol after the name to indicate directories and executable files
● 24
The available arguments for changing file permissions are given below. We can use these
arguments to add/remove or explicitly set permissions. It is important to realize that if we
explicitly set permissions, any unspecified permissions in the command will be revoked:
+: add
-: remove
=: set
r: read
w: write
x: execute
To change the permissions of a file, we type the chmod command, followed by one of the
letters u, g, o, or a, followed by the + - or = to select the type of change, and finally fol-
lowed by the filename. An example is given in Figure 2.2. In this example, file mytest.txt
has the user read and write permissions. We will be changing the permissions so that the
user does not have read permission on this file as shown in Figure 2.3.
● 25
Notice that if we now try to display the contents of file mytest.txt using the cat command
we will get an error message:
All the permissions can be removed from a file by the command chmod a=<filename>
as shown in Figure 2.4.
In the example in Figure 2.5, rwx user permissions are given to file mytest.txt.
● 26
pi@raspberrypi:~ $ cd /home/pi/myfiles
pi@raspberrypi:~/myfiles $
pi@raspberrypi:~/myfiles $ cd..
pi@raspberrypi:~ $
to change our working directory to myfiles, we can also enter the command:
pi@raspberrypi:~ $ cd ~/myfiles
pi@raspberrypi:~/myfiles $
pi@raspberrypi:~/myfiles $ cd ~
pi@raspberrypi:~ $
to find out more information about a file we can use the file command. For example:
The –R argument of command ls lists all the files in all the subdirectories of the current
working directory. An example is given below. Notice here that there are no files in subdi-
rectory myfiles:
● 27
pi@raspberrypi:~ $ ls –R
.:
book Documents MagPi Music mytest.txt Public Templates
Desktop Downloads mu_code myfiles Pictures Scratch Videos
./book:
04
………………………………………………………………….
………………………………………………………………….
………………………………………………………………….
pi@raspberrypi:~ $
To display information on how to use a command, we can use the man command. As an
example, to get assist on using the mkdir command:
NAME
Mkdir – make directories
SYNOPSIS
Mkir [OPTION]…DIRECTORY…
DESCRIPTION
Create the DIRECTORY(ies), if they do not already exist.
-m, --mode=MODE
Set file mode (as in chmod), not a=rwx – umask
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
Help
The man command usually gives several pages of information on how to use a command.
We can type q to exit the man command and return to the operating system prompt.
The less command can be used to display a long listing one page at a time. Using the up
and down arrow keys, we can move between pages. An example is given below. Type q to
exit:
● 28
pi@raspberrypi:~ $ date
wed 13 May 25 20:50:46 BST 2020
pi@raspberrypi:~ $
The current calendar (month and year) can be displayed by the command:
pi@raspberrypi:~ $ cal
May 2020
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
pi@raspberrypi:~ $
Copying a file
To make a copy of a file, use the command cp. In the following example, a copy of file
mytest.txt is made and the new file is given the name another.txt:
If we list all the files in our working directory we should see the new file listed:
pi@raspberrypi:~ $ ls
another.txt Documents mu_code mytest.txt Scratch
book Downloads Music Pictures Templates
Desktop MagPi myfiles Public Videos
pi@raspberrypi:~ $
pi@raspberrypi:~ $ ls Documents
hello.sh Hello.sh.odt
pi@raspberrypi:~ $ cp –r Documents NewDocuments
pi@raspberrypi:~ $ ls NewDocuments
hello.sh Hello.sh.odt
pi@raspberrypi:~ $
● 29
Wildcards
We can use wildcard characters to select multiple files with similar characteristics. e.g. files
having the same file-extension names. The "*" character is used to match any number of
characters. Similarly, the "?" character is used to match any single character. In the exam-
ple below all the files with extensions .txt are listed:
pi@raspberrypi:~ $ ls *.txt
another.txt mytest.txt
pi@raspberrypi:~ $
The wildcard characters [a-z] can be used to match any single character in the specified
character range. An example is given below which matches any files that start with letters
o, p, q, r, s, and t, and with the .png extension:
pi@raspberrypi:~ $ ls [a-d]*.txt
another.txt
pi@raspberrypi:~ $
Renaming a file
You can rename a file using the mv command. In the example below, the name of file an-
other.txt is changed to new.txt:
Deleting a file
The command rm can be used to remove (delete) a file:
pi@raspberrypi:~ $ rm new.txt
pi@raspberrypi:~ $
The argument –v can be used to display a message when a file is removed. Also, the –i
argument asks for confirmation before a file is removed. In general, the two arguments are
used together as –vi. An example is shown in Figure 2.6.
Removing a directory
A directory can be removed using the rmdir command:
● 30
pi@raspberrypi:~ $ ls NewDocuments
hello.sh Hello.sh.odt
pi@raspberrypi:~ $ rm –r NewDocuments
pi@raspberrypi:~ $ ls NewDocuments
ls: cannot access 'NewDocuments': No such file or directory
pi@raspberrypi:~ $
Using two greater signs >> adds to the end (appends) of a file. An example is shown in
Figure 2.7.
● 31
The echo command can also be used to write a line of text to a file. An example is shown
below:
Matching a string
The grep command can be used to match a string in a file. An example is given below as-
suming that the file lin.dat contains sting a line of text. The matched word is shown in bold:
Similarly, the tail command is used to display the last 10 lines of a file. The format of this
command is as follows:
● 32
We can also find out if a certain software package is already installed on your computer.
An example is given below which checks whether or not software called xpdf (PDF reader)
is installed. In this example, xpdf is installed and the details of this software is displayed:
If the software is not installed we get a message similar to the following (assuming we are
checking to see if a software package called bbgd is installed):
……………………………………………………………………………..
…………………………………………………………………………….
pi@raspberrypi:~ $
Command history
The command history displays a list of the commands entered on the terminal. As shown
below, each history item has a number associated with it:
pi@raspberrypi:~ $ history
ls
man scrot
man find
scrot –d
ls –lh
date
pi@raspberrypi:~ $
A command in the command history can be run using ! followed by the command history
number. For example, command 6 is run as follows:
pi@raspberrypi:~ $ !6
Wed 13 May 10:05:00 BST 2020
Pi@raspberrypi:~ $
● 33
The list of software that can be installed is normally included in the Raspberry Pi operating
system distribution. This soon becomes out of date. You should use the following update com-
mand to update the list before installing software:
Sometimes we may want to remove an installed software. The remove option of the apt-get
command can be used to remove software as in the following example:
A list of the installed software packages can be displayed by entering the following command:
pi@raspberrypi:~ $ dpkg –l
The following command will stop all the processes and make the file system safe and then
turn off the system safely:
The system can also be shut down and then restarted after a time by entering the following
command. Optionally, a shutdown message can be displayed if desired:
● 34
2.5 Summary
This chapter has described the use of some important Linux commands. You should be able
to get further information on each command and other Linux commands from the internet
and other books on Raspberry Pi and Linux.
In the next chapter, we will look at system resource monitoring commands which are im-
portant for multitasking applications.
● 35
3.1 Overview
Process management and resource monitoring are important topics in multiprocessing and
multitasking applications. Readers must be familiar with the various tools and commands
available for efficient process management and resource monitoring. In this chapter, we
will look at the commonly used tools and commands available for this purpose.
A command is forced to run in the background by appending the & character at the end of
the command. A command that runs in the background is called a job.
In the following example, the date command runs in the background, creates a file called
curdate.txt, and stores the current date in this file. Notice here that [1] is the background
job number id, and 6309 is the background process id. Done indicate that the background
process has completed and terminated:
A list of the processes running in the background can be listed using command jobs. If no
background processes are running, then nothing is displayed by the command:
pi@raspberrypi:~ $ jobs
We may sometimes want to bring a background process to the foreground. This can be
done using the command fg followed by its job number id, provided that the background
job is not terminated. For example, if it is required to run a background process with job
number id [1] in the foreground, you should enter the following command:
pi@raspberrypi:~ $ fg 1
Similarly, in some applications, we may want to send a running foreground process to the
background. This can be done by pressing Cntrl+Z keys (pressing the Cntrl and Z keys
together).
● 36
A foreground process requires user input stops and waits until the user enters an input
and the process then continues. A background process that requires user input also sits
and waits until the user enters the required input. If we run a process in the background,
we must make sure it is not waiting for input, otherwise, the background process will be
suspended forever.
Foreground and background processes terminate when their parent process ends, for ex-
ample when the user logs out from the system. To have the background process run even
after we log out, we must start the process in a specific way, e.g. using the nohup com-
mand. An example is given below where the background process runs even after we log
out:
Running tasks at regular intervals is managed by the crontab command. This consists of
a set of tables (crontab tables) and the crondeamon. The deamon is started by the init
process at system startup and it wakes up every minute and checks the crontab tables to
determine if there are any tasks scheduled to run.
To create a crontab table we use the crontab command with the –e option. This opens
the vi editor (unless another editor is specified in the environment variable). Each crontab
contains 6 fields. The values in the fields can have fixed values or a range of values, or a
list of values separated by commas:
• Minute (0 - 59)
• Hour (0 - 23)
• Day of the month (1 – 31)
• The month of the year (1 – 12. It can also be specified as jan, feb, mar and so on)
• Day of the week (0 – 6, where 0=Sunday, it can also be mon, tue, wed, etc)
• String (command) to be executed
A * character in the digit position means every. Ranges of numbers are specified by sep-
arating them with a hyphen and the specified range is inclusive. For example, 9-12 for an
Hours entry specifies execution at hours 9, 10, 11, and 12.
Skips of numbers in ranges can be specified by adding character / after the range. For
example, 0-12/2 in the Hours field specifies execution every other hour i.e. at hours 0, 2,
4, 6, 8, 10.
By using a combination of * and /, we can specify steps. For example, */2 in the Hours
field specifies every two hours.
● 37
Instead of the first five fields, we can specify special strings as follows:
String meaning
@reboot run once at startup
@yearly run once every year ("0 0 1 1*")
@monthly run once every month ("0 0 1 * *")
@weekly run once a week ("0 0 * * 0")
@daily run once a day ("0 0 * * *")
@hourly run once an hour ("0 * * * *")
Multiple commands can be entered on a line. Such commands must be separated with &&
characters.
2. @daily <command1>&&<command2>
Run command 1 and command 2 daily
3. 30 0 * * */home/pi/mycron.sh
Run at 12:30 daily
4. 0 4 12 * * /home/pi/mycrob.sh
Run at 4 a.m. on the 12th of every month
5. ***** /home/pi/mycron.sh
Run every minute
6. 0 4 15-21 * 1 /home/pi/mycron.sh
Run every month at 4 a.m. on Mondays, and on the days between 15-21. The day of the
month and the day of the week are used with no restrictions (no *). This is an "or" condi-
tion. Both will be executed
7. 0 11,16* * * /home/pi/mycron.sh
Run every day at 11:00 and 16:00 hours
8. 0 11-14 * * * /home/pi/mycron.sh
Run every day during the hours 11 a.m. -2 p.m. (i.e. 11 a.m., 12 a.m., 1 p.m., 2 p.m.)
● 38
By default, crontab sends the job to the user who scheduled the job. If you don't want mail
to be sent to anyone, you should specify the following line in the crontab:
MAIL=""
Any outputs in a scheduled process are usually logged in files. For example, to run mycron.
sh daily and send the output to file daily.txt, enter the following command:
Instead of directly editing the crontab file, you can also add the entries to a cron-file first,
and then install them to cron using the crontab command and specify the filename. An
example is given below:
This will install the mycron.sh to the crontab, which will also remove any old cron entries.
The created crontab is stored in directory /etc/spool/cron/<user>
In all the examples above, we have specified the absolute path of the script file that should
be executed. We can specify this path in the PATH environment variable in the crontab and
then just enter the filename. An example is given below where the absolute path to the file
is /home/pi/mycron.sh
PATH=/home/pi
@daily mycron.sh
If the script or the command we wish to run requires privilege, then it should be prefixed
with the sudo command.
Example
It is required to run myscript.sh every minute. Assume that this script file has the follow-
ing line of command:
Schedule this event using the crontab command. Send the output from the command to a
file called myfile.
Solution
First of all, create the myscript.sh file and type in the above command. The default editor
is nano. Save the file (Cntrl+X, followed by Y and Enter) and exit the editor. Next, give the
file execute permission by entering the following command:
● 39
The steps for using crontab command to schedule this event are given below:
• Run crontab command with the –e flag. You should see the crontab editor screen
as in Figure 3.1
pi@raspberrypi:~ $ crontab –e
* * * * * /home/pi/myscript.sh
Cntrl+X
Y
<Enter>
• The commands in the myscript.sh will now be executed every minute and the
output will be sent to file myfile. We can verify that the commands are executed
by looking at the contents of myfile after a few minutes:
• You can stop the scheduling by deleting the line entered by command
crontab –e
● 40
pi@raspberrypi:~ $ crontab –l
pi@raspberrypi:~ $ crontab –r
pi@raspberrypi:~ $ crontab –l
nocrontab for pi
pi@raspberrypi:~ $
The –i option can be added to the delete command to confirm the delete action.
https://crontab-generator.org/
Figure 3.2 shows part of the Crontab Generator. An example is given to show how to use
this tool.
Example
It is required to run the script file myscript.sh every day at 11:00 a.m. and 4:00 p.m.
hours.
● 41
Solution
The steps are given below:
• Click Save output to file and enter myfile so that the output will be stored in
file myfile.
• You should see the generated line as shown in Figure 3.4, which is:
● 42
• The tool also displays sample dates and times that the script file will be scheduled
to run. It is clear from Figure 3.4 that the script file myscript.sh will run at 11:00
a.m. and 4:00 p.m. every day.
• You should copy the above line to the end of the crontab table by entering the
command crontab –e as discussed earlier.
The following points must be observed when entering a program name in file /etc/rc.lo-
cal, otherwise the system may not be able to complete the boot process:
As an example, insert the following statement to the end of file /etc/rc.local so that pro-
gram myprog runs automatically after the system boots:
/home/pi/myprog
● 43
An example statement is given below which should be included at the end of the crontab
table so that program myprog runs just after the system boot:
@reboot /home/pi/myprog
Assuming our script file is called simple, the chmod command should be used to make the
script executable. e.g.:
To make the program execute on startup, it is necessary to run the following command:
This command creates a link to /etc/init.d/simple and when Raspberry Pi starts or shuts
down it looks to see if any scripts or programs should run. An example is given below.
Example
It is required to start a program called led-control every time the system is started. As-
sume the program resides in the default user directory of /home/pi. Use the /etc/init.d
method to start the program automatically.
Solution
The steps are as follows:
• Use the nano editor to create a script file called simple in directory /etc/init.d
• Enter the following lines into this script file, save the file, and exit the editor:
#!/bin/bash
/home/pi/led-control
● 44
Perhaps one of the most important system monitoring commands is top, which displays
current system processes, how much memory they use, CPU time consumed, process pri-
orities, and much more. Figure 3.5 shows a typical system resource display obtained by
entering the following command:
pi@raspberrypi:~ $ top
The display is dynamic and is updated continuously. It consists of the following sections.
The load average is the average number of processes in the run queue, i.e. processes that
are using the CPU or waiting for their turn.
3.5.2 Tasks
The second line lists the tasks the system is currently performing. The following information
is given:
● 45
Zombie processes are the ones that have finished executing but are still resident in system
memory. Such processes can cause problems.
It is important to notice high values of wa, hi, si, or st. They are not a good sign as high
values indicate the system is waiting on hardware or software to finish processing.
● 46
Notice that the htop command gives a display similar to the top command but it is more
user friendly with colours and dynamic load bars.
The process ID number is a unique number identifying a process. The PR and NI fields
represent the priority of a process. NI displays the nice level which is a static priority of
a process when it is initialized. By default, when a process is initialised it has a nice value
of 0, but can be changed using the nice command. NI has 40 values from -20 to +19. -20
is the highest priority, and +19 the lowest priority. PR shows the process dynamic priority
which is calculated based on the nice value and when a process starts, the PR value equals
NI value plus 20. Usually, processes start with NI values of 0 and PR values of 20. During
the lifetime of a process, PR can change depending upon the system.
The priority of a running process can be changed using the renice command. As an ex-
ample, assume that process rcu_bh has ID = 11 and NI priority of 0. The priority of this
process can be changed to 1 as follows:
The ps command
The nice value of a process can be displayed using the top or ps command. For example,
the PID and nice numbers of process 11 (rcu_bh) can be displayed as follows:
pi@raspberrypi:~ $ ps –o pid,nice –p 11
PID NI
11 1
pi@raspberrypi:~ $
The ps command can also be used to list all processes used by the current user. An exam-
ple is given below:
pi@raspberrypi:~ $ ps
PID TYY TIME CMD
982 pts/0 00:00:00 bash
1172 pts/0 00:00:00 ps
pi@raspberrypi:~ $
● 47
The command ps –ef gives more information about the processes running on the system,
as shown in Figure 3.6.
Figure 3.6 The ps –ef command (only part of the output is shown)
The PID of a process can be displayed using command pidof, followed by the name of the
process. An example is shown below where the PID of process rcu_bh is displayed:
To display the name of a process given its PID, use the ps command with option –q as
shown in the following example:
pi@raspberrypi:~ $ ps –q 11
PID TTY TIME CMD
11 ? 00:00:00 rcu_bh
pi@raspberrypi:~ $
Sometimes we may want to know if a process is running in the system. This can be checked
using the ps command with option –C, followed by the process name. For example, to
check if process rcu_bh is running, enter the following command:
pi@raspberrypi:~ $ ps –C rcu_bh
PID TTY TIME CMD
11 ? 00:00:00 rcu_bh
pi@raspberrypi:~ $
To display the processes started by a specific user (e.g. pi), enter the following command:
pi@raspberrypi:~ $ ps –u pi
Killing a process
There are many options for killing (or stopping) a process. A process can easily be killed by
specifying its PID and using the following command:
● 48
Sometime you may need to use the -9 option to force all related commands to stop. i.e.
You could also use the killall command to stop all occurrences of a process. This command
is not a clean stop as it will immediately stop the specified process:
Figure 3.8 Displaying the CPU information (only part of the display is shown)
● 49
Figure 3.9 Onboard memory information (only part of the display is shown)
● 50
3.6 Summary
In this chapter, we looked at the commands for process management and resource moni-
toring. These commands are important especially in multiprocessing and multitasking ap-
plications where we may want to manipulate several processes.
In the next chapter, we will look at the concepts of multiprocessing and multitasking.
● 51
4.1 Overview
In the last chapter, we looked at important commands for process management and re-
source monitoring which are useful in multiprocessing and multitasking applications. In this
chapter, we will look briefly at the concepts of multiprocessing and multitasking and learn
about their differences. Additionally, we will be looking at the principles of some of the
commonly used scheduling algorithms.
For simplicity, we will use the term multitasking to refer to the use of the following methods
for executing parallel programs on the Raspberry Pi:
• Forks
• Threads (or multithreads)
• Threading (or multithreading)
• Subprocesses
• Multiprocessing
In single-core CPU based systems, only one task can run at any point in time. Multithread-
ing is implemented in such systems by scheduling which task may run at any given time,
and when other waiting tasks can be given CPU time.
In a multicore CPU based system, there are more than one CPU and the operating system
can execute multiple tasks concurrently, where the tasks can work independently of each
other. For example, in a quad-core CPU based system, four applications such as e-mail,
word processing, spreadsheet, and web browsing can each access a separate processor
core at the same time. The user in such a system can perform tasks simultaneously, and
hence an improved overall performance is obtained.
In a multithreading system, there could be several threads (or tasks) in a program, and
the same memory space is used by all the tasks and as a result, precautions should be
taken while reading or writing to the common memory. The Python language supports
multithreaded programming and provides libraries (APIs) to make it easier to create such
applications.
● 52
• Threads share the same variables and run in the same memory space. Processes,
on the other hand, have separate memories
• Sharing objects between threads is easier, but synchorisation to make sure that
two threads will not write to the same object at the same time is important. As a
result of this, thread-based programming is more prone to errors. On the other
hand, process-based programming is less error-prone.
• Threads cannot achieve true parallelism. Processes on the other hand do not
have such restrictions.
• Thread scheduling is done using the Python interpreter, while process scheduling
is handled by the operating system.
• Child threads can't be easily terminated. On the other hand, they can be easily
killed.
• Python offers two libraries in the form of APIs: multiprocessing and threading.
In this book, we will learn how to develop applications using both multithreading and mul-
tiprocessing with the Python programming language.
● 53
• Co-operative scheduling
• Round-robin scheduling
• Pre-emptive scheduling
The type of scheduling algorithm used depends on the nature of the application. In gener-
al, most applications use either one of the above algorithms, a combination of them, or a
modified version of them.
4 .5 .1 Co-operative scheduling
Co-operative scheduling, also known as non-preemptive scheduling, shown in Figure 4.1,
is perhaps the simplest algorithm where tasks voluntarily give up CPU usage when they
have nothing useful to do or when they are waiting for resources to become available. This
algorithm has the main disadvantage that certain tasks can use excessive CPU times, thus
not allowing other important tasks to run when needed. Co-operative scheduling is only
used in simple multitasking systems where there are no time-critical tasks.
State machines, also called finite-state machines (FSM) are probably the simplest ways of
implementing co-operative scheduling. A while loop can be used to execute the tasks, one
after one, as shown below for a 3 task application. In the code below, a task is represented
with a function:
Task1()
{
Task 1 code
}
Task2()
{
Task 2 code
}
Task3()
● 54
{
Task 3 code
}
while(1)
{
Task1();
Task2();
Task3();
{
The tasks are executed one after the other one inside the main infinite loop formed using
a while statement. In this simple approach, the tasks can communicate with each other
using global variables declared at the beginning of the program. The tasks in a co-operative
scheduler must satisfy the following requirements for the successful running of the overall
system:
• Tasks must not block the overall execution, e.g. by using delays or waiting for
some resources and not releasing the CPU.
• Tasks do not have to run to completion and they can exit for example before
waiting for a resource to be available
• Tasks should resume their operations from the point after they release the CPU.
The last requirement listed above is very important and is not satisfied in the simple sched-
uler example given above. Resuming a task requires the address of the program counter
and important variables when the task releases the CPU to be saved, and then restored
when the task resumes (also called context switching) so that the interrupted task can
continue normally as if there has not been any interruption.
Task1()
{
Task 1 code
}
Task2()
{
● 55
Task 2 code
}
Task3()
{
Task 3 code
}
nxt = 1;
while(1)
{
switch(nxt)
{
case 1:
Task1();
nxt = 2;
break;
case 2:
Task2();
nxt = 3;
break;
case 3:
Task3();
nxt = 1;
break;
}
}
4 .5 .2 Round-robin scheduling
Round-robin scheduling (see Figure 4.2) allocates each task an equal share of the CPU
time. Tasks are in a circular queue and when a task's allocated CPU time expires, it is re-
moved and placed at the end of the queue. This type of scheduling cannot be satisfactory in
any real-time application where each task can have a varying amount of CPU requirements
depending on the complexity of the processing involved. Round-robin scheduling requires
the context of the running task to be saved on the stack when a task is removed from the
queue so that the task can resume from the point it was interrupted when it becomes active
again. One variation of the pure Round-robin based scheduling is to provide priority-based
scheduling, where tasks with the same priority levels receive equal amounts of CPU time.
● 56
• It is easy to implement
• Every task gets an equal share of the CPU
• Easy to compute the average response time
• It is not generally good to give the same CPU time to each task
• Some important tasks may not run to completion
• Not suitable for real-time systems where tasks have different processing require-
ments
It is important to realize that in a preemptive scheduler, tasks at the same priorities run
under Round-robin. In such a system, when a task uses its allocated time, a timer interrupt
is generated by the scheduler which saves the context of the current task and gives the CPU
to another task with an equal priority that is ready to run, provided that there are no other
tasks with higher priorities which are ready to run.
● 57
The priority in a preemptive scheduler can be static or dynamic. In a static priority system,
tasks use the same priority all the time. In a dynamic priority-based scheduler, the priority
of tasks can change during their courses of execution.
So far, we have said nothing about how various tasks work together in an orderly man-
ner. In most applications, data and commands must flow between various tasks so that
the tasks can co-operate and work together. One very simple way of doing this is through
shared data held in RAM where every task can access. Modern RTOS systems, however,
provide local task memories and inter-task communication tools such as mailboxes, pipes,
queues, etc to pass data securely and privately between various tasks. Also, tools such as
event flags, semaphores, and mutexes are usually provided for inter-task communication
and synchronization purposes and passing data between tasks.
• Be fair such that each process gets a fair share of the CPU
• Be efficient by keeping the CPU busy. The algorithm should not spend too much
time to decide what to do
• Maximize throughput by minimizing the time users have to wait
• Be predictable so that same tasks take the same time when running multiple
times
• Minimize response time
• Maximize resource use
• Enforce priorities
• Avoid starvation
● 58
Tasks cannot be interrupted A higher priority task interrupts a lower priority one
Waiting and response times are longer Waiting and response times are shorter
• Throughput is low since long processes can hold the CPU, causing short
processes to wait for a long time
• There is no proritization and thus real-time tasks cannot be executed quickly
• It is non-pre-emptive
• The context switching occurs only on task termination and therefore the
overhead is minimal
• Each process gets the chance to be executed even if they have to wait for a
long time
• If a shorter task arrives, the currently running task is interrupted, thus causing
overhead.
• Waiting time of tasks requiring long processing times can be very long
• If there are too many small tasks in the system, longer tasks may never get the
chance to run
● 59
• If a longer task arrives, the currently running task is interrupted, thus causing
overhead.
• Waiting time of tasks requiring short processing times can be very long
• If there are too many long tasks in the system, shorter tasks may never get the
chance to run
4.6 Summary
In this chapter, we learned the differences between multiprocessing and multithreading.
Additionally, the principles of commonly used scheduling algorithms have been briefly de-
scribed.
In the remaining chapters of this book, we will develop various projects using the Raspber-
ry Pi together with the Python programming language, based on the various multitasking
techniques, such as forks, threads, threading, subprocesses, and multiprocessing.
● 60
5.1 Overview
In the last chapter, we looked at the concepts of multitasking and multiprocessing and also
learned the basic principles of some commonly used scheduling algorithms. In this chapter,
we will develop various multitasking projects using the Python programming language on
our Raspberry Pi. All projects in this chapter are based on using the process forks.
Using os.system()
os.system() runs a shell command from a Python program. Some examples are given
below. Notice the os module must be imported to the program and commands must be
enclosed within single or double quotes:
>>> import os
>>> os.system("pwd")
/home/pi
>>> os.system("whoami")
pi
>>> os.system("date")
Thu 14 May 19:45:00 BST 2020
It is important to note that the os.system() command is blocking. i.e. it pauses until the
spawned process exits. One way to get around this problem is to add the & character at the
end of the command to make it a background task as shown below:
Using os.popen()
Os.popen() runs a shell command from a Python program and connects to its input and
output streams. Module os must be Imported to the program. An example is given below:
>>> import os
>>> d = os.popen("date")
>>> c = d.readline()
>>> print(c)
Thu 14 May 19:48:00 BST 2020
● 61
Using subprocess.call()
Using this command requires the subprocess module to be imported. Its use is similar
to os.system(), but it gives more control over how streams are connected and used. An
example is given below:
The new child process is an exact duplicate of the parent process, except for the following
points:
On success, the PID of the child process is returned in the parent, and 0 is returned in
the child. On failure, -1 is returned in the parent, no child process is created, and an error
number is set appropriately.
An example is given below where a child process is created using the fork routine. In this
example, when the os.fork() function is called, the operating system makes an exact copy
of the calling program. But for the child process, a 0 is returned, while for the parent pro-
cess the ID is returned (i.e. a non zero value). In this program the child process and the
parent process both display their process IDs.
● 62
import os
def fork_test():
pid = os.fork()
if pid == 0:
print("Child process pid=%d" % os.getpid())
else:
print("Parent process pid=%d" % os.getpid())
fork_test()
When the program runs, the parent and child processes display their IDs:
import os
import time
def TimeDelay():
time.sleep(5)
pid = os.fork()
if pid > 0:
ChildPID = pid
else:
TimeDelay()
os._exit(0)
● 63
Using fork/exec
The os.fork() function creates an identical copy of a process. This is not very useful as we
may want to carry out different operations in the newly created child process. The fork/
exec combination of functions are used to create a child process that runs a different pro-
gram. The steps are as follows:
Call os.exec() function to replace the child process with a process that we wish to run
An example is given below. Here, program parent.py calls os.fork() to create a child
process. The child process is then replaced with program child.py. Program parent.py
displays message This is the Parent… and the child process displays the message Hello
from the Child…
Program: parent.py
import os
pid = os.fork()
if pid == 0:
os.execlp("python3", "python3", "child.py")
else:
print("This is the Parent..")
Program: child.py
print("Hello from the Child..")
where, program is the executable program's name, and command1, comamnd2 are the
command line arguments (the words we would normally type to start a program). The
os.execlp() function has some variations such as os.execv(), os.execle(), os.execve(),
os.execvpe(), and os.execlpe().
Some simple projects are given in the following sections to show how multitasking applica-
tions can be developed using process forks.
● 64
Description: In this project, two LEDs are connected to the Raspberry Pi. Two tasks are
created using forks such that one of the LEDs flash every second, while the other one flash-
es every 250 milliseconds.
Block Diagram: Figure 5.1 shows the block diagram of the project.
Circuit Diagram: The circuit diagram of the project is shown in Figure 5.2. The LEDs are
connected to Raspberry Pi GPIO 2 and GPIO 3 through 470 Ohm current limiting resistors.
Program Listing: Figure 5.3 shows the program listing (program: two_leds.py). At the
beginning of the program modules RPi, os, and time are imported to the program. Variables
LED2 and LED3 are assigned to 2 and 3 which corresponds to the GPIO pin numbers. The
GPIO mode is then set to BCM and the two LED ports are configured as outputs. The main
program consists of the single statement fork_test() which calls function fork_test().
This function calls to os function fork() to create a child process identical to the parent pro-
cess. The child process is identified with the pid set to 0 and it flashes the LED connected to
GPIO 2 every seconds. The parent process flashes the LED connected to GPIO 3 every 250
● 65
milliseconds. As a result, you should see the two LEDs flashing at different rates.
Notice the program runs in the foreground and therefore the keyboard is not available while
the program is running. We can run the program in the background by inserting an & char-
acter at the end of the command:
#--------------------------------------------------------
# TWO LEDS FLASHING AT DIFFERENT RATES
#
# In this project two LEDs are connected to the
# Raspberry Pi. The LEDs flash at different rates
#
# Author: Dogan Ibrahim
# File : two_leds.py
# Date : MAy 2020
#---------------------------------------------------------
import RPi.GPIO as GPIO
import os
import time
GPIO.setwarnings(False)
● 66
else:
while True: # If parent process
GPIO.output(LED3, 1) # LED ON
time.sleep(0.250) # Wait 250 ms
GPIO.output(LED3, 0) # LED OFF
time.sleep(0.250) # Wait 250 ms
Modified program
In Figure 5.3, one of the LEDs is flashed by the child process while the other one is flashed
by the parent process. In this modified version of the program, we will create two child pro-
cesses to flash the two LEDs. The modified program listing is shown in Figure 5.4 (program:
two_leds2.py). Here, two functions named Flash_LED1 and Flash_LED2 are used to
create two child processes. One child process flashes the LED at GPIO 2 every second, while
the other child process flashes the LED at GPIO 3 every 250 milliseconds.
#--------------------------------------------------------
# TWO LEDS FLASHING AT DIFFERENT RATES
#
# In this project two LEDs are connected to the
# Raspberry Pi. The LEDs flash at different rates.
#
# In this version of the program two child processes
# are created to flash the two LEDs
#
# Author: Dogan Ibrahim
# File : two_leds2.py
# Date : May 2020
#---------------------------------------------------------
import RPi.GPIO as GPIO
import os
import time
GPIO.setwarnings(False)
● 67
You can terminate the background program by entering the following command as before:
Description: This project is similar to Project 1, but here 4 LEDs are used. The project
creates 4 child processes to flash the LEDs at the following rates:
This project aims to show how multiple child processes can be created in a multitasking
environment.
Block Diagram: Figure 5.5 shows the block diagram of the project.
● 68
Circuit Diagram: The circuit diagram of the project is shown in Figure 5.6. The LEDs are
connected to the following GPIO pins of Raspberry Pi 4:
Program Listing: Figure 5.7 shows the program listing (program: four_leds.py). At the
beginning of the program, modules RPi, os, and time are imported to the program as in
Project 1. Variables LED1, LED2, LED3, and LED4 are assigned to 2, 3, 4, and 17 which
correspond to the GPIO pin numbers. The GPIO ports are then configured as outputs. Four
functions are used in the program with the names Flash_LED1, Flash_LED2, Flash_
LED3, and Flash_LED4. Each function creates a child process where each process flashes
one of the LEDs as in Project 1.
● 69
You can terminate the background program by entering the following command as before:
#--------------------------------------------------------
# FOUR LEDS FLASHING AT DIFFERENT RATES
#
# In this project four LEDs are connected to the
# Raspberry Pi. The LEDs flash at different rates
#
# Author: Dogan Ibrahim
# File : four_leds.py
# Date : MAy 2020
#---------------------------------------------------------
import RPi.GPIO as GPIO
import os
import time
GPIO.setwarnings(False)
● 70
GPIO.output(LED2, 1) # LED2 ON
time.sleep(1) # Wait 1 second
GPIO.output(LED2, 0) # LED2 OFF
time.sleep(1) # Wait 1 second
● 71
5.3.3 Project 3 – Setting the LED flashing rate from the keyboard
Description: In this project, an LED is connected to the Raspberry Pi. The flashing rate of
the LED is set dynamically from the keyboard while the LED is flashing.
Circuit Diagram: The circuit diagram of the project is similar to Figure 5.2, but here only
one LED is used and is connected to Raspberry Pi port GPIO 2.
Program Listing: Two processes are used in this process. The child process flashes the
LED, while the parent process reads the required flashing rate in seconds from the key-
board and passes this to the child process so that the flashing rate is changed as required.
It is important to realize that the variables are not shared between the parent and the child
processes. Therefore, if we change the flashing rate in the parent process, the rate will not
change in the child process. There are several ways that variables can be shared, such as
using shared memory, using queues, pipes, and so on. In this project, a queue is used to
send the flashing rate (a floating-point variable) from the parent process to the child pro-
cess as shown in Figure 5.9.
Figure 5.10 shows the program listing (program: led_control.py). At the beginning of
the program, modules RPi, os, and time are imported to the program. Additionally, module
multiprocessing (we will see the use of this module in detail in a later Chapter) is imported
to the program. A queue is created with the name q and number 1 is sent to the queue.
This is the default flashing rate as 1 second. Variable LED2 is set to 2 which corresponds to
GPIO port 2 and this port is configured as an output.
The main program calls function fork_child(). Inside this function, the child process (pid
= 0) gets the flashing rate from the queue by calling function q.get(), and then flashes the
LED at the received rate. Notice that a new flashing rate is only obtained if the queue is not
empty, i.e. if the parent process has inserted a new value to the queue. The parent process
displays the message Enter flashing rate in seconds: and waits until the user enters the
required flashing rate as a floating-point number. This number is stored in a variable called
dly and is sent to the queue using function q.put(). The flashing rate of the LED is then
changed by the child process.
● 72
#-------------------------------------------------------------
# CHANGE LED FLASHING RATE FROM THE KEYBOARD
#
# In this project an LEDs is connected to port GPIO 2 of
# the Raspberry Pi. The LED flashing rate is changed by
# entering it from the keyboard
#
# Author: Dogan Ibrahim
# File : led_control.py
# Date : May 2020
#-------------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import os # Import os
import time # Import time
import multiprocessing # Import multiprocessing
q = multiprocessing.Queue() # Create a queue
GPIO.setwarnings(False) # Disable warnings
● 73
Figure 5.11 shows the message displayed on the screen when the program is run. Here,
the flashing rate is changed to 0.5 seconds. You can terminate the program by entering
Cntrl+C at the keyboard.
Description: This is an event counter project. In this project, an external button is used
to simulate the occurrence of external events. An external event is assumed to occur when
the button is pressed. The events are counted by a child process and the count is sent to
the parent process so that the total count at any time can be displayed on the screen.
Block Diagram: Figure 5.12 shows the block diagram of the project.
Circuit Diagram: The circuit diagram of the project is shown in Figure 5.13. The button is
connected to port GPIO 2 of the Raspberry Pi such that the output of the button is at logic
1 and goes to logic 0 when the button is pressed (i.e. when an external event occurs).
● 74
Program Listing: The program listing is shown in Figure 5.14 (program: events.py). In
this program, a queue is created with the name q. The button is configured as an input
port. At the beginning of the child process, the variable count is initialized to 0. The child
process waits until the button is pressed. i.e. until an external event occurs. When the but-
ton is pressed, the variable count is incremented by 1 and its value is put into the queue.
The child process then waits until the button is released. The parent process checks the
state of the queue and if the queue is not empty, the event count is read and displayed on
the screen.
#------------------------------------------------------------------
# MULTITASKING EVENT COUNTER
#
# This is a multitasking event counter project. A button is
# connected to port GPIO 2 of the Raspberry Pi. External events
# are assumed to occur when the button is pressed. The child process
# counts the events and sends to the parent process where the total
# number of events at any time is displayed
#
# Author: Dogan Ibrahim
# File : events.py
# Date : May 2020
#-------------------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import os # Import os
import time # Import time
import multiprocessing # Import multiprocessing
q = multiprocessing.Queue() # Create a queue
GPIO.setwarnings(False) # Disable warnings
GPIO.setmode(GPIO.BCM)
● 75
Description: In this project, two LEDs named LEDFlash and LEDButton are connected to
ports GPIO 2 and GPIO 3 of the Raspberry Pi through current limiting resistors respectively.
At the same time, a button is connected to port GPIO 4. LEDFlash flashes every second.
LEDButton is turned ON independent of LEDFlash when the button is pressed and is
turned OFF when the button is released.
Block Diagram: Figure 5.16 shows the block diagram of the project.
Circuit Diagram: The circuit diagram of the project is shown in Figure 5.17. The output
state of the button is normally at logic 1 and goes to logic 0 when the button is pressed.
● 76
Program Listing: The program listing is shown in Figure 5.18 (program: LEDButton.py).
At the beginning of the program, the modules required are imported, LEDFlash, LED-
Button and Button are assigned to 2, 3, and 4 respectively to correspond to ports GPIO
2, GPIO 3, and GPIO 4. Ports GPIO 2 and GPIO 3 are configured as outputs, while GPIO 4
is configured as input. The child process flashes the LED every second. Inside the parent
process, LEDButton is turned ON when the button is pressed and turned OFF when the
button is released.
#------------------------------------------------------------------
# LED FLASHING AND LED BUTTON CONTROL
#
# In this program two LEDs,named LEDFlash and LEDButton are connected
# to GPIO 2 and GPIO 3 respectively. In addition, a button is
# conencted to GPIO 4. LEDFlash flashes every second. Pressing the
# button turns LEDButton ON, and releasing the button turns OFF
# LEDButton independent of LEDFlash.
#
# Author: Dogan Ibrahim
# File : LEDButton.py
# Date : May 2020
#-------------------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import os # Import os
import time # Import time
GPIO.setwarnings(False) # Disable warnings
GPIO.setmode(GPIO.BCM)
● 77
5.3.6 Project 6 – S
ynchronizing the parent and child processes -
multitasking event counter
Description: This project is similar to Project 3, but here the parent and child processes
are synchronized. When a new event occurs, the child process puts the event count into the
queue and sets a flag to inform the parent process that a new count is available. The parent
process displays the total count as in Project 3.
Block Diagram: The block diagram of the project is as shown in Figure 5.12
Circuit Diagram: The circuit diagram of the project is as shown in Figure 5.13 where the
button is connected to port GPIO 2 of the Raspberry Pi.
Program Listing: The program listing is shown in Figure 5.19 (program: events_sync.
py). In this program, a queue is created with the name q, and an Event is created with
name e (the details of events will be described in detail in a later Chapter). The button is
configured as an input port. At the beginning of the child process, variable count is initial-
ized to 0. The child process waits until the button is pressed. i.e. until an external event
occurs. When the button is pressed, variable count is incremented by 1 and its value is
put into the queue. Then, function e.set() is called to set an event flag. The child process
then waits until the button is released. The parent process calls to function e.wait() which
suspends the process until the event flag is set by the child process. The total event count
● 78
is then read by the parent process by calling to function q.get() and is displayed on the
screen.
#------------------------------------------------------------------
# MULTITASKING EVENT COUNTER
#
# This is a multitasking event counter project. A button is
# connected to port GPIO 2 of the Raspberry Pi. External events
# are assumed to occur when the button is pressed. The child process
# counts the events and sends to the parent process where the total
# number of events at any time is displayed. The parent and the child
# processes are synchronized when an event occurs
#
# Author: Dogan Ibrahim
# File : events_sync.py
# Date : May 2020
#-------------------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import os # Import os
import time # Import time
import multiprocessing # Import multiprocessing
q = multiprocessing.Queue() # Create a queue
e = multiprocessing.Event() # Create an event
GPIO.setwarnings(False) # Disable warnings
GPIO.setmode(GPIO.BCM)
● 79
Description: This is a multitasking up/down counter project. Two buttons are used with
the names: UP and DOWN. The UP button is controlled by a child process, and DOWN
button by the parent process. Count starts from 0 and pressing UP increments the count
by 1, pressing DOWN decrements the count by 1. When the count reaches 0 it stays at 0.
The total count at any time is displayed on the screen.
Block Diagram: The block diagram of the project is shown in Figure 5.21.
Circuit Diagram: The circuit diagram of the project is shown in Figure 5.22. The buttons
are connected to the Raspberry Pi as follows:
The output state of a button is at logic 1 and goes to logic 0 when the button is pressed.
● 80
Program Listing: The program listing is shown in Figure 5.23 (program: counter.py).
At the beginning of the program, a queue is created with the name q, and an event flag is
created with the name e. The modules used in the program are imported into the program.
Buttons UP and DOWN are assigned to 2 and 3 of the corresponding GPIO ports and are
configured as outputs.
Inside the child process, the process waits until button UP is pressed, and the latest value
of the count is received from the queue and is incremented by 1 and put back into the
queue. At the same time, the event flag is set so that the parent process can continue.
The parent process receives the latest value of the count, decrements it by 1, and puts it
back into the queue. If the count becomes less than 0 then it is set to 0. The total count is
displayed on the screen.
#------------------------------------------------------------------
# UP/DOWN COUNTER
#
# This is an UP/DOWN counter project. 2 buttons are used named as
# UP and DOWN. UP increments the count by 1, and DOWN decrements the
# count by 1. . The child process controls the UP button, while the
# parent process controls the DOWN button. The total count at any time
# is displayed on the screen
#
# Author: Dogan Ibrahim
# File : counter.py
# Date : May 2020
#-------------------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import os # Import os
import multiprocessing # Import multiprocessing
● 81
UP = 2 # UP button at GPIO 2
DOWN = 3 # DOWN button at GPIO 3
● 82
Figure 5.24 shows an output from the program as the UP and DOWN buttons are pressed.
5.4 Summary
In this chapter, we learned how to develop simple multitasking projects using process
forks. In the next chapter, we will look at threads and how we can develop more complex
multitasking applications.
● 83
6.1 Overview
In the last chapter, we looked at the concepts of multitasking using process forks. In this
chapter, we will learn how to develop multitasking applications using threads.
Fork is a new process that looks exactly like the parent process, but it has its own process
ID and memory. It executes independently from the parent process. The child process has
a copy of all the pages corresponding to the parent process, and this is loaded into separate
memory space by the operating system for the child process. As we saw in the previous chap-
ter, after a child process is created, the variables of the parent and the child processes are
not shared. Development of fork based multitasking applications are easy in general and the
code developed is easily maintained. Forking is safe and secure because the child processes
run in their own virtual address spaces. If for example, one child process crashes, it does not
affect any other processes in the system. Threads are in general harder to debug than forks
and are not as portable. Forking is faster than threading, especially on a single CPU. Forks
have longer startup and stopping times. Also, communication and synchronization between
the forked processes are not easy to manage. The management of multitasking is easier with
threads as we can easily end, suspend, and resume threads from the parent. When a parent
exits in a forked child, we get a ghost process that cannot be managed from the parent.
6.2 Threads
Threads are another way used to create multitasking applications. They are like sub-pro-
cesses, but the main difference between a thread and process is that the threads created by
an application share the same memory space. Threads require less overhead than forking
or spawning a new process. The system does not have to initialise a new system virtual
memory space and a new environment with a thread. In general, threads are more effec-
tive on multi-processor or multi-core systems. Because all threads within a process share
the same address space, communication and thread synchronisation is easier to manage
and at the same time, the overhead is reduced.
Threads have unique Thread IDDs, a unique set of registers and stack pointer, local varia-
bles, and priorities. Threads in a process share the following:
Because threads share the same memory space, sharing data between them is easy and
very fast. This can cause race problems if multiple threads attempt to work on the same
data. The programmer has to make sure that the shared data is accessed properly and
safely. There is no process level context switching in multi-threaded applications and as a
result of this, threads give higher speeds. Threads are fast to start and terminate.
● 84
Threads run inside the same main process, in parallel with the main process. Usually, we
create functions inside a main program and then activate these functions to run as inde-
pendent parallel tasks, all as part of the main program. Threads are easy to program since
they can simply be functions in a program and the programmer does not have to worry
about creating child processes using fork or exec functions.
Although threads seem to be easy to program and run, they have a major disadvantage in
that they are not completely independent processes. There is only one standard input (sys.
stdin) and only one standard output (sys.stdout) and usage of input or output functions
within the threads could easily cause conflicts. Also, threads have full access to shared
process resources such as global memory and this requires careful planning to avoid any
read or write conflicts.
Running several threads in a program is like running several different programs concurrent-
ly, but with the following differences:
• Threads within a process share the same data space and therefore it is easier
for the threads to communicate with each other than it is with independent pro-
grams. Because threads share the same memory space, the programmer should
take extra care to avoid multiple threads to work on the shared data at the same
time
• Threads do not have much memory overheads as independent programs
• Threads can be interrupted
• Threads can be suspended while other higher priority threads are running
thread.start_new(name, arguments)
is used to start a new thread. The child thread starts immediately and calls the function
with the passed list of arguments. Here, name is usually the name of a function inside the
main program which will start the thread. The argument field is optional and provides the
means of passing data to the thread. This field should be a tuple. The newly created thread
exists when the function returns (i.e exits). Also, the entire program exits when the main
thread (i.e. the main program) exits.
● 85
Example
An example program is given below in Figure 6.1 (program: exthread.py). In this pro-
gram, two functions named Thread1 and Thread2 are declared and these functions are
created as threads by calling to function thread.start_new(). There are no arguments
passed to these threads in this example. Thread1 displays the message I am Thread1…
twice. Similarly, Thread2 displays the message I am Thread2… twice. Both threads wait
for one second before they continue. The main program waits forever so that the threads
complete their tasks, since terminating the main program will also terminate all the threads.
#-----------------------------------------------------------
# EXAMPLE PROGRAM WITH 2 THREADS
# ==============================
#
# In this program two threads are created where each thread
# displays a message
#
# Author: Dogan Ibrahim
# File : exthread.py
# Date : MAy 2020
#------------------------------------------------------------
import _thread
import time
#
# Thread 1
#
def Thread1():
k = 0 # Initialize k
while k < 2: # Do twice
print("I am Thread1...") # Display message
time.sleep(1) # Wait 1 second
k = k + 1 # Increment k
#
# Thread 2
#
def Thread2():
j = 0 # Initialize j
while j < 2: # Do twice
print("I am Thread2...") # Display message
time.sleep(1) # Wait 1 second
j = j + 1 # Increment j
● 86
while True:
pass
Example
Another example program is given in Figure 6.3 (program: exthread2.py) which shows
how data can be passed to a thread through its arguments. In this example, Thread1
displays the message This is Thread1 twice, and Thread2 displays the message This is
Thread2 three times.
#-----------------------------------------------------------
# EXAMPLE PROGRAM WITH 2 THREADS
# ==============================
#
# In this program two threads are created where each thread
# displays a message
#
# Author: Dogan Ibrahim
# File : exthread2.py
# Date : MAy 2020
#------------------------------------------------------------
import _thread
import time
#
# Thread 1
#
def Thread1(msg, cnt):
k = 0 # Initialize k
while k < cnt: # Do cnt (2) times
print(msg) # Display message
time.sleep(1) # Wait 1 second
k = k + 1 # Increment k
#
# Thread 2
#
● 87
while True:
pass
Figure 6.3 Program: exthread2.py
Notice we can also run the program in the background by inserting the & character at the
end of the command.
In the above program, notice the main parent code waits forever in a while loop and we
have to type Cntrl+C keys to stop the program. We can modify the code and wait until both
threads complete their tasks and exit normally and cleanly. The modified program is given
in the following example.
Example
This example is similar to the previous one, but here a global variable called ThreadCount
is declared and initialized to zero. This variable is incremented when a thread completes
its task. The main program code waits until the ThreadCount reaches 2 (both threads
finished) and then terminates normally, releasing the keyboard. Figure 6.5 shows the mod-
ified program listing (program: exthread3.py).
#-----------------------------------------------------------
# EXAMPLE PROGRAM WITH 2 THREADS
# ==============================
#
# In this program two threads are created where each thread
● 88
ThreadCount = 0
#
# Thread 1
#
def Thread1(msg, cnt):
global ThreadCount
k = 0 # Initialize k
while k < cnt: # Do cnt (2) times
print(msg) # Display message
time.sleep(1) # Wait 1 second
k = k + 1 # Increment k
ThreadCount = ThreadCount + 1 # Increment ThreadCount
#
# Thread 2
#
def Thread2(msg, cnt):
global ThreadCount
j = 0 # Initialize j
while j < cnt: # Do cnt (3) times
print(msg) # Display message
time.sleep(1) # Wait 1 second
j = j + 1 # Increment j
ThreadCount = ThreadCount + 1 # Increment ThreadCount
● 89
Some projects are given in the remaining parts of this chapter to show how multitasking
projects can be developed using thread calls.
Description: In this project, two LEDs are connected to the Raspberry Pi. Two tasks are
created using threads such that one of the LEDs flash every second, while the other one
flashes every 250 milliseconds.
Block Diagram: The block diagram of the project is as shown in Figure 5.1.
Circuit Diagram: The circuit diagram of the project is as shown in Figure 5.2
Program Listing: Figure 6.7 shows the program listing (program: two_leds_thread.
py). At the beginning of the program, modules RPi, thread, and time are imported into the
program. Variables LED2 and LED3 are assigned to 2 and 3 which correspond to the GPIO
pin numbers. The GPIO mode is then set to BCM and the two LED ports are configured as
outputs. There are two threads in the program defined as functions with the names Flash_
LED2 and Flash_LED3. Thread Flash_LED2 flashes the LED at port GPIO 2 every second.
Similarly, thread Flash_LED3 flashes the LED at port GPIO 3 every 250 milliseconds. The
threads are started by the following commands:
As a result, you should see the two LEDs flashing at different rates. The program is started
using the command:
● 90
Notice that the program runs in the foreground and therefore the keyboard is not available
while the program is running. We can run the program in the background by inserting &
character at the end of the command.
#--------------------------------------------------------
# TWO LEDS FLASHING AT DIFFERENT RATES
#
# In this project two LEDs are connected to the Raspberry
# Pi. The LEDs flash at different rates using threading
#
# Author: Dogan Ibrahim
# File : two_leds_thread.py
# Date : May 2020
#---------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
import time
GPIO.setwarnings(False)
#
# Thread to flash LED2
#
def Flash_LED2(): # Thread Flash_LED2
while True: # Do forever
GPIO.output(LED2, 1) # LED2 ON
time.sleep(1) # Wait 1 second
GPIO.output(LED2, 0) # LED2 OFF
time.sleep(1) # Wait 1 second
#
# Thread to flash LED3
#
def Flash_LED3(): # Thread Flash_LED3
while True: # Do forever
GPIO.output(LED3, 1) # LED3 ON
time.sleep(0.250) # Wait 250 ms
GPIO.output(LED3, 0) # LED3 OFF
time.sleep(0.250) # Wait 250 ms
_thread.start_new_thread(Flash_LED2, ())
● 91
_thread.start_new_thread(Flash_LED3, ())
while True:
pass
Modified program
In the program in Figure 6.7, the program runs continuously flashing the two LEDs and
the keyboard is not available while the program is running. In this modified version of the
program (program: two_leds_thread2.py) shown in Figure 6.8, the LEDs flash for 10
seconds and then the program terminates.
#--------------------------------------------------------
# TWO LEDS FLASHING AT DIFFERENT RATES
#
# In this project two LEDs are connected to the Raspberry
# Pi. The LEDs flash at different rates using threading.The
# program terminates after 10 seconds
#
# Author: Dogan Ibrahim
# File : two_leds_thread2.py
# Date : May 2020
#---------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
import time
GPIO.setwarnings(False)
#
# Thread to flash LED2
#
def Flash_LED2(): # Thread Flash_LED2
while True: # Do forever
GPIO.output(LED2, 1) # LED2 ON
time.sleep(1) # Wait 1 second
GPIO.output(LED2, 0) # LED2 OFF
time.sleep(1) # Wait 1 second
● 92
_thread.start_new_thread(Flash_LED2, ())
_thread.start_new_thread(Flash_LED3, ())
#
# Stop after 10 seconds
#
cnt = 0 # Set cnt to 0
while cnt < 10: # Do 10 times
time.sleep(1) # Wait 1 second
cnt = cnt + 1 # Increment cnt
print("End of program") # End of program
Description: This is a multitasking up/down counter project. Three buttons are used with
the names: UP, DOWN, and CLEAR. The count starts from 0 and pressing UP increments
the count by 1, pressing DOWN decrements the count by 1. When the count reaches 0
it stays at 0. Pressing button CLEAR clears the count to 0. The total count at any time is
displayed on the screen.
Block Diagram: The block diagram of the project is shown in Figure 6.9.
● 93
Circuit Diagram: The circuit diagram of the project is shown in Figure 6.10. The buttons
are connected to the Raspberry Pi as follows:
The output state of a button is at logic 1 and goes to logic 0 when the button is pressed.
Program Listing: The program listing is shown in Figure 6.11 (program: counter_thread.
py). At the beginning of the program, the modules used in the program are imported. But-
tons UP, DOWN, and CLEAR are then assigned to 2, 3, and 4 which corresponds to the
GPIO port numbers. These ports are configured as inputs. Three threads are created in the
program, named Button_UP, Button_DOWN, and Button_CLEAR. Taking Button_UP
as an example, this thread waits until the UP button is pressed. When itis pressed, variable
count is incremented by 1. The thread then waits until the button is released. The other
two threads are similar, but Button_DOWN decrements the count, and Button_CLEAR
clears the count. The total count at any time is displayed on the screen. Notice the display
shows only if the value of count changes. The program terminates when the total count
reaches 100 and displays the message End of program.
● 94
#-------------------------------------------------------------
# UP/DOWN COUNTER USING THREADS
# =============================
#
# This is an up/down counter project. Three buttons named
# UP, DOWN, CLEAR are connected to the Raspberry Pi. Pressing
# UP increments the count, pressing DOWN decrements the count
# and pressingCLEAR clears the count to 0. The total count at
# any time is displayed on the screen. The program terminates
# when count becomes 100
#
# Author: Dogan Ibrahim
# File : conter_thread.py
# Date : May 2020
#--------------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
GPIO.setwarnings(False)
UP = 2 # Button UP at GPIO 2
DOWN = 3 # Button DOWN at GPIO 3
CLEAR = 4 # Button CLEAR at GPIO 4
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
GPIO.setup(UP, GPIO.IN) # UP is input
GPIO.setup(DOWN, GPIO.IN) # DOWN is input
GPIO.setup(CLEAR, GPIO.IN) # CLEAR is input
#
# Thread to control button UP
#
def Button_UP(): # Thread Button_UP
global count
while True: # Do forever
while GPIO.input(UP) == 1: # UP not presssed
pass
count = count + 1 # Increment count
while GPIO.input(UP) == 0: # UP not released
pass
#
# Thread to control button DOWN
#
def Button_DOWN(): # Thread Button_DOWN
● 95
global count
while True: # Do forever
while GPIO.input(DOWN) == 1: # DOWN not pressed
pass
count = count - 1 # Decrement count
if count < 0: # If negative
count = 0 # Set to 0
while GPIO.input(DOWN) == 0: # DOWN not released
pass
#
# Thread to control button CLEAR
#
def Button_CLEAR(): # Thread Button_CLEAR
global count
global oldcount
while True: # Do forever
while GPIO.input(CLEAR) == 1: # CLEAR not pressed
pass
count = 0 # Clear count
while GPIO.input(CLEAR) == 0: # CLEAR not released
pass
#
# Create the threads
#
_thread.start_new_thread(Button_UP, ())
_thread.start_new_thread(Button_DOWN, ())
_thread.start_new_thread(Button_CLEAR, ())
#
# Stop if count equals 100
#
while count <= 100: # while count <= 100
if count != oldcount: # If count changed
print("Count = %d" % count) # Display count
oldcount = count
● 96
6.4.3 Project 3 – Setting the LED flashing rate from the keyboard
Description: In this project, an LED is connected to the Raspberry Pi. The flashing rate of
the LED is dynamically set from the keyboard while the LED is flashing.
Circuit Diagram: The circuit diagram of the project is similar to Figure 5.2, but only one
LED is used and is connected to Raspberry Pi port GPIO 2.
Program Listing: Two threads are used in this project. Thread Flash flashes the LED,
while thread Rate changes the flashing rate while the LED is flashing. Initially, the flashing
rate is set to 1 second. Figure 6.13 shows the program listing (program: LEDrate_thread.
py). At the beginning of the program the modules used in the program are imported, LED
is assigned number 2 which corresponds to GPIO port 2, and the port is configured as an
output. Thread Flash flashes the LED with the rate specified by variable dly, which is set to
1 at the beginning of the program. Thread Rate displays the message Enter LED flashing
rate (secs): and prompts the user to enter the required flashing rate in seconds. The value
received is stored in variable dly which changes the LED flashing rate.
#-------------------------------------------------------------
# CHANGE LED FLASHING RATE FROM KEYBOARD
# ======================================
#
# In this program an LED is connected to port GPIO 2 of the
# RAspberry Pi. The LED initially flashes every second. The
# flashing rate can be changed from the keyboard while the
# LED is flashing
#
# Author: Dogan Ibrahim
# File : LEDrate_thread.py
# Date : May 2020
#--------------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
import time
GPIO.setwarnings(False)
● 97
#
# Thread to flash the LED
#
def Flash(): # Thread Flash
global dly
while True: # Do forever
GPIO.output(LED, 1) # LED ON
time.sleep(dly) # Wait dly seconds
GPIO.output(LED, 0) # LED OFF
time.sleep(dly) # Wait dly seconds
#
# Thread to change the LED flashing rate
#
def Rate(): # Thread Rate
global dly
while True: # Do forever
dly = float(input("Enter LED flashing rate (secs): "))
#
# Create the threads
#
_thread.start_new_thread(Flash, ())
_thread.start_new_thread(Rate, ())
while True:
pass
Figure 6.13 Program: LEDrate_thread.py
Description: In this project, an LED and two buttons are connected to the Raspberry Pi.
By default the LED flashes every second. The buttons are named UP and DOWN. Each time
UP is pressed, the flashing rate is incremented by 200 milliseconds. Similarly, each time
● 98
DOWN is pressed, the flashing rate is decremented by 200 milliseconds. If the flashing rate
becomes less than 0 then it is set to 0.
Block Diagram: Figure 6.15 shows the block diagram of the project.
Circuit Diagram: The circuit diagram of the project is shown in Figure 6.16. Buttons UP
and DOWN are connected to GPIO 2 and GPIO 3 respectively. The LED is connected to GPIO
4.
Program Listing: Figure 6.17 shows the program listing (program: rate_thread.py).
At the beginning of the program the modules used are imported to the program, buttons
UP, DOWN, and the LED are assigned to 2, 3, and 4 to correspond to GPIO 2, GPIO 3,
and GPIO 4 respectively. The buttons are configured as inputs and the LED is configured as
output. There are three threads in this program: Flash, UP_Button, and DOWN_Button.
Thread Flash flashes the LED with the rate specified by global variable dly. Thread UP_
Button increases the flashing rate by 200ms when button UP is pressed. Similarly, thread
DOWN_Button decrements the flashing rate by 200ms when button DOWN is pressed.
● 99
#-----------------------------------------------------------------
# CHANGE LED FLASHING RATE USING BUTTONS
# ======================================
#
# In this program an LED and two buttons are connected to the
# Raspberry Pi. The default LED flashing rate is 1 second.The
# buttons are named UP and DOWN. Pressing UP increases the flashing
# rate by 200ms. Similarly, pressing DOWN decreases the flashing
# rate by 200ms.
#
# Author: Dogan Ibrahim
# File : rate_thread.py
# Date : May 2020
#-------------------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
import time
GPIO.setwarnings(False)
UP = 2 # Button UP at GPIO 2
DOWN = 3 # Button DOWN at GPIO 3
LED = 4 # LED at GPIO 4
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
GPIO.setup(UP, GPIO.IN) # UP is input
GPIO.setup(DOWN, GPIO.IN) # DOWN is input
GPIO.setup(LED, GPIO.OUT) # LED is output
dly = 1.0 # Default flashing rate
#
# Thread to flash the LED
#
def Flash(): # Thread Flash
global dly
while True: # Do forever
GPIO.output(LED, 1) # LED ON
time.sleep(dly) # Wait dly seconds
GPIO.output(LED, 0) # LED OFF
time.sleep(dly) # Wait dly seconds
#
# Thread to increase the flashing rate
#
def UP_Button(): # Thread UP_Button
global dly
while True: # Do forever
while GPIO.input(UP) == 1: # UP not pressed
pass
● 100
#
# Thread to decrease the flashing rate
#
def DOWN_Button(): # Thread DOWN_Button
global dly
while True: # Do forever
while GPIO.input(DOWN) == 1: # DOWN not pressed
pass
dly = dly - 0.2 # Decrease flashing rate
if dly < 0:
dly = 0
while GPIO.input(DOWN) == 0: # DOWN not released
pass
#
# Create the threads
#
_thread.start_new_thread(Flash, ())
_thread.start_new_thread(UP_Button, ())
_thread.start_new_thread(DOWN_Button, ())
while True:
pass
● 101
7-segment LED displays: Displaying data is one of the fundamental output activities of
any microcontroller system. For example, displays are used to show sensor data such as
temperature, humidity, pressure, etc. Several types of display devices can be used in mi-
crocontroller-based systems. LCDs and 7-segment displays are probably two of the most
commonly used display devices. There are several types of LCDs, such as text-based LCD,
graphics LCDs, colour LCDs, touch screen LCDs, etc. 7-segment displays are used to display
numeric or alphanumeric values, and they can have one or more digits. One digit displays
can only display numbers from 0 to 9. Two-digit displays can display numbers from 0 to 99,
three-digit displays numbers from 0 to 999, and so on. In this project, a two-digit 7-seg-
ment display is used.
As shown in Figure 6.18, a 7-segment LED display consists of 7 LEDs connected such that
numbers from 0 to 9 and some letters can be displayed. Segments are identified by letters
from a to g. Figure 6.19 shows the segment names of a typical 7-segment display.
f b
g
e c
d
● 102
Figure 6.20 shows how numbers from 0 to 9 can be obtained by turning ON or OFF different
segments of the display.
Multiplexed 7-segment LED displays are available in two different configurations: common
cathode and common anode. As shown in Figure 6.21, in common cathode configura-
tion, all cathodes of all segment LEDs are connected to ground. The segments are then
turned ON by applying a logic 1 to the required segment LED via current limiting resistors.
In common cathode configuration, the 7-segment LED is connected to the microcontroller
in current sourcing mode.
In a common anode configuration, the anode terminals of all the LEDs are connected as
shown in Figure 6.22. This common point is then normally connected to the supply voltage.
A segment is turned ON by connecting its cathode terminal to logic 0 via a current limiting
resistor. In common anode configuration, the 7-segment LED is connected to the microcon-
troller in current sinking mode.
In multiplexed LED applications (for example, see Figure 6.23 for a 2-digit multiplexed LED
display), the LED segments of all the digits are tied together and the common pins of each
digit are turned ON separately by the microcontroller. By displaying each digit for several
● 103
milliseconds the eye can not differentiate that the digits are not ON all the time. This way
we can multiplex any number of 7-segment displays together. For example, to display
number 57, we have to send 5 to the first digit and enable its common pin. After a few
milliseconds, number 7 is sent to the second digit and the common point of the second digit
is enabled. When this process is continuously repeated, the user sees it as if both displays
are ON continuously.
Some manufacturers provide multiplexed multi-digit displays in single packages. For ex-
ample, we can purchase 2,4, or 8 digit multiplexed displays in a single package. The dis-
play used in this project is the DC56-11EWA which is a red colour, 0.56 inch height com-
mon-cathode two-digit multiplexed display with 18 pins, where the pin configuration is
shown in Table 6.1. This display can be controlled from the microcontroller as follows:
● 104
Pin no Segment
1,5 e
2,6 d
3,8 c
14 digit 1 Enable
17,7 g
15,10 b
16,11 a
18,12 f
13 digit 2 Enable
4 decimal Point1
9 decimal Point 2
Block Diagram: Figure 6.25 shows the block diagram of the project.
Enable 1
Enable 2
● 105
Circuit Diagram: The circuit diagram of the project is shown in Figure 6.26. In this project,
the following pins of the Raspberry Pi are used to interface with the 7-segment LED display:
7-segment display segments are driven from the port pins through 470 Ohm current lim-
iting resistors. Digit enable pins E1 and E2 are driven from port pins GPIO 11 and GPIO 9
respectively through two BC108 type NPN transistors (any other NPN transistor can be used
here), used as switches. The collectors of these transistors drive the segment digits. The
segments are enabled when the base of the corresponding transistor is set to logic 1. Notice
the following pins of the display are connected to form a multiplexed display:
Program Listing: Before driving the display, we have to know the relationship between
the numbers to be displayed and the corresponding segments to be turned ON, and this is
shown below:
● 106
Figure 6.27 shows the program listing (program: SevenCount.py). At the beginning of
the program, the connections between the LED segments and GPIO pins are defined in
list variable LED_Segments. Also, the connections between the LED digits and the GPIO
pins are defined in the list variable LED_Digits. These GPIO pins are then configured as
outputs and are all cleared to 0. Variable count is initialized to zero at the beginning of the
program. The program consists of two threads, named: Refresh and Up_Count. Thread
Refresh displays the number in variable count and refreshes the multiplexed LED con-
tinuously every millisecond so that the digits seem to be ON all the time. If the number to
be displayed is less than 10, a 0 is inserted in front of the number so that the numbers 0
to 9 are displayed as 00 to 09. Thread Up_Count simply increment variable count every
second. When count reaches 100 it is cleared back to 0 and the counting continues.
#-----------------------------------------------------------------
# 2 DIGIT SEVEN SEGMENT LED COUNTER
# =================================
#
# This is a 2 digit 7-segment LED counter program. The program
# counts up every second from 0 to 99 continuously. The LED matrix
# is refreshed in a thread. The connections between the 7-segment
# LED and the RaspberryPi are as follows:
#
# 7-Segment LED GPIO
# a 2
# b 3
# c 4
# d 17
# e 27
# f 22
# g 10
# E2 (digit) 9
# E1 (digit) 11
#
# Author: Dogan Ibrahim
# File : SevenCount.py
● 107
#
# Configure the segments and digits as outputs and clear them
#
for seg in LED_Segments:
GPIO.setup(seg, GPIO.OUT) # Segments are outputs
GPIO.output(seg, 0) # Clear all segments
LED_Bits ={
'0':(1,1,1,1,1,1,0), # 0
'1':(0,1,1,0,0,0,0), # 1
'2':(1,1,0,1,1,0,1), # 2
'3':(1,1,1,1,0,0,1), # 3
'4':(0,1,1,0,0,1,1), # 4
'5':(1,0,1,1,0,1,1), # 5
'6':(1,0,1,1,1,1,1), # 6
'7':(1,1,1,0,0,0,0), # 7
'8':(1,1,1,1,1,1,1), # 8
'9':(1,1,1,1,0,1,1)} # 9
#
# Thread to refresh the 7-segment LED
#
def Refresh(): # Thread Refresh
global count
while True: # Do forever
cnt = str(count) # into string
if len(cnt) < 2:
cnt = "0" + cnt # Make sure 2 digits
for dig in range(2): # Do for 2 digits
● 108
#
# Thread to count up evey second
#
def UP_Count(): # Thread UP_Count
global count
while True: # Do forever
time.sleep(1) # Wait a second
count = count + 1 # Increment count
if count == 100: # If count = 100
count = 0
#
# Create the threads
#
_thread.start_new_thread(Refresh, ())
_thread.start_new_thread(UP_Count, ())
while True:
pass
Modified program
In the program shown in Figure 6.27, numbers less than 10 are displayed with a leading 0
(e.g. 05). We can remove the leading zero by blanking all segments of the left-hand digit
if the number is less than 10 so that for example number five is displayed as 5 and not as
05. The modified program listing (program: SevenCount2.py) is shown in Figure 6.28.
Here, list LED_Bits is modified by adding a blank line where all the segment bits are set to
0. Additionally, a blank character is inserted to the front of string cnt if the number is less
than 10 so that the leading digit is blanked.
#-----------------------------------------------------------------
# 2 DIGIT SEVEN SEGMENT LED COUNTER
# =================================
#
# This is a 2 digit 7-segment LED counter program. The program
# counts up every second from 0 to 99 continuously. The LED matrix
# is refreshed in a thread. The connections between the 7-segment
# LED and the RaspberryPi are as follows:
#
● 109
#
# Configure the segments and digits as outputs and clear them
#
for seg in LED_Segments:
GPIO.setup(seg, GPIO.OUT) # Segments are outputs
GPIO.output(seg, 0) # Clear all segments
LED_Bits ={
' ':(0,0,0,0,0,0,0), # Blank
'0':(1,1,1,1,1,1,0), # 0
'1':(0,1,1,0,0,0,0), # 1
'2':(1,1,0,1,1,0,1), # 2
'3':(1,1,1,1,0,0,1), # 3
'4':(0,1,1,0,0,1,1), # 4
'5':(1,0,1,1,0,1,1), # 5
● 110
'6':(1,0,1,1,1,1,1), # 6
'7':(1,1,1,0,0,0,0), # 7
'8':(1,1,1,1,1,1,1), # 8
'9':(1,1,1,1,0,1,1)} # 9
#
# Thread to refresh the 7-segment LED
#
def Refresh(): # Thread Refresh
global count
while True: # Do forever
cnt = str(count) # into string
if len(cnt) < 2:
cnt = " " + cnt # Make sure 2 digits
for dig in range(2): # Do for 2 digits
for loop in range(0,7): # Do for all segments
GPIO.output(LED_Segments[loop], LED_Bits[cnt[dig]][loop])
GPIO.output(LED_Digits[dig], 0)
time.sleep(0.001)
GPIO.output(LED_Digits[dig], 1)
#
# Thread to count up evey second
#
def UP_Count(): # Thread UP_Count
global count
while True: # Do forever
time.sleep(1) # Wait a second
count = count + 1 # Increment count
if count == 100: # If count = 100
count = 0
#
# Create the threads
#
_thread.start_new_thread(Refresh, ())
_thread.start_new_thread(UP_Count, ())
while True:
pass
● 111
Description: In this project, the ambient temperature is read using a temperature sensor
chip. It is then displayed on a 7-segment 2-digit multiplexed LED display as an integer
number every minute. Two threads are used in the program, where one thread reads the
temperature and another thread displays it on the 7-segment LED while at the same time
refreshes the LED continuously.
Block Diagram: Figure 6.29 shows the block diagram of the project. In this project, a
DHT11 temperature and humidity sensor chip is used to read the ambient temperature.
Circuit diagram: A DHT11 type temperature and humidity sensor chip (see Figure 6.30)
is used in this project. This is normally a 3-pin sensor (there is also a 4-pin version of this
sensor where one of the pins is not used) with pins GND, +V, and Data. GND and +V are
connected to the ground and the +3.3V power supply pins of the Raspberry Pi. The Data
pin must be connected to +V through a 10K resistor. In this project, a 3-pin DHT11 from
Elektor is used with a built-in 10K pull-up resistor. As shown in Figure 6.31, the data pin of
the sensor is named as S and is connected to GPIO 26 of the Raspberry Pi. The 7-segment
LED is connected to the Raspberry Pi as in the previous project.
● 112
DHT11 uses a capacitive humidity sensor and a thermistor to measure ambient tempera-
ture. Data output is available from the chip around every two seconds. The basic features
of DHT11 are:
• 3 to 5V operation
• 2.5mA current consumption (during a conversion)
• Temperature reading in the range 0-50ºC with an accuracy of ±2ºC
• Humidity reading in the range 20-80% with 5% accuracy
• Breadboard compatible with 0.1-inch pin spacings
Program listing: In this program, the Adafruit DHT11 library (Adafruit_DHT) module
is used. This module should be installed to the Raspberry Pi before it can be used. The in-
structions for this are as follows:
pi@raspberrypi:~ $ cd Adafruit_python_DHT
pi@raspberrypi:~/Adafruit_python_DHT $
● 113
Figure 6.32 shows the program listing (program: dht11.py). At the beginning of the pro-
gram, the modules used in the program are imported including the Adafruit_DHT library.
This library supports both DHT11 and DHT22 type sensors. Here, the sensor type is spec-
ified as DHT11 by the statement Adafruit_DHT.DHT11. Variable S is set to 26 which
corresponds to GPIO 26. The parts of the code that initialises the 7-segment variables and
segments is the same as in the previous project and is not repeated here. Also, thread Re-
fresh is the same as in the previous project. Thread Get_Temp reads the humidity and the
temperature from the DHT11 sensor every minute. In this project, only the temperature
readings are used and stored in variable temperature. The temperature is then converted
into an integer number and is stored in variable temp which is used by thread Refresh to
display the temperature.
#-----------------------------------------------------------------
# DISPLAY TEMPERATURE ON 7-SEGMENT LED
# ====================================
#
# In this program the ambient temperature is displayed on a 2 digit
# 7-segment LED every minute. DHT11 type temperature sensor is used
# in this project and is connected to GPIO 26. The 7-segment LED is
# connected to the RAspberry Pi as follows:
#
# 7-Segment LED GPIO
# a 2
# b 3
# c 4
# d 17
# e 27
# f 22
# g 10
# E2 (digit) 9
# E1 (digit) 11
#
#
# Author: Dogan Ibrahim
# File : dht11.py
# Date : May 2020
#-------------------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
import time
import Adafruit_DHT # Adafruit DHT11 library
● 114
sensor = Adafruit_DHT.DHT11
S = 26 # DHT11 on GPIO 26
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
#
# Configure the segments and digits as outputs and clear them
#
for seg in LED_Segments:
GPIO.setup(seg, GPIO.OUT) # Segments are outputs
GPIO.output(seg, 0) # Clear all segments
LED_Bits ={
' ':(0,0,0,0,0,0,0), # Blank
'0':(1,1,1,1,1,1,0), # 0
'1':(0,1,1,0,0,0,0), # 1
'2':(1,1,0,1,1,0,1), # 2
'3':(1,1,1,1,0,0,1), # 3
'4':(0,1,1,0,0,1,1), # 4
'5':(1,0,1,1,0,1,1), # 5
'6':(1,0,1,1,1,1,1), # 6
'7':(1,1,1,0,0,0,0), # 7
'8':(1,1,1,1,1,1,1), # 8
'9':(1,1,1,1,0,1,1)} # 9
#
# Thread to refresh the 7-segment LED
#
def Refresh(): # Thread Refresh
global temp
while True: # Do forever
cnt = str(temp) # into string
if len(cnt) < 2:
cnt = " " + cnt # Make sure 2 digits
for dig in range(2): # Do for 2 digits
for loop in range(0,7): # Do for all segments
GPIO.output(LED_Segments[loop], LED_Bits[cnt[dig]][loop])
● 115
GPIO.output(LED_Digits[dig], 0)
time.sleep(0.001)
GPIO.output(LED_Digits[dig], 1)
#
# Thread to get the ambient temperature from DHT11 every minute
#
def Get_Temp(): # Thread Get_Temp
global temp
while True: # Do forever
humidity,temperature = Adafruit_DHT.read_retry(sensor, S)
temp = int(temperature)
time.sleep(60)
#
# Create the threads
#
_thread.start_new_thread(Refresh, ())
_thread.start_new_thread(Get_Temp, ())
while True:
pass
6.4.7 Project 7 – S
quare waveform generator with 7-segment LED display
and keyboard
Description: In this project, square waveform is generated. The frequency can be set from
the keyboard while the waveform is being displayed. The selected frequency can be set
between 1 and 99Hz and is displayed on the 7-segment LED.
Program listing: In this program, the square waveform is generated using a PWM (Pulse
Width Modulation) channel of the Raspberry Pi. PWM waveforms are frequently used in
power control applications. The waveform is a positive only square waveform with variable
ON and OFF times. As shown in Figure 6.33, the total of the ON and OFF times is known as
the Period of the waveform. The ratio of the ON time to the period is known as Duty Cycle
and is represented as a percentage. i.e.
where, T is the ON time, and P is the period (ON time + OFF time).
● 116
By varying the duty cycle from 0% to 100% we can easily control a load, e.g. a motor. For
example, at a 50% duty cycle, the load receives half of the total power. Similarly, at 100%
duty cycle the load receives full power.
In this program, GPIO 26 is chosen as the PWM channel. Figure 6.34 shows the program
listing (program: square.py). The program consists of two threads, named: Refresh and
Change_Freq. Thread Refresh refreshes the 7-segment LED display and shows the set
frequency of the PWM waveform. Thread Change_Freq generates the PWM waveform
at GPIO 26 of the Raspberry Pi. The Duty Cycle of the waveform is set to 50% using the
function call:
p.start(50)
The frequency of the waveform is initially assumed to be 50Hz and is set by the function
call (notice that frequency must be entered in Hz):
p = GPIO.PWM(26, freq)
where variable freq is set to 50 at the beginning of the program. The user is prompted to
set the frequency after displaying the message: Enter PWM frequency (Hz): Frequency
can be changed by the function call:
p.ChangeFrequency(freq)
● 117
#-----------------------------------------------------------------
# GENERATE SQUARE WAVEFORM
# ========================
#
# This program generates square waveforms using the PWM module of
# the Raspberry Pi. The frequency of the waveform can be set between
# 1 and 99Hz through the keyboard. The set frequency is displayed
# on the 7-segment LED display. The waveform is output at GPIO 26 of
# the Raspberry Pi.
# Connections of the 7-segment LED to the Raspberry Pi are as follows:
#
# 7-Segment LED GPIO
# a 2
# b 3
# c 4
# d 17
# e 27
# f 22
# g 10
# E2 (digit) 9
# E1 (digit) 11
#
# Author: Dogan Ibrahim
# File : square.py
# Date : May 2020
#-------------------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
#
# Configure the segments and digits as outputs and clear them
#
for seg in LED_Segments:
GPIO.setup(seg, GPIO.OUT) # Segments are outputs
GPIO.output(seg, 0) # Clear all segments
● 118
LED_Bits ={
' ':(0,0,0,0,0,0,0), # Blank
'0':(1,1,1,1,1,1,0), # 0
'1':(0,1,1,0,0,0,0), # 1
'2':(1,1,0,1,1,0,1), # 2
'3':(1,1,1,1,0,0,1), # 3
'4':(0,1,1,0,0,1,1), # 4
'5':(1,0,1,1,0,1,1), # 5
'6':(1,0,1,1,1,1,1), # 6
'7':(1,1,1,0,0,0,0), # 7
'8':(1,1,1,1,1,1,1), # 8
'9':(1,1,1,1,0,1,1)} # 9
#
# Thread to refresh the 7-segment LED
#
def Refresh(): # Thread Refresh
global freq
while True: # Do forever
f = str(freq) # into string
if len(f) < 2:
f = " " + f # Make sure 2 digits
for dig in range(2): # Do for 2 digits
for loop in range(0,7): # Do for all segments
GPIO.output(LED_Segments[loop], LED_Bits[f[dig]][loop])
GPIO.output(LED_Digits[dig], 0)
time.sleep(0.001)
GPIO.output(LED_Digits[dig], 1)
#
# Thread change the PWM frequency
#
def Change_Freq(): # Thread Change_Freq
global freq
PWM_Port = 26 # PWM on port 26
GPIO.setup(PWM_Port, GPIO.OUT) # Configure as output
p = GPIO.PWM(PWM_Port, freq) # Initialize
p.start(50) # Start PWM with DC=50%
while True: # Do forever
freq = int(input("Enter PWM frequency (Hz): "))
p.ChangeFrequency(freq)
● 119
#
# Create the threads
#
_thread.start_new_thread(Refresh, ())
_thread.start_new_thread(Change_Freq, ())
while True:
pass
Figure 6.34 Program: square.py
6.4.8 Project 8 – S
quare waveform generator with 7-segment LED display and
buttons
Description: This project is similar to Project 7, but here 4 buttons are used to set the
frequency of the PWM waveform instead of the keyboard. As a result, the project is portable
and is independent of the keyboard. The buttons are named: UP10, UP1, DOWN10, and
DOWN1. Frequency is initially set to 50Hz, but it can be set to any integer value from 1
to 99Hz. Buttons UP10 and UP1 increment the frequency in 10s and 1 unit respectively.
For example, pressing button UP10 will increment frequency to 60Hz. Pressing UP1 will
increment it to 61Hz and so on. Similarly, pressing DOWN10 and DOWN1 will decrement
frequency in 10s and 1 unit respectively. The frequency can not be below 1Hz or above
99Hz. The 7-segment LED displays the set frequency.
Block Diagram: The block diagram of the project is shown in Figure 6.35.
Circuit Diagram: The circuit diagram of the project is shown in Figure 6.36. The buttons
are connected to the Raspberry Pi as follows:
● 120
The output state of a button is at logic 1 and goes to logic 0 when the button is pressed.
Program Listing: The program listing is shown in Figure 6.37 (program: square_but-
tons.py). At the beginning of the program, the modules used in the program are imported.
Then the 7-segment LED segment and digit variables are defined as in the previous 7-seg-
ment LED projects. The default starting frequency is set to 50Hz. Buttons UP10, UP1,
DOWN10, and DOWN1 are set to 12, 16, 20, and 21 respectively to correspond to the
GPIO pins. These port pins are configured as inputs. The program consists of 6 threads:
Thread Refresh refreshes the 7-segment LED and displays the set frequency value, which
is in variable called freq.
Thread Freq_UP10 increments the frequency by 10Hz whenever button UP10 is pressed.
If the frequency goes above 99, it is set to 99. Notice in the previous projects the button
pressing action was detected by the following statement:
while GPIO.input(UP10) == 1:
pass
The above statement continuously checks the state of the button until it is pressed. This
statement consumes too much CPU time as it is running continuously and as a result, the
refreshing of the 7-segment LED display may be affected, causing the digits to flicker. In
this program, instead of using the pass statement, a small delay (0.2 seconds) is used so
● 121
that the thread is suspended and releases the CPU time to other threads in the system. As
a result, thread Refresh has more CPU time, thus eliminating flickering of the digits.
Thread Freq_UP1 is similar to Freq_UP10 but here the frequency is incremented by 1Hz.
Thread Generate_PWM generates the PWM waveform at GPIO 26. Here, port GPIO 26
is initially configured as an output and the PWM is initialized. The PWM waveform is then
generated with a default frequency of 50Hz and the Duty Cycle of 50%. The thread checks
if the frequency is changed by the user and if so calls function ChangeFrequency() to
change the frequency to the required value.
#-----------------------------------------------------------------
# GENERATE SQUARE WAVEFORM
# ========================
#
# This program generates square waveforms using the PWM module of
# the Raspberry Pi. The frequency of the waveform can be set between
# 1 and 99Hz through 4 buttons named UP10,UP1,DOWN10 and DOWN1.
# Pressing UP10 or DOWN10 increments or decrements teh frequecny
# by 10Hz respectively. Similarly, pressing UP1 and DOWN1 decrease
# teh frequency by 1Hz. The frequency si set to 50HZ at startup.
# Connections of the 7-segment LED to the Raspberry Pi are as follows:
#
# 7-Segment LED GPIO
# a 2
# b 3
# c 4
# d 17
# e 27
# f 22
# g 10
# E2 (digit) 9
# E1 (digit) 11
#
# Author: Dogan Ibrahim
# File : square_buttons.py
# Date : May 2020
#-------------------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
● 122
#
# Configure the segments and digits as outputs and clear them
#
for seg in LED_Segments:
GPIO.setup(seg, GPIO.OUT) # Segments are outputs
GPIO.output(seg, 0) # Clear all segments
LED_Bits ={
' ':(0,0,0,0,0,0,0), # Blank
'0':(1,1,1,1,1,1,0), # 0
'1':(0,1,1,0,0,0,0), # 1
'2':(1,1,0,1,1,0,1), # 2
'3':(1,1,1,1,0,0,1), # 3
'4':(0,1,1,0,0,1,1), # 4
'5':(1,0,1,1,0,1,1), # 5
'6':(1,0,1,1,1,1,1), # 6
'7':(1,1,1,0,0,0,0), # 7
'8':(1,1,1,1,1,1,1), # 8
'9':(1,1,1,1,0,1,1)} # 9
● 123
#
# Thread UP10
#
def Freq_UP10(): # Thread Freq_UP10
global freq
while True: # Do forever
while GPIO.input(UP10) == 1: # UP10 not pressed
time.sleep(0.2)
freq = freq + 10 # Increment freq
if freq > 99:
freq = 99
while GPIO.input(UP10) == 0: # UP10 not released
time.sleep(0.2)
#
# Thread UP1
#
def Freq_UP1(): # Thread UP1
global freq
while True: # Do forever
while GPIO.input(UP1) == 1: # UP1 not pressed
time.sleep(0.2)
freq = freq + 1 # Increment freq
if freq > 99:
freq = 99
while GPIO.input(UP1) == 0: # UP1 not released
time.sleep(0.2)
#
# Thread DOWN10
#
def Freq_DOWN10(): # Thread DOWN10
global freq
while True: # Do forever
while GPIO.input(DOWN10) == 1: # DOWN10 not pressed
time.sleep(0.2)
freq = freq - 10 # Decrement freq
if freq < 0:
freq = 1
● 124
#
# Thread DOWN1
#
def Freq_DOWN1(): # Thread DOWN1
global freq
while True: # Do forever
while GPIO.input(DOWN1) == 1: # DOWN1 not pressed
time.sleep(0.2)
freq = freq - 1 # Decrement freq
if freq < 0:
freq = 1
while GPIO.input(DOWN1) == 0: # DOWN1 not released
time.sleep(0.2)
#
# Thread Generate_PWM
#
def Generate_PWM(): # Thread Generate_PWM
global freq
global oldfreq
PWM_Port = 26 # PWM port
GPIO.setup(PWM_Port, GPIO.OUT) # Configure as output
p =GPIO.PWM(PWM_Port, freq) # Initialize PWM
p.start(50) # Start with DC=50%
while True: # Change freq
if freq != oldfreq:
p.ChangeFrequency(freq)
oldfreq = freq
time.sleep(0.5)
#
# Create the threads
#
_thread.start_new_thread(Refresh, ())
_thread.start_new_thread(Freq_UP10, ())
_thread.start_new_thread(Freq_UP1, ())
_thread.start_new_thread(Freq_DOWN10, ())
_thread.start_new_thread(Freq_DOWN1, ())
_thread.start_new_thread(Generate_PWM, ())
while True:
pass
Figure 6.37 Program: square_buttons.py
● 125
Figure 6.38 shows the waveform generated on a digital oscilloscope where the set frequen-
cy was 50Hz. Here, the horizontal time scale was 10 ms/division and the vertical voltage
scale was 2 V/division. The measured frequency of the waveform was 49.7Hz.
The operation of a 4-digit multiplexed display (Figure 6.39) is similar to the 2-digit display,
where the LED segments of the digits are tied together and the common pins of each digit
are turned ON separately by the microcontroller. By displaying each digit for several milli-
seconds, the eye can not differentiate that the digits are not ON all the time. This way we
can multiplex any number of 7-segment displays together. For example, to display number
5734, we have to send 5 to the first digit and enable its common pin. After a few millisec-
onds, number 7 is sent to the second digit and the common point of the second digit is
enabled, and so on. When this process is repeated, the user sees as if both displays are
ON continuously.
● 126
The display used in this project is the DC56-11EWA which is a red colour, 0.56 inch height
common-cathode two-digit multiplexed display having 18 pins, where the pin configuration
is shown in Table 6.1. Two such display modules are used to construct a 4 digit display.
Each module has E1 and E2 enable pins.
Block Diagram: Figure 6.40 shows the block diagram of the project.
Enable 1
Enable 2
Enable 3
Enable 4
Circuit Diagram: The circuit diagram of the project is shown in Figure 6.41. In this project,
the following pins of the Raspberry Pi are used to interface with the 7-segment LED display:
7-segment display segments are driven from the port pins through 470 Ohm current limit-
ing resistors. Digit enable pins E1, E2 of the first module and E1, E2 of the second module
are driven from port pins GPIO 11, GPIO 9, GPIO 21, and GPIO 20 respectively through four
BC108 type NPN transistors (any other NPN transistor can be used here), used as switch-
es. The collectors of these transistors drive the segment digits. The segments are enabled
● 127
when the base of the corresponding transistor is set to logic 1. Notice that the following pins
of the display are connected to form a multiplexed display:
Program Listing: Figure 6.42 shows the program listing (program: SevenCount4.py).
At the beginning of the program, the modules are imported. The program is very similar to
the one with 2 digits. Here, the list LED_Digits contains 4 numbers which are the digit en-
able pins of the two 7-segment LED modules. The program consists of two threads. Thread
Refresh is very similar to the one with 2 digits, except that we had to make sure that the
number to be displayed (count) consists of 4 digits. If the number is less than 4 digits,
spaces are inserted in front of it to blank the display for the digit positions. Notice that digit
count runs from 0 to 4 and not from 0 to 2 which was the case with the 2-digit display. The
delay between the digit enables is reduced to 0.5 ms (it was 1 ms with the 2 digit display)
since we have 4 digits and a faster refresh rate is required. Variable count starts from 0 at
the beginning of the program by default. It is incremented by 1 in Thread Up_Count. When
the count reaches 10000, it is reset back to 0.
#-----------------------------------------------------------------
# 4 DIGIT SEVEN SEGMENT LED SECONDS COUNTER
# =========================================
#
# This is a 4 digit 7-segment LED seconds counter program. The
# program counts up every second from 0 to 9999 continuously.
# The LED matrix is refreshed in a thread. The connections between
# the 7-segment LED and the RaspberryPi are as follows:
#
# 7-Segment LED GPIO
# a 2
● 128
# b 3
# c 4
# d 17
# e 27
# f 22
# g 10
# E1 module1 11
# E2 module1 9
# E1 module2 21
# E2 module2 20
#
# Author: Dogan Ibrahim
# File : SevenCount4.py
# Date : May 2020
#-------------------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
#
# Configure the segments and digits as outputs and clear them
#
for seg in LED_Segments:
GPIO.setup(seg, GPIO.OUT) # Segments are outputs
GPIO.output(seg, 0) # Clear all segments
LED_Bits ={
' ':(0,0,0,0,0,0,0), # Blank
'0':(1,1,1,1,1,1,0), # 0
'1':(0,1,1,0,0,0,0), # 1
'2':(1,1,0,1,1,0,1), # 2
'3':(1,1,1,1,0,0,1), # 3
'4':(0,1,1,0,0,1,1), # 4
'5':(1,0,1,1,0,1,1), # 5
'6':(1,0,1,1,1,1,1), # 6
'7':(1,1,1,0,0,0,0), # 7
'8':(1,1,1,1,1,1,1), # 8
● 129
'9':(1,1,1,1,0,1,1)} # 9
#
# Thread to refresh the 7-segment LED
#
def Refresh(): # Thread Refresh
global count
while True: # Do forever
cnt = str(count) # into string
if len(cnt) == 3: # If 3 digits
cnt = " " + cnt # Add " "
elif len(cnt) == 2: # If 2 digits
cnt = " " + cnt # Add " "
elif len(cnt) == 1: # If 1 digit
cnt = " " + cnt # Add " "
for dig in range(4): # Do for 4 digits
for loop in range(0,7): # Do for all segments
GPIO.output(LED_Segments[loop], LED_Bits[cnt[dig]][loop])
GPIO.output(LED_Digits[dig], 1)
time.sleep(0.0005)
GPIO.output(LED_Digits[dig], 0)
#
# Thread to count up evey second
#
def UP_Count(): # Thread UP_Count
global count
while True: # Do forever
time.sleep(1) # Wait a second
count = count + 1 # Increment count
if count == 10000: # If count = 10000
count = 0
#
# Create the threads
#
_thread.start_new_thread(Refresh, ())
_thread.start_new_thread(UP_Count, ())
while True:
pass
● 130
Description: In this project, the objects (e.g bottles) moving on a conveyor belt are count-
ed and displayed on a 7-segment 4-digit multiplexed LED display.
Block Diagram: As shown in Figure 6.43, the project is based on a light-dependent resis-
tor (LDR). A light beam shines from one side of the conveyor belt to the objects moving on
the conveyor belt. An LDR on the other side of the conveyor belt detects when an object
interrupts the light beam. This generates a logic 1 signal which is used to increment a coun-
ter where the total count is displayed on the LED.
The LDR sensor used in this project is known as the KY-018. As shown in Figure 6.44 it
consists of a photoresistor in series with a 10K resistor. Photoresistors, also known as Light
Dependent Resistors (LDR) are light-sensitive 2-pin resistors that can be used to indicate
the presence or absence of light. Their dark resistances are very high (up to 1Mohm), but
when they are exposed to light their resistances can drop down to several ohms depending
on light intensity (Figure 6.45). Modern LDRs are made up of cadmium selenide (CdS),
where a light-sensitive tract is created using CdS (Figure 6.46). The sensitivity of a pho-
toresistor varies with light wavelength. In general, they have lower sensitivities than photo-
diodes or phototransistors. LDRs are also sensitive to temperature changes and because of
this, they are not suitable for precise light intensity measurements. LDRs are slow devices.
It can take up to 10ms for their resistances to change when light intensity changes. For this
reason, LDRs cannot be used in applications where light intensity changes fast. LDRs are
normally used with a series resistor where voltage is applied to the resistor. The voltage
across the LDR is related to the light intensity striking on the LDR.
● 131
Circuit Diagram: When there are no objects in front of the LDR, the light shines directly
on the LDR. It is assumed that under such circumstances the resistance of the LDR is about
1K or less. It is assumed when there is an object in front of the light the resistance of the
LDR will increase to about 100K. Supplying +3.3V to the LDR module, the voltage at the
LDR output will be (see Figure 6.47):
● 132
Therefore, when there is no object in front of the LDR the output logic state can be taken
as 0, and when there is an object in front of the LDR we can assume a logic state of 1. The
rising edge of the output voltage from the LDR can be used to detect the presence of an
object and this voltage can be used as an input to our Raspberry Pi to count the number of
objects passing in front of the LDR.
The display used in this project is the 4 digit 7-segment LED used in Project 9, connected to
the Raspberry Pi as in Project 9. Figure 6.48 shows the circuit diagram of the project. The
output pin of the LDR (pin S) is connected to GPIO 16.
● 133
Program Listing: Figure 6.49 shows the program listing (program: ldr.py). This project is
similar to Project 9. The program consists of two threads. Thread Refresh is the same as in
Project 9. It refreshes the display and shows the count at any time. Thread Object_Count
waits until an object is detected. The output of the LDR (GPIO 16) is normally at logic 0 and
goes to logic 1 when an object is detected (i.e. when an object interrupts the light beam).
At this point, variable count is incremented by one. The thread then waits until the object
is moved away. The above process continues until stopped by the user.
#-----------------------------------------------------------------
# CONVEYOR BELT OBJECT COUNTER
# ============================
#
# This is a conveyor belt object counter project. Object move on a
# conveyor belt. A light shines from one side to the objects. On the
# other side an LDR is used. When an object interrupts the light
# source the LDR output goes to logic 1 which is detected by the
# Raspberry Pi and the count is incremented and displayed on a 4
# digit 7-segment LED..
# The connections between the 7-segment LED and the Raspberry Pi are:
#
# 7-Segment LED GPIO
# a 2
# b 3
# c 4
# d 17
# e 27
# f 22
# g 10
# E1 module1 11
# E2 module1 9
# E1 module2 21
# E2 module2 20
#
# Author: Dogan Ibrahim
# File : ldr.py
# Date : May 2020
#-------------------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
● 134
#
# Configure the segments and digits as outputs and clear them
#
for seg in LED_Segments:
GPIO.setup(seg, GPIO.OUT) # Segments are outputs
GPIO.output(seg, 0) # Clear all segments
LED_Bits ={
' ':(0,0,0,0,0,0,0), # Blank
'0':(1,1,1,1,1,1,0), # 0
'1':(0,1,1,0,0,0,0), # 1
'2':(1,1,0,1,1,0,1), # 2
'3':(1,1,1,1,0,0,1), # 3
'4':(0,1,1,0,0,1,1), # 4
'5':(1,0,1,1,0,1,1), # 5
'6':(1,0,1,1,1,1,1), # 6
'7':(1,1,1,0,0,0,0), # 7
'8':(1,1,1,1,1,1,1), # 8
'9':(1,1,1,1,0,1,1)} # 9
#
# Thread to refresh the 7-segment LED
#
def Refresh(): # Thread Refresh
global count
while True: # Do forever
cnt = str(count) # into string
if len(cnt) == 3: # If 3 digits
cnt = " " + cnt # Add " "
elif len(cnt) == 2: # If 2 digits
cnt = " " + cnt # Add " "
elif len(cnt) == 1: # If 1 digit
cnt = " " + cnt # Add " "
for dig in range(4): # Do for 4 digits
for loop in range(0,7): # Do for all segments
GPIO.output(LED_Segments[loop], LED_Bits[cnt[dig]][loop])
GPIO.output(LED_Digits[dig], 1)
time.sleep(0.0005)
● 135
GPIO.output(LED_Digits[dig], 0)
#
# Thread to count up evey second. Objects are counted on the rising
# edge of the pulse
#
def Object_Count(): # Object LDR_Count
global count
while True: # Do forever
while GPIO.input(ldr) == 0: # Wait for an object
time.sleep(0.2)
count = count + 1 # Increment count
while GPIO.input(ldr) == 1: # Wait until object moved
time.sleep(0.2)
#
# Create the threads
#
_thread.start_new_thread(Refresh, ())
_thread.start_new_thread(Object_Count, ())
while True:
pass
In this project, it is required to control the temperature of an oven using an ON-OFF type
controller. The setpoint temperature is entered by a keyboard on a laptop. This setpoint val-
● 136
ue can be changed at any desired time while the oven is under control. i.e. there is no need
to stop the controller to change the setpoint value (hence multitasking). A relay connected
to the Raspberry Pi turns the heater ON or OFF under the control of software. An LCD shows
the setpoint temperature as well as the actual measured oven temperature. The tempera-
ture of the oven is measured using an accurate digital temperature sensor chip. A buzzer is
used which becomes active if the temperature of the oven goes above a dangerous preset
value to indicate an alarm condition. An LED displays the state of the oven at any time such
that when the heater is ON then the LED is ON, and vice-versa.
Block Diagram: Figure 6.50 shows the block diagram of the project.
Circuit Diagram: In this project, a DS18B20 type temperature sensor chip is used. This
chip is available as a module with the name KY-001 (see Figure 6.51). The module has 3
pins: output (S pin), power supply (middle pin), and ground (- pin).
DS18B20 is a digital temperature sensor chip that provides 9-bit to 12-bit temperature
measurements and has an alarm function with nonvolatile user-programmable trigger
points. The chip communicates over a 1-wire bus. Each DS18B20 has a unique 64-bit serial
code, which allows multiple DS18B20s to be connected to the same bus. The basic features
of the DS18B20 are:
● 137
• 1 wire communication
• Temperature measurement range: -55ºC to +125ºC
• Accuracy: ±0.5ºC
• Programmable resolution (9 or 12 bits)
• No external components are required
• Operating voltage: +3V to +5.5V
• Standby current: 750nA
• Active current: 1mA
In this project, an I2C type LCD is used to display the setpoint as well as the actual tem-
peratures. I2C LCDs are standard LCDs but also have a small add-on circuit mounted on the
back of the module which features a PCF8574 chip for I2C communication (see Figure 6.52),
and a potentiometer to adjust the LED backlight. The I2C LCD has 4 pins: GND, +V, SDA,
and SCL. SDA is connected to pin GPIO 2 and SCL is connected to pin GPIO 3. +V pin of
the display should be connected to the +5V (pin 2) of the Raspberry Pi 4 (some versions of
Raspberry Pi, such as the Raspberry Pi Zero and Raspberry Pi Zero W cannot supply enough
power to drive the LCD. It is recommendable to use an external +5V supply for the LCD if
you are using one of these models). Note there is no problem mixing the +3.3V GPIO pins
of the Raspberry Pi with the +5V of the I2C LCD. This is because the Raspberry Pi is the I2C
master device and the SDA and SCL lines are pulled up to +3.3V through resistors. The SCL
line is the clock that is always output from the master device. The slave device (I2C LCD
here) only pulls down the SDA line when it acknowledges the receipt of data and does not
send any data to the master device. Therefore, there are no voltage level problems as long
as the Raspberry Pi I2C output pins can drive the I2C LCD inputs, which is the case here.
I2C is a multi-slave, multi-master, single-ended serial bus used to attach low-speed pe-
ripheral devices to microcontrollers. The bus consists of only two wires called SDA and SCL
where SDA is the data line and SCL the clock line. Up to 1008 slave devices can be sup-
ported on the bus. Both lines must be pulled up to the supply voltage by suitable resistors.
The clock signal is always generated by the bus master. The devices on the I2C bus can
communicate at 100 kHz or 400 kHz.
A small relay module (e.g. KY-019) is connected to the Raspberry Pi which controls the
heater. Additionally, as shown in the circuit diagram in Figure 6.53, a buzzer is connected
to the Raspberry Pi to indicate any over-temperature alarm conditions. An LED indicates
whether the heater is ON or OFF.
● 138
The connections between the Raspberry Pi and the peripheral devices are as follows:
GPIO 4 S (KY-001)
GPIO 17 Buzzer
● 139
GPIO 27 S (Relay)
GPIO 22 LED
Figure 6.54 shows a simplified block diagram of our oven control system.
Program Listing: Before writing the program we have to install the required library mod-
ules to our Raspberry Pi for the I2C LCD and the DS18B20 temperature sensor chip. These
are described below:
Now we have to install the I2C library on our Raspberry Pi 4. The steps are as follows:
● 140
• Enter the following command to test the installation. You should see i2c_
bcm2837 listed:
• Exit nano by typing Ctrl X and Y to save the file. You can check the contents of
this file by entering the command:
• Connect your I2C LCD to the Raspberry Pi device as shown in Figure 6.53, and
enter the following command to check whether or not the LCD is recognized by
the Raspberry Pi:
You should see a table similar to the one shown in Figure 6.55. A number in the table means
the LCD has been recognised correctly and the I2C slave address of the LCD is shown. In
this example, the LCD address is 27:
● 141
We should now install an I2C LCD library so we can send commands and data to our LCD.
There are many Python libraries available for I2C type LCDs. The one preferred here is
called the RPi_I2C_driver. This library is installed as follows:
https://gist.github.com/DenisFromHR/cc863375a6e19dce359d
• Start the WinSCP file copy utility (you should install it if you don't already have
it) on your PC and copy file RPi_I2C_driver.py to folder usr/lib/python3 on
your Raspberry Pi.
• Check to make sure that the file is copied successfully. You should see the file
listed with the command:
pi@raspberrypi:~ $ ls /usr/lib/python3
pi@raspberrypi:~ $ dist-packages RPi_I2C_driver.py
The I2C LCD library supports the following functions (see the I2C LCD library documentation
for more details):
● 142
Program Listing: Figure 6.56 shows the program listing (program: onoff.py). The LCD
library and the 1 wire library are imported at the beginning of the program in addition to
the other libraries used in the program. Buzzer, LED, and Relay are set to 17, 22, and 27
respectively which correspond to the GPIO port pin numbers. These ports are then config-
ured as outputs. The program consists of two threads: DS18B20 and Set_Temperature.
Thread Set_Temperature prompts the user to enter the required setpoint temperature
and this is stored in a variable called setpoint. Thread DS18B20 reads the ambient tem-
perature from the DS18B20 chip every second and stores it in the temperature variable.
If temperature is greater than the setpoint value, then the heater is turned OFF and at
the same time, the LED is also turned OFF to indicate the heater is OFF. Otherwise (if the
temperature is less than the setpoint value), both heater and LED are turned ON. The
temperature alarm value is set to 30ºC at the beginning of the program. If the temperature
reaches this value, the buzzer is activated to indicate the alarm condition, otherwise, the
buzzer is deactivated.
When the program is started, the message ON-OFF CONTROLLER is displayed on the LCD
for 2 seconds. After this time the setpoint temperature value and measured temperature
are displayed on the LCD every second.
#-----------------------------------------------------------------
# ON-OFF TEMPERATURE CONTROLLER
# =============================
#
# This is an on-off temperatrue control project which controls
# the temperature of an oven (or room). A digital temperature
# sensor measures the temperature. If this temperatrue is lower
# then the required setpoint temperature then a heater is activated
# and at the same time an LED is turned ON to indicate that the
# heater is ON. If the temperature is above a preset alarm value
# then a buzzer is activated
#
# Author: Dogan Ibrahim
# File : onoff.py
# Date : May 2020
● 143
#-------------------------------------------------------------------
import RPi.GPIO as GPIO
import _thread
import time
from w1thermsensor import W1ThermSensor # 1 wire library
import RPi_I2C_driver # I2C library
LCD = RPi_I2C_driver.lcd()
sensor = W1ThermSensor()
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
#
# Thread to read the temperature
#
def DS18B20(): # Thread DS18B20
global setpoint
global temperature
while True: # Do forever
temperature = str(sensor.get_temperature())[0:4]
if float(temperature) > float(setpoint):
GPIO.output(Relay, 0)
GPIO.output(LED, 0)
else:
GPIO.output(Relay, 1) # Relay ON
GPIO.output(LED, 1) # LED ON
#
# Thread to set the required temperature
● 144
#
def Set_Temperature(): # Thread Set_temperature
global setpoint
print("ON-OFF TEMPERATURE CONTROLLER") # Heading
print("=============================")
while True: # Do forever
setpoint = str(input("Enter the setpoint temperature: "))
#
# Create the threads
#
_thread.start_new_thread(DS18B20, ())
_thread.start_new_thread(Set_Temperature, ())
#
# Display the setpoint and the measured temperatures
#
LCD.lcd_clear() # Clear LCD
LCD.lcd_display_string("ON-OFF CONTROL", 1) # Heading
time.sleep(2) # Wait 2 secs
LCD.lcd_clear() # Clear LCD
6.5 Summary
In this chapter, we learned how to develop multitasking projects using threads. In the
next chapter, we will look at how the threading library can be used to create multitasking
projects.
● 145
7.1 Overview
In the last chapter, we looked at how to create projects using threads in Python. In this
chapter, we will be using threading for creating multitasking projects.
Python supports two types of thread modules: the thread as described in Chapter 6, and
threading which will be described in this chapter. Threading is a higher-level interface and
uses the standard thread module internally. Threading has been supported since Python
2.4 and provides higher-level support.
7.2 Threading
Threading uses all methods of the thread module and provides support for the following
additional methods:
● 146
#-------------------------------------------------------------
# THREADING EXAMPLE
# =================
#
# This program creates a new thread called newprocess which
# displays message Hello from new process... When the trhead
# terminates the message Hello from the creator... is displayed
#
# Author: Dogan Ibrahim
# File : hellothreading.py
# Date : May 2020
#---------------------------------------------------------------
import threading
def newprocess():
print("Hello from new process...")
We can give a name to a thread. For example, let us name the thread created by function
newprocess in Figure 7.1 TASK1 and then display the name of this thread as shown in
the program in Figure 7.2 (program: namethread.py).
#-------------------------------------------------------------
# THREADING EXAMPLE
# =================
#
# This program shows how a thread can be named
#
# Author: Dogan Ibrahim
# File : namethread.py
# Date : May 2020
#---------------------------------------------------------------
import threading
● 147
def newprocess():
thread.setName("Task1")
name = thread.getName()
print(name)
print("Hello from new process...")
#-------------------------------------------------------------
# MULTIPLE THREAD EXAMPLE
# =======================
#
# This program shows how multiple threads can be created
#
# Author: Dogan Ibrahim
# File : multiple.py
# Date : May 2020
#---------------------------------------------------------------
import threading
def newprocess(j):
print("Hello from new process %d" %j)
for i in range(5):
t = threading.Thread(target = newprocess, args=(i, ))
t.start()
● 148
Example
Figure 7.4 shows an example program (program: locks.py). In this example, a shared
variable called count is used. Thread Task1 locks the shared variable count and releases
it after incrementing by 1. Task2 locks the shared variable count and releases it after in-
crementing by 2.
#-------------------------------------------------------------
# EXAMPLE THREAD LOCK OBJECTS
# ===========================
#
# This program shows how thread objects can be used
#
# Author: Dogan Ibrahim
# File : locks.py
# Date : May 2020
#---------------------------------------------------------------
import threading
lock = threading.Lock()
count = 0
def Task1():
global count
lock.acquire() # Lock count
● 149
def Task2():
global count
lock.acquire() # Lock count
count = count + 2 # Increment count
lock.release() # Release count
7.2.2 Semaphores
A semaphore is an advanced lock mechanism. It is used to limit access to a resource with
limited capacity. A semaphore has an internal counter which is decremented when the
semaphore is acquired and incremented when the semaphore is released. If the counter
reaches zero when acquired, the acquiring thread will block. When the semaphore is incre-
mented again, one of the blocking threads will run.
The semaphore counter must be initialized before used. If not initialized, a count of 1 is
assumed by default. For example, to set the counter to 5, use the statements:
max = 5
semaphore = threading.BoundedSemaphore(max)
Example
Figure 7.5 shows an example program (program: sema.py). In this example, the sema-
phore count is set to 1 and after acquiring the semaphore numbers 0 to 4 are displayed by
Task1. After the semaphore is released, numbers 0 to 4 are displayed by Task2.
#-------------------------------------------------------------
# EXAMPLE SEMAPHORE
# =================
#
# This program shows how a semaphore can be used
#
● 150
def Task1():
sema.acquire() # acuire semaphore
for i in range(4):
print(i)
sema.release() # release semaphore
def Task2():
sema.acquire() # acquire semaphore
for i in range(4):
print(i)
sema.release() # release semaphore
7.2.3 Events
Events are simple synchronization objects using internal flags. Threads can wait for the flag
to be set, or set or clear the flags.
● 151
Example
Figure 7.6 shows an example program (program: event.py). In this example, thread Task1
waits until the event flag is set. Thread Task2 prompts the user to enter a key. After enter-
ing a key, the event flag is set and Task1 displays a message Event occurred…
#-------------------------------------------------------------
# EXAMPLE EVENT
# =============
#
# This program shows how an event can be used
#
# Author: Dogan Ibrahim
# File : event.py
# Date : May 2020
#---------------------------------------------------------------
import threading
event = threading.Event()
def Task1():
event.wait() # Wait for the event
print("Event occurred...")
def Task2():
a = input("Enter a key to set the event:")
event.set()
● 152
#-------------------------------------------------------------
# TIMER THREAD EXAMPLE
# ====================
#
# This program shows how a timer thread can be used
#
# Author: Dogan Ibrahim
# File : tmrthread.py
# Date : May 2020
#---------------------------------------------------------------
import threading
import time
def TimerThread():
print("TimerThread started.")
● 153
Program Listing: Figure 7.8 shows the program listing (program: flashcount.py). At
the beginning of the program, an event flag is created with the name event. The LED is
assigned to 22 which corresponds to the GPIO port number where it is connected to. This
port is then configured as an output.
There are two threads in the program. Thread Flash flashes the LED every second. Just
before turning the LED ON, the event flag is set, Thread Count waits for the event flag to
be set, then clears the event flag, and increments the count variable by 1. The total count
is then displayed on the screen.
#-------------------------------------------------------------
# EXAMPLE SEMAPHORE
# =================
#
# This program shows how a semaphore can be used
#
# Author: Dogan Ibrahim
# File : flashcount.py
# Date : May 2020
#---------------------------------------------------------------
import threading
import time
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
event = threading.Event()
● 154
The following output will be displayed when the program is run (only part of the output is
shown):
7.4 Summary
In this chapter, we learned how to develop multitasking projects using the threading mod-
ule of Python on a Raspberry Pi. In the next chapter, we will use subprocesses to create
multitasking applications.
● 155
8.1 Overview
Subprocesses were introduced to replace the following various old modules (functions)
present in Python:
• os.system
• os.spawn
• os.popen
• commands
Subprocesses are used to run independent programs (or commands). Details of how to use
subprocesses are given in the following sections.
● 156
import subprocess
subprocess.call(["python3", "hello.py"])
● 157
Notice that subprocess calls are blocking, but we can use Popen to execute a child process
so that the parent process is not blocked. An example project is given below.
Description: This project is the same as Project 1 in Chapter 5.3.1 where two LEDs are
connected to the Raspberry Pi. One of the LEDs flashes every second while the other one
flashes every 250 milliseconds.
Block Diagram: The block diagram of the project is the same as in Figure 5.1.
Circuit Diagram: The circuit diagram of the project is the same as in Figure 5.2. The
LEDs are connected to Raspberry Pi GPIO 2 and GPIO 3 through 470 Ohm current limiting
resistors.
Program Listing: Figure 8.1 shows the parent program listing (program: ledsubprocess.
py). This program activates two child programs called flash1000.py and flash250.py.
Program flash1000.py (Figure 8.2) flashes the LED connected to GPIO 2 every second.
Similarly, program flash250.py (Figure 8.3) flashes the LED connected to GPIO 3 every
250 milliseconds.
#-------------------------------------------------------------
# START PROGRAMS TO FLASH 2 LEDs
# ==============================
#
# This program starts programs flash1000.py and flash250.py
# to flash two LEDs at the rates 1 second and 250ms
#
# Author: Dogan Ibrahim
# File : ledsubprocess.py
# Date : May 2020
#---------------------------------------------------------------
import subprocess
print("Start flash1000.py")
subprocess.Popen(["python3", "flash1000.py"])
print("Start flash250.py")
subprocess.Popen(["python3", "flash250.py"])
print("Parent exited")
● 158
#----------------------------------------------------------
# FLASH LED EVERY SECOND
# ======================
#
# THis program flashes the LED at GPIO 2 every second
#
# Author : Dogan Ibrahim
# File : flash1000.py
# Date : May 2020
#----------------------------------------------------------#
import RPi.GPIO as GPIO # Import RPi
import time
GPIO.setwarnings(False) # Disable warnings
#----------------------------------------------------------
# FLASH LED EVERY 250ms
# =====================
#
# This program flashes the LED every 250ms
#
# Author : Dogan Ibrahim
# File : flash250.py
# Date : May 2020
#----------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import time
GPIO.setwarnings(False) # Disable warnings
● 159
Notice that the two programs flash1000.py and flash250.py will run independently of the
parent program.
8.9 Summary
In this chapter, we have looked briefly at the subprocesses and learned how they can be
used in projects. In the next chapter, we will look in detail at how to use multi programs in
Raspberry Pi Python projects.
● 160
9.1 Overview
Multiprocessing (also called parallel processing) is the method of using more than one
processor (e.g. CPU) by an application. Multiprocessing is highly suitable for heavyweight
tasks such as CPU bound tasks. Python multiprocessing module provides a powerful API
to develop multiprogramming applications. Processors such as the Raspberry Pi include
several cores in their CPUs and as a result creating multiprocessing applications on such
systems are highly efficient.
In a multiprocessing application, all processes are independent of each other and have their
share of the overall system resources, such as memory, processing power, etc. Processes
in a multiprogramming application can share memory and communicate with each other
using functions provided in the multiprocessing API.
• The processes in a multiprocessing system have their own CPU and memory
spaces which are unique to each process. Threading applications, on the other
hand, utilise the same CPU and memory present in the parent process
• Sharing objects (e.g. data) in a thread-based system is very easy because such
objects are global to all the threads in the system. Sharing data in a multi-
processing system however is more difficult because special synchronisation and
software functions are required to share objects between different processes.
● 161
#----------------------------------------------------------
# SIMPLE MULTIPROCESSING EXAMPLE
# ==============================
#
# This program creates a process which displays a message
#
# Author : Dogan Ibrahim
# File : multiproc.py
# Date : May 2020
#----------------------------------------------------------
from multiprocessing import Process
def NewProcess():
print("Hello from the new process...")
Notice the program in Figure 9.1 could have been written by importing the whole multiproc-
essing module as shown in the program in Figure 9.3 (program: multiproc2.py).
● 162
#----------------------------------------------------------
# SIMPLE MULTIPROCESSING EXAMPLE
# ==============================
#
# This program creates a process which displays a message.
# In this version of the program whole multiprocessing
# module is imported to the program
#
# Author : Dogan Ibrahim
# File : multiproc2.py
# Date : May 2020
#----------------------------------------------------------
import multiprocessing
def NewProcess():
print("Hello from the new process...")
condition.acquire(): obtain an internal lock. The process is blocked until the lock
is available
condition.notify(): wake up one of the processes waiting (if there is one waiting)
condition.notifyAll(): wake up all waiting processes
condition.Release(): release the internal lock
condition.wait(): release the lock and then block until awakened by notify()
● 163
Queues must be created before they are used. Function put(data) puts data into the
queue. Function get() gets data from the queue.
Block Diagram: The block diagram of the project is the same as in Figure 5.1.
● 164
Circuit Diagram: The circuit diagram of the project is the same as in Figure 5.2. The
LEDs are connected to Raspberry Pi GPIO 2 and GPIO 3 through 470 Ohm current limiting
resistors.
Program Listing: Figure 9.4 shows the program listing (program: ledmulti.py). After
importing the modules used in the program, LED1 and LED2 are assigned to 2 and 3 which
correspond to GPIO 2 and GPIO 3. Process Flash1000 flashes LED1 every second, while
process Flash250 flashes LED2 every 250 milliseconds. Notice that processes Flash1000
and Flash250 are given the names Flash1000 and Flash250 respectively when they are
created. Process Flash250 displays its name and process ID as soon as it runs. The two
processes are started with function calls start().
#----------------------------------------------------------
# FLASH 2 LEDs AT DIFEFRENT RATES
# ===============================
#
# Two LEDs are connected to the Raspberry Pi. One of the LEDs
# is flashed every second, while the other one every 250m
#
# Author : Dogan Ibrahim
# File : ledmulti.py
# Date : May 2020
#----------------------------------------------------------#
import multiprocessing
import os
import RPi.GPIO as GPIO # Import RPi
import time
GPIO.setwarnings(False) # Disable warnings
● 165
9.12.2 Project 2 – Setting the LED flashing rate from the keyboard
Description: This project is the same as Project 3 in section 5.3.3 where an LED is con-
nected to the Raspberry Pi and its flashing rate is set from the keyboard while the LED is
flashing.
Circuit Diagram: The circuit diagram of the project is similar to Figure 5.2, but here only
one LED is used and it is connected to Raspberry Pi port GPIO 2.
Program Listing: Figure 9.6 shows the program listing (program: ledkey.py). At the
beginning of the program, a Queue with the name q is created. The LED is assigned to
GPIO 2 and this port is configured as an output. The flashing rate is set to 1 second by
sending 1 to the queue using function put(1). Process Flash checks whether the queue
is empty and if not the new flashing rate is read from the queue using function get(). The
LED then flashes at this rate. The main program creates process Flash and starts it. The
input function is then used to read the required flashing rate from the keyboard which is
then sent to the queue.
#----------------------------------------------------------
# CHANGE LED FLASHING RATE FROM THE KEYBOARD
# ==========================================
#
# In this program an LED is connected to the Raspberry Pi and
# its flashing rate is changed from the keyboard
#
# Author : Dogan Ibrahim
# File : ledkey.py
● 166
#
# Start process Flash
#
p = multiprocessing.Process(target = Flash, args = ())
p.start()
#
# Input the flashing rate
#
while True:
Flash_Rate = float(input("Enter flashing rate: "))
q.put(Flash_Rate)
Description: This is the same as Project 11 given in section 6.4.11, but designed using
processes instead of threads.
As in Project 11, in this project, it is required to control the temperature of an oven using
ON-OFF type controller. The setpoint temperature is entered through the keyboard of a lap-
top. This setpoint value can be changed at any desired time while the oven is under control.
● 167
i.e. there is no need to stop the controller to change the setpoint value (hence multitask-
ing). A relay connected to the Raspberry Pi turns the heater ON or OFF under the control
of software. An LCD displays the setpoint temperature as well as the actual measured oven
temperature. The temperature of the oven is measured using an accurate digital tempera-
ture sensor chip. A buzzer is used which becomes active if the temperature of the oven goes
above a dangerous preset value to indicate an alarm condition. An LED displays the state of
the oven at any time such that when the heater is ON then the LED is ON, and vice-versa.
Circuit Diagram: The circuit diagram of the project is as in Figure 6.53, where the tem-
perature is read using a DS18B20 type temperature sensor chip.
Program Listing: Figure 9.7 shows the program listing (program: onoffproc.py). The
RPi, multiprocessing, time, one wire, and the I2C LCD libraries are imported at the be-
ginning of the program. Two queues are then created: one for the temperature (called
qtemperature) and one for the setpoint (called qsetpoint). The Buzzer, LED and Relay
are assigned to 17,22, and 27 respectively to correspond to the GPIO numbers. These ports
are then configured as outputs. The setpoint, temperature, and alarm value are set to 20,
20, and 30 respectively at the beginning of the program.
Process Dislay_Temperature clears the display and displays heading ON-OFF CONTROL
in the first row of the LCD. After 2 seconds the display is cleared. The setpoint and the
measured temperature values are read from the corresponding queues and stored in local
variables called temperature and setpoint respectively. The process then compares the
measured value with the setpoint value and if the measured value is greater than the set-
point value, the Relay and the LED are both turned OFF. If on the other hand, the measured
value is less than the setpoint value then both the Relay and the LED are turned ON. The
process also compares the measured temperature with the alarm value and if the meas-
ured value is greater than the preset alarm value then the Buzzer is activated.
The main program displays the message Enter the setpoint temperature: and prompts
the user to enter the required setpoint value. The value entered is read and put into queue
qsetpoint.
● 168
#-----------------------------------------------------------------------
# ON-OFF TEMPERATURE CONTROLLER
# =============================
#
# This is an on-off temperature control project which controls
# the temperature of an oven (or room). A digital temperature
# sensor measures the temperature. If this temperatrue is lower
# then the required setpoint temperature then a heater is activated
# and at the same time an LED is turned ON to indicate that the
# heater is ON. If the temperature is above a preset alarm value
# then a buzzer is activated
#
# Author: Dogan Ibrahim
# File : onoffproc.py
# Date : May 2020
#------------------------------------------------------------------------
import RPi.GPIO as GPIO # RPi library
import multiprocessing # multiprocessing
import time # time
from w1thermsensor import W1ThermSensor # 1 wire library
import RPi_I2C_driver # I2C library
LCD = RPi_I2C_driver.lcd()
sensor = W1ThermSensor()
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
#
# Process to read the temperature and send to temperature queue
#
def DS18B20(): # Process DS18B20
while True: # Do forever
● 169
temperature = str(sensor.get_temperature())[0:4]
if qtemperature.empty():
qtemperature.put(temperature) # Send to queue
time.sleep(1) # 1 second delay
#
# Process to display the setpoint and measured temperatures and to
# control the Relay, LED and Buzzer
#
def Display_Temperature(): # Process Display_Temperature
LCD.lcd_clear() # Clear LCD
LCD.lcd_display_string("ON-OFF CONTROL", 1)
time.sleep(2) # Wait 2 seconds
LCD.lcd_clear() # Clear LCD
#
# Create the processes
#
p = multiprocessing.Process(target = DS18B20, args = ())
q = multiprocessing.Process(target = Display_Temperature, args = ())
● 170
p.start()
q.start()
#
# Read the setpoint value from the keyboard
#
print("ON-OFF TEMPERATURE CONTROLLER")
print("=============================")
while True:
setpoint = str(input("Enter the setpoint temperature:" ))
qsetpoint.put(setpoint)
This project is about the design of a metronome using the Raspberry Pi in a multitasking
environment. The default timing of the metronome is set to 120 bpm. Two buttons, named
UP and DOWN are used to change the bpm. Pressing UP increments the bpm by 10. Sim-
ilarly, pressing DOWN decrements the bpm by 10. The bpm setting is displayed on an I2C
LCD. An active buzzer is used to generate pulsing sounds to indicate the timings (you may
like to use an amplifier to increase the sound level of the buzzer or use a small speaker
with an audio amplifier).
Block Diagram: The block diagram of the project is shown in Figure 9.8.
● 171
Circuit Diagram: The circuit diagram of the project is shown in Figure 9.9. Buttons UP and
DOWN are connected to GPIO 22 and GPIO 27 respectively. The active buzzer is connected
to GPIO 17. The SDA and SCL pins of the I2C LCD are connected to GPIO 2 and GPIO 3
respectively.
Program Listing: Figure 9.10 shows the program listing (program: metronome.py).
At the beginning of the program modules RPi, multiprocessing, time, and I2C LCD library
modules are included in the program. Two queues with the names q1 and q2 are created.
Buzzer, UP and DOWN are assigned to 17, 22 and 27 to correspond to the GPIO port
names. Buzzer is configured as an output, and UP and DOWN buttons are configured as
inputs. There are 3 processes in the program: UP_Button, DOWN_Button, and Acti-
vate_Buzzer.
Process UP_Button increments the bpm by 10 every time the UP button is pressed. The
default starting value of the bpm is set to 120. After changing the bpm it is sent to both
queues q1 and q2. Queue q1 is used by processes UP_Button and DOWN_Button and
also by the main program which displays the current value of the selected bpm. Queue
q2 on the other hand is used by process Activate_Buzzer. It was necessary to use two
queues in this program because processes UP_Button and DOWN_Button change the
bpm and send it to queue q1. But this value is taken from the queue by the display program
and therefore nothing is left in the queue for the process Activate_Buzzer to get. By using
another queue for Activate_Buzzer the problem has been solved.
Process DOWN_Button decrements the bpm by 10 every time the DOWN button is
pressed. If the bpm becomes less than 10 then it is set to 10. As in process UP_Button,
both queues q1 and q2 are used in this process.
● 172
Process Activate_Buzzer activates the buzzer so that the metronome pulses can be
heard. The pulse width of the metronome pulses is set to a low value. i.e. to 0.0625 second
(62.5ms). Therefore, the buzzer ON time is 0.0625 seconds. The buzzer OFF time is chosen
such that the required bpm is achieved. The required overall time delay is stored in variable
dly which is given by:
dly = 60 / bpm
The buzzer OFF time is then set to dly – 0.0625 seconds. For example, if the bpm is se-
lected as 120, then dly = 60 / 120 = 0.5 second. The buzzer ON time is then 0.0625
second, and the OFF time is 0.5 – 0.0625 = 0.4375 second, in total making 0.5 seconds
which corresponds to 120 bpm.
The LCD displays the selected bpm and is updated every second in the main part of the
program.
#-----------------------------------------------------------------------
# METRONOME
# =========
#
# This is a metronome project. A buzzer is connected to the Raspberry Pi
# to sound the timing pulses. The timings are set by two buttons and are
# displayed on an I2C LCD. The default bpm is set to 120.
#
# Author: Dogan Ibrahim
# File : metronome.py
# Date : May 2020
#------------------------------------------------------------------------
import RPi.GPIO as GPIO # RPi library
import multiprocessing # multiprocessing
import time # time
import RPi_I2C_driver # I2C library
LCD = RPi_I2C_driver.lcd()
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
● 173
#
# Process to increment bpm
#
def UP_Button(): # Process UP_Button
bpm = 120
while True: # Do forever
while GPIO.input(UP) == 1: # UP not pressed
pass
while not q1.empty(): # If not empty
bpm = q1.get()
bpm = bpm + 10 # Increment bpm
q1.put(bpm) # Send bpm to queue
q2.put(bpm) #
while GPIO.input(UP) == 0: # UP not released
pass
#
# Process to decrement bpm
#
def DOWN_Button(): # Process DOWN_Button
bpm = 120
while True: # Do forever
while GPIO.input(DOWN) == 1: # DOWN not pressed
pass
while not q1.empty(): # If not empty
bpm = q1.get()
bpm = bpm - 10 # Decrement bpm
if bpm < 10: # If < 10
bpm = 10
q1.put(bpm) # Send bpm to queue
q2.put(bpm)
while GPIO.input(DOWN) == 0: # DOWN not released
pass
● 174
#
# Create the processes
#
p = multiprocessing.Process(target = UP_Button, args = ())
r = multiprocessing.Process(target = DOWN_Button, args = ())
s = multiprocessing.Process(target = Activate_Buzzer, args = ())
p.start()
r.start()
s.start()
#
# Clear the LCD and display the bpm
#
LCD.lcd_clear()
while True:
while not q1.empty():
bpm = q1.get()
q1.put(bpm)
firstline = "bpm = " + str(bpm) + " "
LCD.lcd_display_string(firstline, 1)
time.sleep(1)
Description: In this project, a simple traffic light controller is designed for a junction. The
junction is located at the intersection of two roads: East Street and North Street. There
are traffic lights at each end of the junction. There are pedestrian buttons located near the
traffic lights on North Street. Pressing a pedestrian button turns all lights to red at the end
of their cycles. A buzzer is then sounded to indicate that the pedestrians can cross the road
safely. Also, an LCD is connected to the system to display whether the pedestrian cycle is
running or the traffic cycle is running. Figure 9.12 shows the layout of the equipment at
the junction.
● 175
In this project, the following fixed times are given to each traffic light duration, and also
to the duration of the pedestrian buzzer. For simplicity, both roads of the junction are as-
sumed to have the same timings:
The total cycle time of the lights in this example project is set to be 38 seconds.
The sequence of traffic lights is assumed to be as follows (different countries may have
different sequences):
Red Green
Amber+Red Amber
Green Red
Amber Amber+Red
● 176
Block Diagram: Figure 9.13 shows the block diagram of the project.
Circuit Diagram: The circuit diagram of the project is shown in Figure 9.14. Red (R),
Amber (A), and Green (G) LEDs are used in this project to represent the real traffic lights.
The following connections are made between the Raspberry Pi and road traffic equipment:
Raspberry Pi Equipment
GPIO 21 LED R1
GPIO 20 LED A1
GPIO 16 LED G1
GPIO 12 LED R2
GPIO 7 LED A2
GPIO 8 LED G2
GPIO 25 Buzzer
● 177
Program Listing: Figure 9.15 shows the program listing (program: Traffic.py). At the
beginning of the program modules RPi, time, I2C LCD driver, and multiprocessing are im-
ported to the program and two queues named pedq and lcdq are created.
Two functions are defined in the program with the names ONOF and CONF_OUT. Function
ONOF has two arguments: port, and state. This function sends the state (0 or 1) to the
specified GPIO port. Function CONF_OUT has one parameter called port. This function
configures the specified GPIO port to output state.
There are two processes in the program: Lights, and Pedestrian. At the beginning of
Lights process, the connections between the Raspberry Pi and the LEDs (traffic lights) and
the Buzzer are defined and these ports are configured as outputs. Additionally, all these
port outputs are cleared to 0 so that all LEDs and Buzzer are set OFF. Additionally, the LEDs
are sequenced in the correct order with the correct timings. Towards the end of the func-
tion, it is checked as to whether the pedestrian button has been pressed. The pedestrian
button is pressed if queue pedq is not empty. During the pedestrian cycle, the red lights
are turned ON on both streets to stop the traffic flowing and give way to the pedestrians.
Also, the Buzzer is activated for 10 seconds during the pedestrian cycle to inform the pe-
destrians that it is safe to cross the road.
The Pedestrian process continuously monitors button PB1. If the button is pressed, then
a 1 is sent to queue pedq so that process Lights can easily detect this action and start the
pedestrian cycle.
● 178
The main program controls the LCD. When the program is started, the message TRAFFIC
CONTROL is displayed on the first row of the LCD. The second row of the LCD continuously
checks queue lcdq and displays either Ped Cycle or Traffic Cycle.
#--------------------------------------------------------------------
# TRAFFIC LIGHTS CONTROLLER
# =========================
#
# This is a traffic lights controller project controlling lights
# at a junction. 6 LEDS are used to represent the traffic lights.
# Additionally a button is used for pedestrian crossing, and an
# LCD shows the state of the traffic lights at any time
#
# Author: Dogan Ibrahim
# File : traffic.py
# Date : May 2020
#----------------------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import multiprocessing # Import multiprocessing
import time # Import time
import RPi_I2C_driver # I2C library
LCD = RPi_I2C_driver.lcd() # Import LCD
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
pedq = multiprocessing.Queue() # Create queue
lcdq = multiprocessing.Queue() # Creaet queue
#
# This function sends data 'state (0 or 1)' to specified port
#
def ONOF(port, state):
GPIO.output(port, state)
#
# This function configures the specified port as output
#
def CONF_OUT(port):
GPIO.setup(port, GPIO.OUT)
#
# Process to control the lights
#
def Lights(): # Process Lights
R1=21; A1=20; G1=16 # LED connections
R2=12; A2=7; G2=8 # LED conenctions
Buzzer=25 # Buzzer connection
● 179
RedDuration = 15
GreenDuration = 15
AmberDuration = 2
#
# Control the traffic light sequence
#
while True: # Do forever
ONOF(R1,0); ONOF(A1,0); ONOF(G1,1); ONOF(R2,1); ONOF(A2,0); ONOF(G2,0)
time.sleep(RedDuration)
ONOF(G1,0); ONOF(A1,1)
time.sleep(AmberDuration)
ONOF(A1,0); ONOF(R1,1); ONOF(A2,1)
time.sleep(AmberDuration)
ONOF(A2,0); ONOF(R2,0); ONOF(G2,1)
time.sleep(GreenDuration)
ONOF(G2,0); ONOF(A2,1)
time.sleep(AmberDuration)
ONOF(A2,0); ONOF(A1,1); ONOF(R2,1)
time.sleep(AmberDuration)
● 180
#
# Create the processes
#
p = multiprocessing.Process(target = Lights, args = ())
q = multiprocessing.Process(target = Pedestrian, args = ())
p.start()
q.start()
#
# LCD Display control. Display 'Ped Cycle' or 'Traffic Cycle'
#
LCD.lcd_clear() # Clear LCD
LCD.lcd_display_string("TRAFFIC CONTROL", 1) # Heading
Description: In this project, an ultrasonic sensor module is used to measure the distance
to objects while parking a vehicle. The sensor is assumed to be mounted on the rear of
the vehicle. This is a multitasking project where the buzzer sounds to indicate the distance
to any object behind the vehicle such that the activation rate of the buzzer increases as
the vehicle gets closer to an object. i.e. the detection of the distance to the object and the
buzzer activation are different processes.
● 181
Block Diagram: Figure 9.17 shows the block diagram of the project.
Circuit Diagram: The circuit diagram of the project is shown in Figure 9.18. In this pro-
ject, the popular HC-SR04 ultrasonic module is used (see Figure 9.19). This module has
the following specifications:
Vcc: +V power
Trig: Trigger input
Echo: Echo output
Gnd: Power ground
● 182
Therefore,
Figure 9.20 shows the principle of operation of the ultrasonic sensor module. For example,
if the time to receive the echo is 0.3 milliseconds, the distance to the object is calculated as:
● 183
The output of the ultrasonic sensor is +5V and is therefore not compatible with the inputs of
the Raspberry Pi. A resistive potential divider circuit is used to lower the voltage to +3.3V.
The voltage at the output of the potential divider resistor is:
Program Listing: Figure 9.21 shows the program listing (program: park.py). At the be-
ginning of the program, various modules are imported to the program. Trigger and echo
pins of the ultrasonic module are set to 20 and 21 corresponding to pin numbers GPIO 20
and GPIO 21 respectively. Trigger and echo pins are configured as output and input respec-
tively. Two processes are created in the program with the names Measure_Distance and
Activate_Buzzer.
Process Activate_Buzzer reads the distance to the obstacle from queue distq. Then,
sounds with different pulsing rates are generated depending on the distance to the obstacle
as in the following code, where d is the distance to the obstacle:
If d < 10:
BuzzerSound(50)
elif d < 20:
BuzzerSound(150)
elif d < 30:
BuzzerSound(250)
elif d < 40:
BuzzerSound(350)
elif d < 50:
BuzzerSound(450)
elif d < 70:
BuzzerSound(700)
elif d < 90:
BuzzerSound(900)
● 184
Function BuzzerSound has one argument: the ON and OFF times of the Buzzer in millisec-
onds. For example, if the distance to the obstacle is less than 10cm, the Buzzer ON and OFF
times are set 50ms. Similarly, if the distance to the obstacle is less than 20cm, the Buzzer
ON and OFF times are set to 150ms and so on.
#--------------------------------------------------------------------
# ULTRASONIC CAR PARKING AID WITH BUZZER
# ======================================
#
# This is an ultrasonic car parking aid project. It is assumed that an
# ultrasonic module is mounted at the rear (or in-front) of a vehicle.
# The program measures the distance to obstacles and sound a buzzer.
# The pulse rate of the buzzer increases as the vehicle gets closer to
# the object.
#
# Author: Dogan Ibrahim
# File : park.py
# Date : May 2020
#----------------------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import multiprocessing # Import multiprocessing
import time # Import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCMe
distq = multiprocessing.Queue() # Create queue
#
# Process Measure_Distance
#
def Measure_Distance(): # Process Measure_Distance
div = 34300 / 2
● 185
#
# Generate sound at different rates with the buzzer
#
def BuzzerSound(F):
Buzzer = 16 # Buzzer at GPIO 16
ms = F / 1000 # In milliseconds
GPIO.setup(Buzzer, GPIO.OUT) # Buzzer is output
GPIO.output(Buzzer, 1) # Buzzer ON
time.sleep(ms) # Wait ms
GPIO.output(Buzzer, 0) # Buzzer OFF
time.sleep(ms) # Wait ms
● 186
Notice the size of a Queue can be specified as an argument when the queue is created.
Queues have the following methods:
put(obj[,block[,timeout]]): put obj into the queue. If option block is True (de-
fault) and timeout is Node (default), the queue will block until a free slot is availa-
ble. If timeout is a positive number, the queue will block at most timeout seconds
and raise the queue.Full exception if no free slot was available within that time
put_nowait(obj): put obj into queue (same as above when block is False)
get_no_wait(): remove and return an item from the queue (same as above
when block is False)
● 187
Sometimes we may want to check whether the queue is full or empty, or to detect if an
error occurs when we want to put items to a full queue, or to read items from an empty
queue. The following exception can be used to check when the queue is full and take the
required actions:
try:
q.put(item) # Put item into queue
except q.Full:
# code to take action if the queue is full # Queue is full
or,
try:
d = q.get() # Get item from queue
except q.Empty:
# code to take action if the queue is empty # Queue is empty
Description: This is a reaction timer project which makes use of an LED and a push-button
switch. The user is expected to press the push-button switch as soon as the LED is turned
ON. The time between the LED being turned ON and the user pressing the button is meas-
ured and displayed on an LCD in seconds. The LED is turned ON again after a random delay,
ready for the next measurement.
Block Diagram: Figure 9.22 shows the block diagram of the project.
Circuit Diagram: The circuit diagram of the project is shown in Figure 9.23. The LED and
the button are connected to GPIO 21 and GPIO 20 respectively. The I2C LCD is connected
to GPIO 2 and GPIO 3 SA and SCL pins of the Raspberry Pi as in the previous LCD projects.
● 188
Program Listing: The program listing (program: reaction.py) is shown in Figure 9.24. At
the beginning of the program, the modules used in the program are imported. Notice mod-
ule random is used to generate random numbers which are then used to generate random
delays. Two events are created in the program with names e and t. Button (PB) is assigned
number 20 and this port is configured as input.
The program consists of a process called LED_ON. This process configures the LED port as
output and turns OFF the LED. The remainder of this process is executed in an endless loop.
The LCD is controlled in the main program. Here the program waits until the LED is turned
ON and then starts a timer. When the button is pressed, the timer reading is read and the
elapsed time is calculated and displayed as the reaction time of the user.
#--------------------------------------------------------------------
# REACTION TIMER
# ==============
#
# This is a reaction timer project. An LED is lit at random times
# and the user is requested to press a button as soon as it is lit.
# The time between seeing the LED lit and pressing the button is
# measured and displayed in seconds on the LCD.
#
# Author: Dogan Ibrahim
# File : reaction.py
# Date : June 2020
#----------------------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import multiprocessing # Import multiprocessing
import random # Import random
import time # Import time
● 189
PB = 20 # Button at GPIO 20
GPIO.setup(PB, GPIO.IN) # Button is input
#
# Process to turn ON the LED
#
def LED_ON(): # Process LED_ON
LED = 21 # LED at GPIO 21
GPIO.setup(LED, GPIO.OUT) # LED is output
GPIO.output(LED, 0)
#
# LCD Display control. Display the reaction time in seconds
#
LCD.lcd_clear() # Clear LCD
LCD.lcd_display_string("REACTION TIMER", 1) # Heading
● 190
An example display is shown in Figure 9.26 where the reaction time was 0.7370 seconds.
The multiprocessing Event has the following methods (assuming e is the created event
flag):
e.wait(t): wait t seconds for the event flag to be set. If not set within the specified
timeout, continue. Here, t is optional
Description: This project shows how to use a stepper motor in a multitasking environ-
ment. A small stepper motor is connected to the Raspberry Pi through a driver module.
The operation of the motor is controlled from the keyboard using the following commands:
● 191
Block Diagram: Figure 9.27 shows the block diagram of the project.
Stepper motors
Stepper motors are DC motors that rotate in small steps. These motors have several coils
that are energized in sequence, causing the motor to rotate one step at a time. Stepper
motors have the advantages that very precise positioning or speed control of the motor
shaft can be achieved. These motors are used in many precision motion control applica-
tions, in robotic arms, and in mobile robots to drive the wheels.
● 192
Unipolar motors can be rotated in reverse by reversing the sequence of applied pulses.
Unipolar stepper motors can be driven in full stepping mode or half-stepping mode. The
most popular drive modes are 1 phase full-step, 2 phase full-step, and 2 phase half-step.
In 1 phase full-step mode, as shown in Table 9.1, each motor winding receives one pulse
per step. This mode has the disadvantage that the available torque is low.
Step a c b d
1 1 0 0 0
2 0 1 0 0
3 0 0 1 0
4 0 0 0 1
In 2-phase full-step mode, as shown in Table 9.2, two motor windings receive pulses per
step. The advantage of this mode is that a higher torque is available from the motor.
Step a c b d
1 1 0 0 1
2 1 1 0 0
3 0 1 1 0
4 0 0 1 1
In 2-phase half-step mode, as shown in Table 9.3, two motor windings sometimes receive
pulses, and sometimes only one winding receives a pulse. Because the motor is driven at
half-step mode, 8 steps are required to complete a cycle instead of 4. This mode gives
higher precision, but at the expense of lower torque.
Step a c b d
1 1 0 0 0
2 1 1 0 0
3 0 1 0 0
4 0 1 1 0
5 0 0 1 0
6 0 0 1 1
7 0 0 0 1
8 1 0 0 1
● 193
Table 9.4 shows the steps required to drive a bipolar stepper motor. Here, + and – signs
refer to the polarity of the voltages applied to the motor leads.
Step a c b d
1 + - - -
2 - + - -
3 - - + -
4 - - - +
RPM = 60β/360T
or, RPM = β/6T
where RPM is the number of revolutions per minute, β is the step constant of the motor in
degrees, and T is the time between the steps in seconds.
As an example, assume that the step constant of a stepper motor is 10 degrees (β = 10º).
If we want to rotate this motor at a speed of 1000 RPM (assuming that the motor is capable
of rotating this fast), the time between the pulses is calculated as:
● 194
n = v/β
For example, assuming that the step constant is 5º (β = 5) and that we want the motor to
rotate by 200 degrees, the required number of steps is:
n = 200/5 = 40
T0 = nT
For example, assuming the time between the pulses is 1ms, if we want the motor to rotate
for 5 seconds, the required number of pulses is given by:
Stepper motors can be driven by several ways, such as using bipolar transistors, using
MOSFET transistors, or using integrated circuits such as L293, ULN2003, and so on.
Circuit Diagram: In this project, a small 28BYJ-48 type unipolar stepper motor (see Fig-
ure 9.30) is used. This motor has the following specifications:
Rated voltage: 5V
Number of phases: 4
Gear ratio: 64
Frequency: 100Hz
Step angle: 11.25º / step
Maximum speed: 18 RPM
● 195
In this project, the motor is driven using a ULN2003 IC-based motor driver module shown
in Figure 9.31 together with its circuit diagram. This module has four input connections
labelled IN1, IN2, IN3, and IN4. The motor is plugged into the socket in the middle of the
module. Four LEDs, labelled A, B, C, D are provided to see the status of the motor windings.
Power to the module is applied through the bottom two header pins on the right-hand side
of the module. The LEDs can be enabled by shorting the two top header pins on the right-
hand side of the module. In this project, the module is powered from an external +5V DC
power supply for the motor (it is recommended to use an external +5V power supply, and
not use the Raspberry Pi +5V power supply as it may not provide enough current to drive
the motor).
Figure 9.32 shows the circuit diagram of the project. The connections between the stepper
motor controller and Raspberry Pi are as follows:
● 196
Program Listing: The 28BYJ-48 stepper motor can either be operated in full-step or in
half-step modes.
Full-step mode
In full-step mode, there are 4 steps per cycle and 11.25 degrees/step, corresponding to
32 steps per one revolution of the internal motor shaft. Because the motor is geared with
a gear ratio of 64 (in fact the gear ratio is 63.68395), the number steps for one external
complete revolution are 2048 steps/revolution (512 cycles with 4 steps per cycle).
Table 9.5 shows the motor winding sequence for the full-step mode (this sequence is re-
peated. Reversing the sequence reverses the direction of rotation).
1 1 1 0 0
2 0 1 1 0
3 0 0 1 1
4 1 0 0 1
Half-step mode
The half-step mode with 8 steps per cycle is recommended by the manufacturer. In half-
step mode we have 5.625 degrees/step, corresponding to 64 steps per one revolution of
the internal motor shaft. Because the motor is geared with a gear ratio of 64, the number
of steps for one external complete revolution is 4096 steps/revolution (512 cycles with 8
steps per cycle).
Table 9.6 shows the motor winding pulse sequence for the half-step mode (this sequence
is repeated. Reversing the sequence reverses the direction of rotation).
● 197
1 1 0 0 0
2 1 1 0 0
3 0 1 0 0
4 0 1 1 0
5 0 0 1 0
6 0 0 1 1
7 0 0 0 1
8 1 0 0 1
In this project, the stepper motor is controlled in Half-Step mode and Figure 9.33 shows the
program listing (program: stepper.py).
The speed of the motor depends on the delay inserted between each step. In Half-step
mode there are 4096 steps in a complete revolution. The motor speed in RPM is given by
the following equation:
Where, RPM is the motor speed in revolutions per minute, and T is the delay between each
step in milliseconds. We usually want to know how much delay to insert between each step
so that the required number of revolutions can be achieved. This is given in milliseconds by:
T = 14.648 / RPM
or,
T = 0.014648 / RPM
At the beginning of the program, 2 queues named command and rev, and an event
named e is created. Queue command stores the entered command, and rev stores the
number of required revolutions. Event flag e is used to stop the motor.
The program consists of a process called STEPPER. Inside this process, the connections
between the stepper motor driver pins IN1, IN2, IN3, and IN4 and the Raspberry Pi are
defined and these pins are configured as outputs. The RPM of the motor is set to 12, and
the motor parameters StepsPerRevolution, StepDelay, and HalfStep sequence are de-
fined. The remainder of process STEPPER implements the actual motor control. If the user
command is C then the required number of revolutions is read from queue rev and function
● 198
CLOCKWISE is called to rotate the motor shaft in the clockwise direction. If on the other
hand, the user command is A, the required number of revolutions is read and function AN-
TICLOCKWISE is called to rotate the motor shaft in the anticlockwise direction.
Inside functions CLOCKWISE and ANTICLOCKWISE, the program checks if the event flag
e is set and stops the program if it is (this event flag is set if the user enters command S
to stop the motor).
The main program prompts the user to enter one of the following valid commands on their
keyboard. An error message is displayed if an invalid command is entered:
For example, entering CW5 will rotate the motor shaft in the clockwise direction by 5 revo-
lutions. Entering command S at any time on the keyboard will stop the motor immediately.
The user command is put into queue command. The number of revolutions is extracted,
converted into an integer, and then put into queue rev. Notice that user commands can
be entered as either lower case or upper case since they are all converted to upper case
before they are processed.
#--------------------------------------------------------------------
# STEPPER MOTOR CONTROL
# =====================
#
# This is a stepper motor control project. A small stepper motor
# connected to the Raspberry Pi is controlled by entering the
# following commands from the keyboard:
#
# CW : rotate clockwise
# AW: rotate anticlockwise
# S : stop
# X : terminate program
#
# Author: Dogan Ibrahim
# File : stepper.py
# Date : June 2020
#----------------------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import multiprocessing # Import multiprocessing
import time # Import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
● 199
#
# This function sends pulses to the motor in Half-Step mode
#
def SendPulse(k):
global IN1,IN2,IN3,IN4mHalfStep
GPIO.output(IN1, HalfStep[k][3]) # Send to IN1
GPIO.output(IN2, HalfStep[k][2]) # Send to IN2
GPIO.output(IN3, HalfStep[k][1]) # Send to IN3
GPIO.output(IN4, HalfStep[k][0]) # Send to IN4
#
# This function rotates the motor clockwise. VAriabe count is the
# number of times the motor shaft will rotate 360 degrees
#
def CLOCKWISE(count):
global StepsPerRevolution, StepDelay
for j in range(0, count):
for m in range(0, StepsPerRevolution):
for i in range(7, -1, -1):
SendPulse(i)
if e.is_set(): # Is event flag e set?
e.clear() # Clear event flag e
return # Stop the motor
time.sleep(StepDelay)
#
# This function rotates the motor anticlockwise. Variable count is the
# number of times the motor shaft will rotate 360 degrees
#
def ANTICLOCKWISE(count):
global StepsPerRevolution, StepDelay
for j in range(0, count):
for m in range(0, StepsPerRevolution):
for i in range(0, 8):
SendPulse(i)
if e.is_set(): # Is event flag e set?
e.clear() # Clear event flag e
return # Stop the motor
time.sleep(StepDelay)
● 200
RPM = 12 # RPM
StepsPerRevolution = 512 # Steps/Rev
StepDelay = 0.014648 / RPM # Step delay
#
# Create the process
#
p = multiprocessing.Process(target = STEPPER, args = ())
p.start()
● 201
#
# Read control parameters from the keyboard. Read a command and the
# required number of revolutions of the motor shaft
#
while True: # Do forever
print("")
cmd = input("Enter a command (CWn,AWn,S, X): ").upper()
if cmd.find("CW") >= 0:
revs = int(cmd[2:])
rev.put(revs)
command.put("C") # Put "C" in queue
elif cmd.find("AW") >= 0: # Is it "AW"?
revs = int(cmd[2:]) # Get rev count
rev.put(revs) # Put rev count in queue
command.put("A") # Put "A" in queue
elif cmd.find("S") >= 0: # Is it "S"?
e.set() # Set event flag e
elif cmd.find("X") >= 0: # Is it "X"?
p.terminate() # Terminate program
print("End of program") # Display message
exit(0) # Exit
else:
print("Invalid command...") # Display invalid
Description: In this project, an LED, keypad, and LCD are connected to the Raspberry
Pi. The flashing rate of the LED is set using the keypad. This project aims to show how a
keypad can be used in a multitasking environment. Like the 7-segment displays, keypads
are ideal applications for multitasking.
● 202
Keypads are used in many microcontroller-based applications since they are small, porta-
ble, and do not require any external power supplies.
Block Diagram: Figure 9.35 shows the block diagram of the project. The D key is used as
the Enter key.
The Keypad: Several types of keypads can be used in microcontroller based projects. In
this project, a 4x4 keypad (see Figure 9.36) is used. This keypad has keys for numbers 0
to 9 and letters A,B,C,D,*, and #. The keypad is interfaced to the processor with 8 wires
with the names R1 to R4 and C1 to C4, representing the rows and columns respectively of
the keypad (see Figure 9.37).
● 203
The operation of the keypad is very simple: the columns are configured as outputs and the
rows as inputs. The key pressed is identified by using column scanning. Here, a column is
forced low while the other columns are held high. Then the state of each row is scanned,
and if a row is found to be low, the key at the intersection of the row (which is low) and this
column is the key pressed. This process is repeated for all rows.
Circuit Diagram: Figure 9.38 shows the circuit diagram of the project. The I2C LCD is
connected to the Raspberry Pi as in the previous projects using the LCD, where GPIO 2 and
GPIO 3 are used as the SDA and SCL pins respectively. The LED is connected to GPIO 21 of
the Raspberry Pi. The 4x4 keypad is connected to the following GPIO pins of the Raspberry
Pi. The row pins are held high using 10K pull-up resistors to +3.3V (notice that Raspberry
Pi has internal pull-up resistors when a pin is used as an input but the use of these pull-up
resistors are not reliable):
Figure 9.39 shows the pin configuration of the 4x4 keypad used in the project.
● 204
Test program
Before writing the project program we will, first of all, develop the code to read keys from
the keypad. The basic steps to read a key are as follows:
At the beginning of the program the keypad keys are defined after importing the required
modules to the program. The keypad row and columns connections are defined using lists
ROWS and COLS respectively. Columns are then configured as outputs and are set to
1. Similarly, the rows are configured as inputs. Function Get_Key reads the pressed key
and returns it to the calling program. Two for loops are used in the function: the first loop
selects the columns and sets them to 0 one after the other one. The second loop scans
the rows and checks if a row is at 0. The main program calls the function and displays the
pressed key on the screen.
● 205
#--------------------------------------------------------------
# KEYPAD TEST PROGRAM
# ===================
#
# This program shows how the a keypad can be used to dislay
# the pressed keys
#
# Author: Dogan Ibrahim
# File : keypad.py
# Date : June 2020
#-------------------------------------------------------------
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
#
# This function reads a key from the keypad
#
def Get_Key():
while True:
for j in range(4):
GPIO.output(COLS[j], 0) # Set col j to 0
for i in range(4): # For all rows
if GPIO.input(ROWS[i]) == 0: # Row is 0?
return (KEYPAD[i][j]) # Return key
while GPIO.input(ROWS[i]) == 0:
pass
GPIO.output(COLS[j], 1) # Col back to 1
● 206
try:
while True:
key = Get_Key() # Get a key
print(key) # Display the key
time.sleep(0.5)
except KeyboardInterrupt:
GPIO.cleanup()
Program Listing: We are now ready to develop the program of this project. Figure 9.41
shows the program listing (program: keypadled.py). In this program, key D is assumed
to be the ENTER key where all inputs to the keypad must be terminated by pressing the
ENTER key. There is one process in the program called FLASH. This process flashes the
LED at a rate set by variable dly (the default value of dly is set to one second). The flashing
rate is extracted from the queue and is loaded into variable dly.
The main program controls the LCD and keypad to receive the required flashing rate. The
top row of the LCD shows the text Flash rate (ms): The program runs in an endless loop
where the keys entered by the user are read and the required total delay is calculated
and stored in variable Total until the ENTER key is pressed. The LCD shows each number
entered by the user in the second row and when the ENTER key is pressed, the required
flashing rate is calculated in seconds by dividing the number read by 1000. This number
(in variable tim) is then put into the queue so that it can be extracted by process FLASH
to change the flashing rate. The LED SET text is then displayed for 2 seconds to confirm
the entered number has been accepted and the flashing rate has been changed. After 2
seconds, the second row of the LCD is cleared, ready for the next entry.
#-----------------------------------------------------------------
# SETTING LED FLASHING RATE WITH KEYPAD
# =====================================
#
# In this program an LED is connected to the Raspberry Pi and
# the flashing rate of the LED is set using the keypad
#
# Author: Dogan Ibrahim
# File : keypadled.py
# Date : June 2020
#----------------------------------------------------------------
import RPi.GPIO as GPIO
import time
import multiprocessing
import RPi_I2C_driver
● 207
LCD = RPi_I2C_driver.lcd()
#
# This function reads a key from the keypad
#
def Get_Key():
while True:
for j in range(4):
GPIO.output(COLS[j], 0) # Set col j to 0
for i in range(4): # For all rows
if GPIO.input(ROWS[i]) == 0: # Row is 0?
return (KEYPAD[i][j]) # Return key
while GPIO.input(ROWS[i]) == 0:
pass
GPIO.output(COLS[j], 1) # Col back to 1
time.sleep(0.05) # Wait 0.05s
#
# This process flashes the LED at the rate dly which is
# entered from the keyboard
#
def FLASH(): # Process FLASH
LED = 21 # LED at GPIO 21
GPIO.setup(LED, GPIO.OUT) # LED is output
dly = 1 # 1 second (default)
● 208
#
# Main program. Here the flashing rate is read from the keypad
# and displayed on the LCD and is then sent to process FLASH
LCD.lcd_clear()
LCD.lcd_display_string("Flash rate (ms):", 1) # Heading
Total = 0
An example display is shown in Figure 9.42. In this example, the flashing rate was changed
to 100ms.
● 209
Description: This is a multitasking secure door lock project. In this project, an LED, LCD,
relay, and keypad are connected to the Raspberry Pi. The project activates the relay to
open a door (or safe) if the correct code is entered on the keypad. The secret code in this
project is preset to 1234 for simplicity. The LED flashes to indicate that the system is ready
to accept the code. If the code is entered wrongly 3 times, the LED is turned OFF and the
system is locked for 5 minutes, accepting no code entries. The LCD shows the code entered
by the user.
Block Diagram: Figure 9.43 shows the block diagram of the project. The D key is used as
the Enter key.
Circuit Diagram: Figure 9.44 shows the circuit diagram of the project. The I2C LCD is
connected to the Raspberry Pi as in the previous projects using the LCD, where GPIO 2 and
GPIO 3 are used as the SDA and SCL pins respectively. The LED and the relay are connected
to GPIO 21 and GPIO 20 of the Raspberry Pi. The 4x4 keypad is connected as in the previ-
ous project. It is assumed the relay is activated when logic 1 is applied to its terminals, and
that the door mechanism is opened when the relay is activated.
● 210
Program Listing: Figure 9.45 shows the program listing (program: door.py). The opera-
tion of the program can be summarised as follows:
When the program is started, the relay is deactivated, the LED starts flashing, and the text
Enter code: is displayed on the first row of the LCD. The user is then expected to enter the
secret code to open the door. If the code 1234 is entered, the relay will be activated for 10
seconds, message Opened will be displayed on the LCD, and the relay will be deactivated
at the end of 10 seconds. At this time, it is assumed that the door will be closed. If the
user enters the wrong code, the message Try Again will be displayed on the second row
of the LCD. The user is given 3 consecutive attempts to enter the correct code and if the
code is wrong, the message Disabled will be displayed on the second row of the LCD and
the LED will be turned OFF to indicate the system is disabled and does not accept a code.
The system is disabled for 5 minutes. After this time, the LED is turned ON again so that it
starts flashing, and the user is given the chance to enter the secret code again. The above
process is repeated forever until the program is terminated by the user.
As shown in Figure 9.45, at the beginning of the program the modules used by the program
are imported, an event is created and the keypad pins are configured. The program consists
of a process called FLASH. This process flashes the LED every 0.25 seconds as long as the
event flag e is cleared. If the event flag is set, then the LED is turned OFF. The event flag is
set when the user enters the wrong code 3 consecutive times. The main program controls
the relay and LCD. The relay is initially deactivated. Function Get_Key receives the code
entered by the user. Notice that the code must be terminated by pressing the ENTER key
which is key D on the keypad. When the ENTER key is detected, the program compares
the code entered with the secret code stored in variable SecretCode. The relay will be
activated if the entered code is correct as described earlier.
#-----------------------------------------------------------------
# SECRET DOOR LOCK WITH KEYPAD
# ============================
#
# In this program an LED,a relay, an LCD and a keypad are connected
# to the Raspberry Pi. The relay is activated when the correct secret
# code is entered on the keypad. The LED flashes to indicate when the
# system is ready to accept the code. If the code is entered wrong 3
# times then the system is disabled for 5 minutes.
#
# Author: Dogan Ibrahim
# File : door.py
# Date : June 2020
#----------------------------------------------------------------
import RPi.GPIO as GPIO
import time
import multiprocessing
import RPi_I2C_driver
LCD = RPi_I2C_driver.lcd()
● 211
#
# This function reads a key from the keypad
#
def Get_Key():
while True:
for j in range(4):
GPIO.output(COLS[j], 0) # Set col j to 0
for i in range(4): # For all rows
if GPIO.input(ROWS[i]) == 0: # Row is 0?
return (KEYPAD[i][j]) # Return key
while GPIO.input(ROWS[i]) == 0:
pass
GPIO.output(COLS[j], 1) # Col back to 1
time.sleep(0.05) # Wait 0.05s
#
# This process flashes the LED at the rate dly which is
# entered from the keyboard
#
def FLASH(): # Process FLASH
LED = 21 # LED at GPIO 21
GPIO.setup(LED, GPIO.OUT) # LED is output
● 212
#
# Main program. Here the secret code is read and the relay is
# activated if the code is correct, otherwise the system is disabled
#
Relay = 20 # Relay at GPIO 20
GPIO.setup(Relay, GPIO.OUT) # Relay is output
GPIO.output(Relay, 0) # relay OFF
● 213
Description: This is a car park management system where the idea is to control the op-
eration of a car park. It is assumed the car park has one level with a capacity of 100 cars.
An LCD close to the car park shows the number of free spaces available in the car park.
Only members of the car park are allowed to use the car park where each member is given
an RFID (Radio Frequency IDentification) card for identification. A barrier is placed at the
entrance to the car park which is operated (i.e. lifted) with a stepper motor. If there are
spaces in the car park and when a valid member customer places an authorized RFID card
close to the card reader near the entrance to the car park, the barrier is lifted automatically
to let the driver into the car park. Unauthorized RFID cards are rejected and the barrier is
not lifted. There is no barrier at the exit from the car park, but a passive pressure switch is
placed under the exit route that detects the vehicles as they leave the car park. The number
of spaces available inside the car park is calculated as the cars enter and leave the car park
and this is displayed continuously on the LCD. The barrier is not lifted if there are no spaces
inside the car park. A red light and a green light (e.g. LED in this project) are mounted on
the entrance to the car park. When the red LED is ON, the driver is required to wait until
the Green LED comes ON. The Green LED comes ON when the barrier is lifted, so that a car
can enter the car park. When the barrier is down, the Red LED comes ON and the Green
LED is turned OFF.
All operations of the car park are controlled using multitasking with the Python program-
ming language on a Raspberry Pi 4.
● 214
Block Diagram: Figure 9.47 shows the block diagram of the car park. The physical inter-
face to the Raspberry Pi is not shown in this Figure.
● 215
need to be within the line of sight of the readers. Additionally, tags can contain much more
information than simple barcodes.
RFID tags can be either passive or active (battery operated). Passive tags are cheaper and
smaller and are more commonly used. Passive tags must be placed very close to the RFID
readers (e.g. at 5cm) so that their contents can be read. These tags can be read-only or
read-write type. Read-only tags are pre-programmed in factories with unique numbers
and these numbers can be read using compatible RFID readers. Active RFID tags have the
advantage that they can be read from longer distances, but they are much more expensive
than passive tags.
In this project, a passive RFID tag system is used. The RFID reader used in this project is
the RDM6300 type UART based reader (see Figure 9.48). This reader is EM4100 protocol
compatible and operates with the 125kHz RFID tags (see Figure 9.49). The basic features
of the RDM6300 readers are as follows:
● 216
The RDM6300 is sold with a coil antenna that communicates with the RFID cards. This an-
tenna must be connected to the reader board. Figure 9.50 shows the pin configuration of
the RDM6300 reader.
There are 3 headers on the board with the following functions. Notice header 3 is for testing
where an optional external LED can be connected. The LED goes from logic HIGH to LOW
when an RFID tag is present:
Header P1
1. Pin number Description
2. TX (UART transmit)
3. RX (UART receive)
4. Not used
5. GND
6. +5V power supply
Header P2
1. Antenna
2. Antenna
Header 3
1. External LED
2. +5V power supply
3. GND
● 217
The RDM6300 reader operates at 9600 Baud, 8 data bits, 1 stop bit, and no parity bit. At
9600 baud, bit time is 104μs. The interface protocols available for this reader are the Wei-
gang26 and TTL level RS232. In this project, the TTL level RS232 interface and protocol
is used. Normally, the standard RS232 signal voltage levels are ±12V. TTL based RS232
signal voltage levels are 0 or +5V (or 0 to +3.3V) where the line is normally at +5V and
goes to 0V at the beginning of data transmission (i.e. the start bit is at logic 0V). Reader
output consists of 14-bytes as follows:
The start and end flags are always 0x02 and 0x03 respectively. The checksum is calculated
by exclusive Or' ing all the data bytes.
Notice the card number marked on the RFID cards is a 10 digit decimal number. For ex-
ample, if the decimal number on the card is 007564912, it corresponds to hexadecimal
number 00736E70.
Circuit Diagram: The circuit diagram of the project is shown in Figure 9.51. The I2C LCD
is connected to GPIO 2 and 3 as in the previous I2C LCD projects. The stepper motor is
attached to the motor driver board and the board pins IN1, IN2, IN3, IN4 are connected to
port pins GPIO 23, GPIO 24, GPIO 25, and GPIO 8 respectively. The motor driver board is
powered from an external +5V power supply. The pressure switch is connected to port pin
GPIO 16. The output of this switch is at logic 1 and goes to logic 0 when a car is present on
the switch. The TX pin of the RFID reader module is connected to UART pin GPIO 15 (RXD)
of the Raspberry Pi. The RX pin of the RFID reader is not used in this project. Notice that
although the RDM6300 operates with +5V, the voltage at its TX output does not get more
than +3.3V and as a result of this, we can directly connect the TX pin to the RXD pin of the
Raspberry Pi (GPIO 15). The red and the green LEDs are connected to GPIO 21 and GPIO
20 respectively through 470 Ohm current limiting resistors.
The interface between the Raspberry Pi and the various peripheral devices is summarized
below:
● 218
The antenna of the RFID reader module is connected to header pins P2. The LED interface
of the RFID reader module is not used in this project.
Program Listing: The RDM6300 RFID reader uses the serial port of the host processor.
We have to enable the serial port of the Raspberry Pi 4 before using the RFID device. This
is described below in some detail.
The Raspberry Pis have two built-in UARTs: a PL011 and a mini UART. They are implement-
ed using different hardware blocks and therefore have slightly different characteristics. On
Raspberry Pis which are equipped with Wireless/Bluetooth modules (e.g. Raspberry Pi 3,
Zero W, 4, etc), the PL011 UART is connected by default to the Bluetooth module, while
the mini UART is the primary UART with the Linux console on it. In all other models, the
PL011 is used as the primary UART. By default, /dev/ttyS0 refers to the mini UART and /
dev/ttAMA0 refers to the PL011. The Linux console uses the primary UART which depends
on the Raspberry Pi model used. Also, if enabled, /dev/serial0 refers to the primary UART
(if enabled), and if enabled, /dev/serial1 refers to the secondary UART
By default, the primary UART (serial0) is assigned to the Linux console. Using the serial port
for other purposes requires this default configuration to be changed. On startup, systemd
checks the Linux kernel command line for any console entries and will use the console de-
fined therein. To stop this behaviour, the serial console setting needs to be removed from
the command line. This is easily done by using the sudo raspi-config utility by selecting
option 5 (Interfacing Options) and then P6 (Serial), and select No to the question Would
you like a login shell to be accessible over serial? and then Yes to the question Would
you like the serial port hardware to be enabled?. Click OK and Exit raspi-config and
restart your Raspberry Pi (note: do not forget to re-enable the console serial port after you
finish your project).
● 219
On Raspberry Pi 3 and 4, the serial port (/dev/ttyS0) is routed to two pins GPIO14 (TXD)
and GPIO15 (RXD) on the header. This port is stable and of a good quality. This project is
based on the Raspberry Pi 4.
To search for available serial ports on your Raspberry Pi, use the command:
After disabling the serial console, you should have the display shown in Figure 9.53 when
the command dmesg | grep tty is entered. We can now use the serial port to read data
from the RFID reader.
Figure 9.54 shows the program listing (program: carprk.py). The program consists of 3
processes with the names: PRESSURE, RFID, and STEPPER. In this program, the pro-
cesses exchange data with each other using the multiprocessing Value as described in an
earlier Chapter.
● 220
At the beginning of the program, the modules used in the program are imported into
the program. Note that Value is imported as from multiprocessing import Value. The
traceback limit is set to zero so that error messages are not generated when the program
is terminated using the Cntrl+C keys.
The operation of the processes are described below using PDL (Program Description Lan-
guage):
Process STEPPER
Define stepper motor connections to Raspberry Pi
Define stepper motor parameters
Configure stepper moor connections as outputs
Define the Half-Step sequence
DO FOREVER
Wait for event flag e to be set
Clear event flag e
Open the barrier (lift it)
Wait 15 seconds
Close the barrier (lower it)
ENDDO
Process PRESSURE
Define connection between pressure sensor switch (button) and Raspberry Pi
Configure the button as input
DO FOREVER
Wait until the button is pressed
Get the number of space count
Increment the space count
Save the space count
Wait until the button is released
ENDDO
Process RFID
Define RED and GREEN LED interfaces
Configure the LEDs as outputs
Turn ON RED LED
Turn OFF GREEN LED
Open the serial port
Define the valid RFID tags (only 3 are used)
● 221
DO FOREVER
Read RFID tag code
IF this is a valid code and there are spaces in the car park THEN
Decrement the space count by one
Save the space count
Set event flag e (activate stepper motors)
Turn OFF RED LED
Turn ON GREEN LED
ENDIF
Wait 5 seconds
ENDDO
The main program displays the space count on the LCD where the display is updated every
second.
On entry:
• Car park space count is displayed on the LCD
• Red LED is ON, Green LED is OFF
• A car approaches the car park
• Member places the RFID tag close to the RFID reader
• If the card is valid then it is accepted
• Car park space count is decremented by one
• The barrier is lifted up
• Green LED is ON, Red Led is OFF
• A car enters the car park
On exit:
• A car is on the pressure switch at the exit
• Increment the space count by one
• Wait until the car leaves
LCD:
• Display the car park space count
In this program, 3 valid RFID tags are used for demonstration purposes. Notice the card
reader returns 14 bytes, but only the bytes that represent the number on the cards are
extracted and used in the program.
In this program, the data exchange between the processes is done using the multiprocess-
ing Value objects, instead of using multiprocessing queues which was the case in earlier
multiprocessing projects. The arguments of the processes that are required to exchange
data are set to val. The processes themselves are created with an argument called n. The
statement n.value is then used to exchange the space count (variable spaces) between
processes.
● 222
#--------------------------------------------------------------------
# CAR PARK MANAGEMENT SYSTEM
# ==========================
#
# This is a car park control system. Vehicles whose owners have valid
# RFID cards can enter the car park after a barrier opens with the
# help of a stepper motor at the entrance. A green LED shows when it
# is safe to enter the car park.An LCD displays the number of free
# spaces inside the car park
#
# Author: Dogan Ibrahim
# File : carprk.py
# Date : June 2020
#----------------------------------------------------------------------
import RPi.GPIO as GPIO # Import RPi
import RPi_I2C_driver # Import LCD driver
import multiprocessing # Import multiprocessing
from multiprocessing import Value # Import Value
import time # Import time
import serial # Import serial
import sys
sys.tracebacklimit=0
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO mode BCM
LCD = RPi_I2C_driver.lcd() # LCD driver
#
# This function sends pulses to the motor in Half-Step mode
#
def SendPulse(k):
global IN1,IN2,IN3,IN4 # HalfStep
GPIO.output(IN1, HalfStep[k][3]) # Send to IN1
GPIO.output(IN2, HalfStep[k][2]) # Send to IN2
GPIO.output(IN3, HalfStep[k][1]) # Send to IN3
GPIO.output(IN4, HalfStep[k][0]) # Send to IN4
#
# This function rotates the stepper motor clockwise by specified degrees
# (90 degrees to close the barrier)
#
def BarrierDown(degrees):
global StepsPerCycle, StepDelay
DegreeTurn = StepsPerCycle * degrees / 360.0
d = int(DegreeTurn)
● 223
#
# This function rotates the motor anticlockwise by specified degrees
# (90 degrees to open the barrier)
#
def BarrierUp(degrees):
global StepsPerCycle, StepDelay
DegreeTurn = StepsPerCycle * degrees / 360.0
d = int(DegreeTurn)
#
# Process to control the stepper motor. The motor is rotated clockwise
# or anticlockwise to close or open the barrier respectively
#
def STEPPER(): # Process STEPPER
global IN1,IN2,IN3,IN4,RPM,StepsPerCycle,StepDelay,HalfStep
IN1 = 23 # IN1 is at GPIO 23
IN2 = 24 # IN2 is at GPIO 24
IN3 = 25 # IN3 is at GPIO 25
IN4 = 8 # IN4 is at GPIO 8
RPM = 10 # RPM
StepDelay = 0.014648 / RPM # Step delay
StepsPerCycle = 512 # Steps per cycle
● 224
[0,1,0,0],
[0,1,1,0],
[0,0,1,0],
[0,0,1,1],
[0,0,0,1],
[1,0,0,1]]
#
# Motor control loop
#
while True: # Do forever
e.wait() # Wait for event
e.clear() # Clear event
BarrierUp(90) # Rotate clockwise
time.sleep(15) # Wait 15 secs
BarrierDown(90) # Barrier down
#
# Process PRESSURE. This process scans the pressure button (switch)
# and if a vehicle leaves the car park then the spaces count is
# incremented by one
#
def PRESSURE(n): # Process PRESSURE
Button = 16 # Button at 16
GPIO.setup(Button, GPIO.IN) # Button is input
#
# Process RFID
#
def RFID(n): # Process RFID
RED = 21 # RED LED at 21
GREEN = 20 # GREEN LED at 20
GPIO.setup(RED, GPIO.OUT) # LED is output
GPIO.setup(GREEN, GPIO.OUT) # LED is output
GPIO.output(RED, 1) # Turn ON RED LED
GPIO.output(GREEN, 0) # Turn OFF GREEN LED
RFID = serial.Serial('/dev/ttyS0') # Conf serial port
● 225
#
# Create the process
#
p = multiprocessing.Process(target = PRESSURE, args = (val,))
r = multiprocessing.Process(target = RFID, args = (val,))
s = multiprocessing.Process(target = STEPPER, args = ())
p.start()
r.start()
s.start()
● 226
# Display the number of free spaces on the LCD. First row of the LCD
# displays "Free Spaces", while the second row displays "Spaces:nn"
# where nn is the number of free spaces in the car park
#
TotalCapacity = 100 # Total capacity
Spaces = TotalCapacity
LCD.lcd_clear() # Clear LCD
LCD.lcd_display_string("Free Spaces:", 1) # Display heading
val.value=Spaces # Save spaces
try:
while True: # Do forever
Spaces=val.value # Get spaces
s = "Spaces:" + str(Spaces) + " " # Add text
LCD.lcd_display_string(s, 2) # Display text
time.sleep(1) # Wait 1 sec
except KeyboardInterrupt:
GPIO.cleanup()
print("End of program")
● 227
• 4 x Button
• 4 x 10K resistor
• 2 x Red LED
• 2 x Orange LED
• 2 x Green LED
• 7 x 470 Ohm resistor
• 1 x Light Dependent Resistor (KY-018)
• 1 x 4 digit 7-segment common cathode LED display (e.g. 2 x DC56-11EWA)
• 4 x NPN transistors (e.g. BC108 or any NPN type)
• 4 x 1K resistors
• 1 x DHT11 temperature and humidity sensor
• 1 x small relay
• 1 x DS18B20 sensor (e.g. KY-001)
• 1 x small buzzer
• 1 x I2C LCD
• 1 x Ultrasonic module (e.g. HC-SR04)
• 1 x small stepper motor (e.g. 28BYJ-48)
• 1 x ULN2003 stepper motor driver
• 1 x 4x4 keypad
• 1 x RDM6300 RFID reader
• 1 x RFID tag
• 1 x small breadboard
• Female-male jumper wires
• Female-female jumper wires
Additionally:
• 1 x Raspberry Pi 4 processor with power supply
• 1 x +5V external power supply (1A)
● 228
● 229
Index
A G
Anonymous pipe 164 get 164
grep 32
B
Background process 36 H
Bipolar stepper motor 194 Half-step mode 197
halt 34
C HC-SR04 182
cat 26 help 28
check_call 156 history 33
check_output 157
clear 152 I
common anode 103 I2C LCD 139
common cathode 103 is_set 152
Conveyor belt 131
Co-operative scheduling 54 J
cp 29 jobs 36
CPU architecture 51
CPU utilization 46 K
crontab 39 Keypad 203
43 kill 49
killall 68
D KY-001 137
date 31
DHT11 112 L
Disk usage 49 LDR 131
dpkg 32 lock 149
Duty cycle 116 ls 21
Dynamic priority scheduling 60
M
E man 28
echo 31 Memory usage 46
Etcher 13 Metronome 171
Events 151 mkdir 22
exec 64 Multiple threads 148
Multilevel queue scheduling 60
F Multiprocessing 161
file 27 Multitasking event counter 74
fg 36 mv 30
First come, first served 59
foreground process 36 N
Full-step mode 197 Named pipe 164
● 230
P TightVNC 19
Pipes 164 TightVNC viewer 19
popen 61 Timer 153
Process fork 62 top 45
Process table 46 Traffic lights 175
ps 47
Pre-emptive scheduling 54 U
Pressure switch 215 ULN2003 196
put 164 Ultrasonic 181
Putty 16 Unipolar stepper motor 192
pwd 21 Up/down counter 80, 93
PWM 117
V
Q VNC 18
Queues 164 VNC server 19
R W
Raspbian Buster 12 wait 152
raspi-config 15 Wildcard 30
RDM6300 216
Relay 210
RFID reader 215
RFID tag 217
Resource monitoring 45
rmdir 30
Round-robin scheduling 54
S
SCL 139
SDA 139
Sharing data 164
Semaphore 150
set 152
Seven segment LED 101
126
Square waveform 116
SSH 18
Stepper motor 191
Subprocess 156
Synchronizing parent and child 78
T
Task scheduling 37
Temperature controller 136, 167
Threading 146
Threads 84
● 231