0% found this document useful (0 votes)
14 views580 pages

Intro To Robotics Level B V20314

The document presents a curriculum for 'Intro to Robotics Level B,' focusing on working with sensors and intermediate programming using Python. It aims to teach students, with no prior experience, how to build electrical circuits and write code to control these circuits, ultimately leading to the creation of a functional robot. The course is structured in a four-part series, with Level B serving as a bridge to more advanced concepts in robotics and programming.

Uploaded by

Brad M
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views580 pages

Intro To Robotics Level B V20314

The document presents a curriculum for 'Intro to Robotics Level B,' focusing on working with sensors and intermediate programming using Python. It aims to teach students, with no prior experience, how to build electrical circuits and write code to control these circuits, ultimately leading to the creation of a functional robot. The course is structured in a four-part series, with Level B serving as a bridge to more advanced concepts in robotics and programming.

Uploaded by

Brad M
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 580

42 ELECTRONICS

INTRO TO ROBOTICS

LEVEL B
WORKING WITH SENSORS & INTERMEDIATE PROGRAMMING

LEARN ROBOTICS AT HOME


Prepared exclusively for [email protected] Transaction: 5095
42 ELECTRONICS PRESENTS:

Intro to Robotics
Level B: Working with Sensors &
Intermediate Programming
V20314

By Eric Feickert and Julie Feickert

All contents copyright © 2020 by 42 Development LLC DBA 42 Electronics. All rights
reserved.

Prepared exclusively for [email protected] Transaction: 5095


INTRODUCTION TO
42 ELECTRONICS
Thank you for choosing 42 Electronics for teaching your children robotics. We have created this
curriculum to teach your children the same way we teach ours: by providing thorough instruction
on building electrical circuits, writing computer code in Python, and pulling those two skills
together to build mobile electronic devices and write programs to control them (robotics!)

When our own children reached an age where they were ready to dive into electronics,
programming, and robotics, we started searching for a great kit to get them started. We were so
disappointed with what we found! The kits available often have components and instruction
booklets for specific projects but do not have any systematic teaching to understand how
components work and interface with each other. Also, there's not much out there for initially
learning to integrate circuits with the Raspberry Pi making it difficult to efficiently build skills to
tackle the many amazing projects available online. Essentially, if you don't already know what
you're doing, the other kits on the market aren't going to do much to improve that situation.

We decided to tackle this project by designing a curriculum written directly to the student which
assumes no background in electronics, programming, or robotics by either the parent or the
child. We start with the most basic concepts and systematically work our way forward to achieve
a thorough understanding.

Intro to Robotics Course of Study:


Level A: Building Circuits and Beginning Programming

Level B: Working with Sensors and Intermediate Programming YOU ARE HERE

Level C: Incorporating Video and Advanced Programming

Level D: Working with Motors and Taking It Mobile

Our goal is that when a child completes this four-part course they will have:

• A functioning robot and the skills to reconfigure the robot for any number of tasks.
• An extensive set of components to work with.
• The skills to venture out to design projects themselves or complete projects they find
online.

Prepared exclusively for [email protected] Transaction: 5095


PHOTOCOPYING & DISTRIBUTION
POLICY
All contents copyright © 2020 by 42 Development LLC DBA 42 Electronics. All rights
reserved.

No part of this document may be reproduced or transmitted in any form except with
written permission by the author. This includes email lists or websites.

Families may make as many photocopies of this material as you need for use WITHIN
YOUR OWN FAMILY ONLY.

Schools and Co-ops MAY NOT PHOTOCOPY any portion of the materials. 42
Electronics offers a reprinting license of $10 per student, per course level, per year. If
you would like to repurchase a printing license, please contact 42 Electronics at
[email protected].

Prepared exclusively for [email protected] Transaction: 5095


LEVEL B TABLE OF CONTENTS
Level B Overview .................................................................................................. 1
How to Use This Course ....................................................................................... 2
Lesson 1: File and Folder Management................................................................ 7
Lesson 2: Functions ............................................................................................ 38
Lesson 3: Program Layout Options and Advanced String Concepts .................. 59
Lesson 4: Import Methods and Pulse Width Modulation ..................................... 85
Lesson 5: Switches and Correcting for Switch Bounce ..................................... 114
Lesson 6: Logical Operators ............................................................................. 143
Lesson 7: Working with a 3x4 Matrix Style Keypad .......................................... 173
Lesson 8: Github and Python 2 vs. Python 3 .................................................... 202
Lesson 9: Analog Signal Processing with the Raspberry Pi.............................. 229
Lesson 10: Potentiometers, Phototransistors, and List Commands .................. 262
Lesson 11: RFID ............................................................................................... 297
Lesson 12: Using Input Files and Multithreaded Operations............................. 327
Lesson 13: Level Shifting and Infrared Sensors ............................................... 359
Lesson 14: Ultrasonic Range Sensing and NumPy .......................................... 395
Lesson 15: I2C and Temperature Sensing ....................................................... 428
Lesson 16: OLED I2C Display .......................................................................... 461
Lesson 17: Capacitors and Capacitive Touch Sensors..................................... 499
Lesson 18: Range Sensing Game .................................................................... 532
Quick Reference: Components ......................................................................... 564
Quick Reference: List of Materials for Each Lesson ......................................... 571

Prepared exclusively for [email protected] Transaction: 5095


OVERVIEW

LEVEL B: WORKING WITH


SENSORS AND INTERMEDIATE
PROGRAMMING

Level B is the second in a four-part series making up our Intro to Robotics course. In
this level, your student will continue to learn to build electrical circuits using common
components, learn to write more complex programs in Python, and pull those two skills
together to learn to control the circuits he or she builds with the computer code he or
she writes.

This level lays the ground work for Levels C which move on to adding more complex
audiovisual components such as sound and video, all while continuing to advance their
Python coding skills. In Level D, all these components and coding skills come together
allowing the student to build a mobile robot they will program to gather data, make
decisions, perform tasks, etc.

Our goal in the end, is that your student not only have a fully functional robot but that
they have a full understanding of every component and every line of code. This is
powerful knowledge! The collection of components they will have amassed along with
the knowledge they will have built, will allow them total flexibility to modify the robot,
build other robotic devices they dream up, or take on entirely new projects they may find
online.

Intro to Robotics Course of Study:


Level A: Building Circuits and Beginning Programming

Level B: Working with Sensors and Intermediate Programming YOU ARE HERE

Level C: Adding Audiovisual and Advanced Programming

Level D: Working with Motors and Taking It Mobile

Level B Overview Page 1

Prepared exclusively for [email protected] Transaction: 5095


HOW TO USE THIS COURSE
Welcome to Intro to Robotics Level B! We are excited to take this journey with you.
Please read the following information carefully before starting the course. If you have
any questions, please don’t hesitate to reach out:
www.42electronics.com/pages/contact-us

OPEN AND GO
This program is written directly to the student and is designed for students (and parents)
with no previous electronics or programming experience. Each lesson will walk you
through step-by-step to teach you to use common electrical circuit components and
common Python commands. Please follow each lesson as written for optimal results.

PREVIOUS EXPERIENCE

Please be sure your student completes all lessons in Level A prior to moving to Level B.
Both the skills and materials used in Level A are necessary for completing Level B.

NEXT LEVELS
Once you have completed Level B, please move onto the next levels:
• Level C: Audiovisual and Advanced Programming
• Level D: Working with Motors and Taking It Mobile

Level B: How to Use This Program Page 2

Prepared exclusively for [email protected] Transaction: 5095


SCHEDULING
We recommend scheduling 1-2 sessions weekly for this program. The average lesson is
60-90 minutes and we suggest leaving time, especially in later lessons, to practice and
explore further using your new skills. The program will take 9-18 weeks to complete
depending on how many days a week are scheduled.
High school students can aim to complete A and B in a single semester if desired
(Levels A and B together represent 0.5 credits).

Note to Parents of Middle School Students: The concepts taught in Level B are more
complex than Level A, so parents of younger students should consider significantly
slowing down lesson frequency to perhaps once per week or less. This would allow the
student to complete the lesson more than once if needed and to practice the new skills
prior to moving on to the next lesson. Keep in mind because students are constantly
building on previous skills, there are lots of options for experimenting with new skills in
between lessons to include other skills and components that were taught previously.

Middle school students should aim to complete Levels A and B in 1+ school year (we
strongly encourage moving at your child's individual pace--there's plenty of time to
complete all four levels!)

PARTS KIT
The parts kit for this course shipped within a few days of your order being placed. Once
it arrives, please leave all components in their packaging in the box. This will keep them
safe and clean. You’ll pull them out as needed for each lesson. Each bag is clearly
labeled and each lesson will list which components are needed. A complete list of
included parts and pictures, can be found on page 561.
TIP: If you take good care of the components, they will be reusable to build many
amazing projects long after you complete this course.

REQUIRED EQUIPMENT
In addition to this curriculum and the Level B parts kit purchased with it, your student will
also need the equipment they used in Level A:

• Computer Monitor (a television can also be used provided it has HDMI inputs)
• HDMI cable (likely attached to your computer monitor)
• Keyboard and Mouse

Level B: How to Use This Program Page 3

Prepared exclusively for [email protected] Transaction: 5095


o Please use a wired keyboard and mouse if possible. Wireless models do
not work reliably with the Raspberry Pi. Visit www.42electronics.com/level-
a-resources for a list of inexpensive keyboard and mouse options.
• Internet access (wired or wireless)
o For this level of the course, internet access is required to download
libraries.

WHAT’S NEXT?
After completing this course, please move on to Level C of this program available at
www.42electronics.com. The next level will continue to build both your electrical and
coding skills as well as add fun new components to your tool box.

YOUNGER STUDENTS
We recommend this course for students in middle school and high school (and for
adults who want to learn how to work with the Raspberry Pi to do amazing projects). It is
possible for a younger student to use this course, but we would recommend the
following:
• Work alongside an adult or older child. Remember, this curriculum is designed
for people who have no previous experience, so your parents shouldn’t be afraid
to join you.
• Move quickly through sections that focus on theoretical background and
mathematical equations. You can always revisit these sections as you get older.
• Keep in mind that Level A is more appropriate for younger students since Levels
B-D teach significantly more complex skills. If your student is not quite ready to
move on to B after A, it’s best to let them continue to experiment with skills
learned in Level A and try again with B in 6-12 months.

Level B: How to Use This Program Page 4

Prepared exclusively for [email protected] Transaction: 5095


ACADEMIC CREDIT (HIGH SCHOOL)
For parents wishing this to be a full academic curriculum for a high school student, we
would recommend some combination of the following:
• Require written answers for the Questions for Understanding at the end of
each lesson (we have included the answers on a separate page to facilitate
this).

• Research and build Raspberry Pi and Python projects found online

• Have the student design a circuit and program with the skills they’ve learned
as a final project.

• Complete multiple levels of this course. Scheduled for 2-3 days a week, four
levels (plus the additional options below) should take approximately one
academic year to complete. Additional levels may be purchased at
www.42electronics.com.

• If it is not possible to complete Levels A-D, we would suggest completing a


full Python coding course at www.codeacademy.com. This course can be
taken for a low monthly fee and generally takes 8-12 hours to complete and is
suited for older high school students and adults.

• Have the student read one or more books outlining the historical development
of the fields of electronics or computer science, or a biography of a significant
contributor to the field and write a report. Excellent choices include Nikola
Tesla, Albert Einstein, Ada Lovelace, Charles Babbage, and Alan Turing.

REUSING THE CURRICULUM


This program is non-consumable and both the curriculum and kit are fully reusable for
other children in the immediate family. The copyright for this curriculum does not allow it
to be sold or given away.

Level B: How to Use This Program Page 5

Prepared exclusively for [email protected] Transaction: 5095


LESSONS

Page 6

Prepared exclusively for [email protected] Transaction: 5095


LESSON 1

FILE AND FOLDER MANAGEMENT

OBJECTIVE
Learn administrative file management skills for working in both the Graphical User
Interface (GUI) and the Terminal.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Working in the GUI and Terminal Interfaces; Working with the Sudo Command
(Lesson A-9)

Lesson 1 – File and Folder Management Page 7

Prepared exclusively for [email protected] Transaction: 5095


LESSON
Welcome to Level B! Before you launch into learning more Python code and working
with more electrical components, you need to master managing your files and folders.
This will allow you to advance to more complex projects. Specifically, in this lesson you
will learn to manipulate files in both the Graphical User Interface (GUI) and the
command line. Command line file work will become more important as you progress to
more advanced Python programming related tasks that are beyond what the GUI can
support.

UNDERSTANDING THE RASPBIAN FILE SYSTEM


Just like the Windows operating system (OS), in Raspbian, there are many files and
folders operating in the background to run the OS while you primarily interact with files
and folders on your desktop. Here is a list of all the main folders in the Raspbian OS:

/ **System files** – Top level folder known as root directory


/bin **System files** – user binaries
/boot **System files** – boot files
/dev **System files** – device files
/etc **System files** – configuration files
/home Contains user files like everything on your desktop
/lib **System files** – shared libraries
/lost+found **System files** – files that have been corrupted during a crash
/media Removable media like USB drives
/mnt **System files** – Temporary mount points
/opt **System files** – Optional software packages
/proc **System files** – Kernel and process files
/root **System files** – Root users home directory
/run **System files** – Run files for applications
/sbin **System files** – System administration binaries
/srv **System files** – Service files
/sys **System files** – System information
/tmp **System files** – Temporary files
/usr **System files** – User binaries
/var **System files** – Variable data files

Lesson 1 – File and Folder Management Page 8

Prepared exclusively for [email protected] Transaction: 5095


It is recommended you never modify any files in the folders above
marked **System files**. The information in these folders is required
for proper system operation and renaming, deleting, or modifying
any of these files or folders could cause serious problems with
Raspbian. Please exercise extreme caution following any online
tutorial that asks you to modify any files within these folders.

Lesson 1 – File and Folder Management Page 9

Prepared exclusively for [email protected] Transaction: 5095


COMPLETING FILE TASKS IN THE GUI
As you work more with programs and files, it will be very helpful to keep your files
organized. This may mean creating a new folder to store your project, so that everything
related to that project will be contained in the same folder.

The workflows for file related changes in Raspbian are very similar to ones that you
might already be using within the Windows or Mac operating systems. The "desktop"
area referred to below is the screen area that's visible when Raspbian boots up.

CREATING A FOLDER
To create a new folder (also known as a directory) on the desktop, right-click anywhere
on the desktop, highlight Create New, and select Folder from the list. You will be
prompted to name the new folder. Once a name is added and you click OK, the new
folder will be created on the desktop.

You can view the contents of the folder by double clicking on it using the left mouse
button.

You can now click and drag files into your new folder, or even create other folders inside
your folder. A folder inside of another folder is commonly referred to as a sub-folder.

Lesson 1 – File and Folder Management Page 10

Prepared exclusively for [email protected] Transaction: 5095


CREATING A FILE
New files can also be created inside of a folder. To create a new file, right-click inside
the folder, highlight Create New, and select Empty File. You will be prompted to name
the new file. The type of file will be specified by the file extension, or the part of the file
name after the dot. Common file extensions are text files (.txt) or Python program files
(.py). So, a text file named project would be project.txt or a Python program named test
would be test.py.

Choosing the right file type for your project is important because it will determine which
program will try to open your file. A text file would be good for a list of project parts
because it will automatically open in the text editor, allowing you to edit your list. You
would not want to create a Python program called program.txt as it would be treated by
the OS as a text file and would open in the text editor. You could view the code however
the text editor has no ability to run Python code.

Lesson 1 – File and Folder Management Page 11

Prepared exclusively for [email protected] Transaction: 5095


RENAMING A FILE / FOLDER
To rename a file or folder you will use the same process. Just right-click on the file or
folder, select Rename… from the list of options, and type in the new name. Click OK
and the file / folder will be renamed.

Lesson 1 – File and Folder Management Page 12

Prepared exclusively for [email protected] Transaction: 5095


CHANGING PERMISSIONS ON A FILE / FOLDER
File system permission settings control which users can interact with a file / folder and
what they are allowed to do. You will not generally need to modify permissions, however
some tutorials you find online may require permission changes to files or folders you
download to make programs function properly. Here are the permissions that are
available to be set:

Owner: This will generally be the user that created the file, this user will have ultimate
permission over what happens in the folder and can include/exclude other users access
based on changing the permission settings.

Group: This setting designates a group of users that can have a special set of
privileges for a file. This group can, for example, be given the right to view a file while
other users outside the group are not allowed to view the file.

View content: Also known as read – this permission designates who is allowed to view
or read the file or folder.

Change content: Also known as modify – this permission designates who is allowed to
change or modify the file or folder content.

Access content: Also known as execute – this permission designates who is allowed
to execute or run the file.

To change permissions, right click on the file and select Properties from the menu. Click
on the Permissions tab and the current permissions will be displayed. In this area you
can change the selections for who can read, write, and execute the file. Click OK to
apply your changes to the permission settings or Cancel to exit without saving.

ROOT USER
One user that overrides all of these permission settings is called root. The root user can
read, modify, and execute any file in the whole system by default. The root user is used
by the Raspbian system to make file system changes behind the scenes. While it is
possible to log in as root, it is not advised. Raspbian, like other types of Linux-based
systems, doesn’t display error messages letting you know that you are about to do
something very bad to your file system.

It's possible that while logged in as root you could delete a folder that is critical for
system operation. Your system would then cease to function. The only way to recover
would be to load a new OS, overwriting all of the previous files on the SD card.
Needless to say, you want to avoid this.

Lesson 1 – File and Folder Management Page 13

Prepared exclusively for [email protected] Transaction: 5095


This is why you should remain logged into the Raspberry Pi as user pi. There is a way
to momentarily obtain root user permission levels for specific tasks when it is absolutely
necessary. If you refer back to Level A, Lesson 9 for the discussion concerning the sudo
command, you will find a command structure that allows you to temporarily act as a root
user. The sudo command will allow you to perform file changes as if you were root, so
additional caution is required any time you are using sudo with a command.

DELETING FILES OR FOLDERS


There are two ways to delete files or folders in the GUI. One method is to right-click on
the file, select Move to Wastebasket from the list, then click Yes to move the file to the
wastebasket.

You can also hover over the file, hold down the left mouse button while dragging the file
to the wastebasket, then release the left mouse button while over the wastebasket.
Select Yes when asked for confirmation that you intend for the file to be added to the
wastebasket.

Lesson 1 – File and Folder Management Page 14

Prepared exclusively for [email protected] Transaction: 5095


NAVIGATING FOLDERS USING FILE MANAGER
You can navigate the Raspbian file system graphically using File Manager inside the
GUI. File Manager can be accessed by clicking on the icon of two folders up in the top-
left menu bar.

File Manager will open and display the contents of the /home/pi directory

File Manager will contain two panes of information:

The left pane contains a list of all folders in the file system. This view can also be
referred to as the file tree as subfolders will "branch" out from the main set of folders.
These subfolders can be expanded or collapsed by clicking the + or – symbol next to a
folder. A+ symbol indicates there are subfolders that can be expanded and viewed by
clicking on the + sign. A– sign indicates that subfolders are already expanded, and that
section of the file tree can be collapsed by clicking the – sign.

The right pane contains the contents of any folder that is highlighted in the left pane.
Contents of a folder in the right pane can also be displayed by double-clicking that
folder in the right panel to view its subfolders or files.

Lesson 1 – File and Folder Management Page 15

Prepared exclusively for [email protected] Transaction: 5095


The address bar above the right pane will show a text representation of your current
location in the file tree. This can also be referred to as the "path" to your location. A path
of /home/pi/Desktop/files indicates that you're viewing the contents of a folder named
files, which is inside a folder named Desktop, which is inside a folder named pi, which is
inside a folder named home. It sounds confusing when broken out like this, but it will
make more sense as you start moving around between directories throughout the rest
of this lesson.

The up arrow next to the address bar will take you to the folder above your current
folder. If you're viewing /home/pi/Desktop/files, then clicking the up arrow will take you
to /home/pi/Desktop. Clicking the up arrow again will take you up another level to
/home/pi.

Lesson 1 – File and Folder Management Page 16

Prepared exclusively for [email protected] Transaction: 5095


COMPLETING FILE TASKS USING THE COMMAND LINE
Please refer back to Lesson A-9 where you learned about the Terminal program. The
Terminal program is very powerful and can also be used to make all kinds of file system
modifications, however the lack of a graphical interface makes it slightly more difficult to
work with. As you progress, and your Python programs become more complex, there
will be tasks best competed using the command line rather than the GUI, so it is
important to develop the skills needed to work in the Terminal. The following are
common tasks that can be completed using the command line.

You can access the Terminal program by clicking on in the upper-left menu bar of
your Raspberry Pi.

WORKING DIRECTORY
The working directory is the directory that you're currently working in on the command
line. When first opening Terminal this will be the pi user's home directory or /home/pi.
You can confirm what directory you're currently located in by typing pwd and pressing
the enter key. This command is short for print working directory and it will print your
current location in the file tree.

Lesson 1 – File and Folder Management Page 17

Prepared exclusively for [email protected] Transaction: 5095


LISTING DIRECTORY CONTENTS
Now that you know which directory you're in, it would be helpful to know which files or
folders are in this directory. Typing ls and pressing enter will display the contents from
your current working directory. Folders will be blue with no file extension listed. Files will
be different colors based on their file type which is determined by their file extension.

The ls command is short for the word list. Even though the first character might look
like the number 1 or an upper-case i depending on the font, it’s actually a lower-case L.

CREATING A FILE
To create a file, you can use the Nano command-line text editor:

sudo nano robot.py

This will momentarily escalate your user permissions to a root user, create a new file
named robot.py, and open it in Nano. You can make modifications to the file and when
you’re ready to save, hit CTRL-X (control key and x together), press y to save changes,
confirm the filename, and press enter.

You can also use this same command to edit an existing file. If the file already exists
then it will open in Nano. If the file does not exist, then the new file will be created, and it
will open in Nano.

Lesson 1 – File and Folder Management Page 18

Prepared exclusively for [email protected] Transaction: 5095


CREATING A FOLDER
To create a folder, also known as a directory, you can use the mkdir command:

mkdir testing

This will create a new directory called testing within your working directory.

MOVING TO ANOTHER DIRECTORY


Now that you've listed out the contents of your working directory, you might want to
move into one of those directories. You can do this by typing cd followed by a space,
and then the exact name of the directory as it's listed, then hit enter. cd is short for
change directory.

cd Desktop

Capitalization is important, so desktop is not the same as Desktop. If you mistyped the
directory name or tried to go into a directory that doesn't exist, you will receive a "No
such file or directory" error.

If you really know where you're going you can specify multiple folders in one command

You can use the command cd .. to go up one directory. There must be a space
between cd and .. in order for the command to be recognized.

cd ..

Running this command from inside the /home/pi/Desktop folder will take you to the
/home/pi folder. Running it again from there would take you to /home.

There is a shortcut to get back to user pi's home directory or /home/pi. You can use the
command cd ~ to be taken directly to /home/pi from anywhere in the file system.

cd ~

This command needs a space between cd and the ~ to operate properly.

Lesson 1 – File and Folder Management Page 19

Prepared exclusively for [email protected] Transaction: 5095


COPYING A FILE OR FOLDER
You can copy files by using the cp command followed by the source file and then the
destination file. To make a copy of the file lesson.txt and name the new copy lesson2.txt
you would use the command:

cp lesson.txt lesson2.txt

This will create a copy of lesson.txt, name it lesson2.txt, and drop the file into your
current working directory. This is very handy for making a copy of the file before you
modify the original. For example, let's say you have a program called robot.py and you
want to make some extensive modifications to the code. You could modify your original
file but there won't be a good way to undo the changes if they don't work. You can make
a backup of your original file before modifying robot.py:

cp robot.py robot_original.py

You will now have a perfect copy of your original file. If things go badly with your
changes you can always delete your modified robot.py file, rename robot_original.py
back to its original name, and everything will be back to the way it was before the
changes. We will get into deleting and renaming files in the next few sections.

You can also specify a folder path with the copy command. Let's say that you want to
create the backup file called robot_original.py but you want it to be kept in a subfolder
you created called backups. You would use the following command:

cp robot.py /backups/robot_original.py

This will create a copy of your file and drop it into your backups subfolder.

Copying a folder requires an additional argument after the rm command:

cp -r Folder_Name New_Folder_Name

The -r argument for the cp command stands for recursive, and it means that the copy
command will be applied to the directory and all of its contents. Without the -r
argument, the cp command will attempt to treat the folder as a regular file, and the
command will generate an error.

It's always good to make backup copies of files before you make big changes, so you
can revert the changes if desired. You might also want to periodically keep backup
copies of important programs you've created on a USB drive for safe keeping. The SD
card storage on the Raspberry Pi is fairly reliable, however unplugging power

Lesson 1 – File and Folder Management Page 20

Prepared exclusively for [email protected] Transaction: 5095


unexpectedly or removing the SD card while the Pi is running could cause your file to
become corrupted and having a backup copy on USB could save you a lot of work
recreating your program.

MOVING OR RENAMING A FILE OR FOLDER


In Raspbian there is no specific command to rename a file or folder. Instead the move
command or mv is used to rename files. It works almost exactly like the copy command:

mv robot.py new_robot.py

This will "move" the file to the same directory with the new name of new_robot.py. Just
like the copy command, you can specify a path for the source or destination files. If you
wanted to move robot.py from your current folder into a subfolder called finished, you
would use the following command:

mv robot.py /finished/robot.py

The file will be moved, and you will now have to move into the finished directory using
the cd command to view or work on robot.py.

DELETING A FILE OR FOLDER


Note: Caution must be used when deleting files. Unlike Windows, Raspbian will not ask
if you're absolutely sure you want to delete a critical system file. It will just delete the file
which could cause file system or booting problems. Be very careful when deleting files.

Deleting files can be done with the rm command which is short for remove:

rm robot.py

This will delete the program called robot.py. As stated above, there is no confirmation
for deletion. Once you press enter that file is gone, so be careful.

The command to delete a folder is slightly different. To delete a folder, you must add the
-r argument to the rm command. This lets the rm command know you understand this is
a directory, and its contents will be deleted.

rm -r testing

This command will delete the folder called testing along with any files or subfolders
that it contains.

Lesson 1 – File and Folder Management Page 21

Prepared exclusively for [email protected] Transaction: 5095


OTHER USEFUL COMMAND LINE TOOLS
The command line is useful for a number of functions in Python. Next you will learn
about some of the most common tools.

CONFIRMING RASPBIAN VERSION


Some tutorials you find online might want you to confirm your current version of
Raspbian to ensure compatibility with libraries and other code. Some version numbers
and codenames you might find are:

Version 9 – codename "stretch"

Version 8 – codename "jessie"

Version 7 – codename "wheezy"

You can find your current version by using the following command:

cat /etc/os-release

This will report the version of Raspbian you're currently running on the Raspberry Pi.

TAKING A SCREEN CAPTURE


You might find it useful at some point to take a picture of your Raspberry Pi's screen.
You could do this using a camera, but instead you could use the built-in tool called scrot
(short for screenshot) that will create an image of your screen and save it to your
working directory.

scrot

This command will create a screenshot of your current desktop view and save it to your
working directory with a name generated using the date/time of the capture. You can
also specify a name for the file as an argument:

scrot file.png

This will take a screenshot and save it to your working directory as file.png.

Lesson 1 – File and Folder Management Page 22

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
The following activities will help you practice some of the file and folder management
skills you've learned throughout this lesson.

ACTIVITY #1 – GUI FILE AND FOLDER OPERATIONS


In this activity you will perform some file and folder actions in the GUI.

STEP #1
First, create a new folder on the desktop.

Right click on the desktop and create a new folder named New.

Lesson 1 – File and Folder Management Page 23

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Next, open the new folder in File Manager.

Double click on the new folder and File Manager will open.

Lesson 1 – File and Folder Management Page 24

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Make note of the folder's position in the file tree. It will be located inside of the Desktop
folder.

Right-click in the new folders content pane and create a new empty file called test.txt.

Lesson 1 – File and Folder Management Page 25

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Add some text to the test file.

Double click on the test.txt file and it will be opened in the text area. Add the word test
to the text area and then select File > Save from the menus. Select File > Quit to close
the file.

Entering text into file

Saving test.txt file

Quitting the text editor

Lesson 1 – File and Folder Management Page 26

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
Now delete the test file.

Right-click on the test file and select Move to Wastebasket from the menu. Click on Yes
when prompted for confirmation.

STEP #6
Close File Manager by clicking on the X icon in the right corner of the top menu bar.

Lesson 1 – File and Folder Management Page 27

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Now delete the folder you created in step #1.

Right-click on the folder and select Move to Wastebasket from the menu. Click on Yes
when prompted for confirmation.

Lesson 1 – File and Folder Management Page 28

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – COMMAND LINE FILE AND FOLDER OPERATIONS
In this activity you will perform some file and folder operations using the command-line.

NOTE: The Terminal program pictured below was switched to a white


background instead of the default black background. This was done to keep this
document as printer-friendly as possible, but all commands will work exactly the
same in the Terminal program on your Raspberry Pi.

STEP #1
The first step is to open a Terminal window, so you can use the command-line.

Click on the Terminal icon in the upper-left menu bar to open a terminal window.

STEP #2
Confirm the current working directory.

You can do this by typing pwd and pressing enter. This will confirm that you're located in
the /home/pi directory.

Lesson 1 – File and Folder Management Page 29

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Next, list the contents of your working directory.

This can be done by typing ls and pressing enter.

STEP #4
Now change directories into the Desktop directory.

Type cd Desktop and press enter. Remember that capitalization is important when
referring to directories. Desktop is not the same as desktop.

Lesson 1 – File and Folder Management Page 30

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
Now that you are inside the Desktop directory, create a new folder called projects.

Create this folder by typing the command mkdir projects and pressing enter. Since
you are creating this folder inside your Desktop folder, the new folder will also be visible
on your desktop once it is created.

STEP #6
Next, list the contents of your working directory. This can be done by typing ls and
pressing enter. You will now see a directory called projects.

Lesson 1 – File and Folder Management Page 31

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Change directories into the projects directory by using the cd projects command.

STEP #8
Now, you will create a text file inside your projects folder called test.txt.

Type the command sudo nano test.txt and press enter to create the file and open it
in Nano.

Lesson 1 – File and Folder Management Page 32

Prepared exclusively for [email protected] Transaction: 5095


STEP #9
It's time to add some text to the test file and save the file.

Add here is my text to the text file and press CTRL-X to exit and press y to save
changes. Confirm the filename, and then press enter.

STEP #10
Next, list the contents of your working directory again.

This can be done by typing ls and pressing enter. You should now see your recently
edited test file named test.txt.

Lesson 1 – File and Folder Management Page 33

Prepared exclusively for [email protected] Transaction: 5095


STEP #11
Delete your projects folder and the test text file. You're currently located inside the
projects folder, so it can't be deleted from here.

Type cd .. to move up one directory and press enter. You can confirm your new
location by the command prompt changing from ~/Desktop/projects to ~/Desktop:

STEP #12
Now that you're back in the Desktop directory, use the command rm -r projects to
remove the projects directory and its contents.

You will be prompted for confirmation prior to deletion of each file since you are running
the rm command as the regular user pi, and not using sudo to run the command as root.
This is a much safer alternative to running every command using sudo.

The folder named projects as well as the text file that it contained have been deleted.
You will also notice that the folder has disappeared from your Desktop.

Lesson 1 – File and Folder Management Page 34

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Is it safe to delete or rename important system files or folders?

2. What command is used on the command line to rename a file or folder?

3. Is there a way to confirm which version of Raspbian your Raspberry Pi is


currently running?

Answers can be found on the next page.

Lesson 1 – File and Folder Management Page 35

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Is it safe to delete or rename important system files or folders?

ANSWER: No, system files are critical to the operation of the Raspbian OS and
you should never delete or rename important system files or folders.

2. What command is used on the command line to rename a file or folder?

ANSWER: Renaming a file or folder requires using the move command.

To rename a file you would use mv robot.py new_robot.py.

3. Is there a way to confirm which version of Raspbian your Raspberry Pi is


currently running?

ANSWER: Yes, you can confirm which version you are using with the command
cat /etc/os-release.

Lesson 1 – File and Folder Management Page 36

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to administratively manage files and folders in both the GUI
and Terminal systems. These skills will be important as your Python programs grow
more complex.

In the next lesson, you will learn to work with functions. Functions allow for automation
of tasks and saving space in the code. You will see functions in many projects you work
on going forward.

Lesson 1 – File and Folder Management Page 37

Prepared exclusively for [email protected] Transaction: 5095


LESSON 2

FUNCTIONS

OBJECTIVE
In this lesson you will learn to work with functions. Functions allow for the automation of
tasks and saving space in the code. You will see functions in many projects you work on
going forward.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Program Flow, Strings, Variables, Print Commands, Order (Lesson A-11)


• Code Organization (Lesson A-12)
• Loops (Lesson A-17)

Lesson 2 – Functions Page 38

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will learn about functions. Functions are an important part of Python
programming as they are a great way to automate repetitive tasks and save code
space.

UNDERSTANDING FUNCTIONS
Functions can be used to define a set of commands in a program. You can think of a
function like a list of chores. It would not be very efficient for your parent to tell you to do
every one of your assigned chores. Instead they ask you to do your chores and you
know this means everything on the chore list.

Functions work the same way in a program. You can assign several commands to a
single function and call on that group of commands anytime you need them. This can be
very helpful for keeping your program organized. Since many lines of duplicated code
can be represented by one function, you can call on them as often as you like in your
program, using only one line of code.

Functions follow this format:

def function_name():

command 1

command 2

command 3

In this case, def lets Python know that you are about to define a function,
function_name is the name of our function, and command 1, command 2, and
command 3 will be executed any time this function is called. The function definition must
end in open and close parenthesis and then a colon. Also, the commands contained in
the function must be indented to be considered part of the function.

To call this function in a program you will use function_name(). This is just like using a
variable in your program, in that you must define the function before you try to call it in
your program. If you call the function before defining it, you will have errors. Function

Lesson 2 – Functions Page 39

Prepared exclusively for [email protected] Transaction: 5095


names and calls are also case-sensitive, so the name you define must match the call
name exactly or Python will generate errors.

Here are two examples of this behavior:

Function called before defining Function called after defining


thing() def thing():
print('here is the thing')
def thing(): print('and another thing')
print('here is the thing')
print('and another thing') thing()

Since programs run from top to bottom, This program will run properly since the
this code will generate an error as the function was defined above, or before,
thing function is called before the the function was called by the program.
program has been told what the
statements make up the thing function.

Lesson 2 – Functions Page 40

Prepared exclusively for [email protected] Transaction: 5095


KEEPING CODE ORGANIZED
Generally, module imports and function definitions are inserted at the very beginning of
the program. While they might not be called until much later in a program, its good
practice to keep all the items together, making things much easier to find and modify
later. Consider the following code for example:

Disorganized program layout Organized program layout


import time import time
def thing1(): import RPi.GPIO as GPIO
print('Inside function 1')
GPIO.setmode(GPIO.BCM)
import RPi.GPIO as GPIO
thing1() def thing1():
print('Inside function 1')
def thing2():
print('Inside function 2') def thing2():
print('Inside function 2')
thing2()
GPIO.setmode(GPIO.BCM) thing1()
thing2()

Technically, both layouts will run without errors. Even on the random side, modules are
imported, and functions are defined, prior to being called by the program. This layout
seems simple enough now, however it becomes very problematic as your program gets
more complex.

Imagine 30 or more lines of code separating each of these groups of code on the
random side. If you receive an error that function thing2 has not been defined, where do
you begin to look? Since the code is not organized in a logical manner, you would have
to search through all the code to find the location where thing 2 is being defined and
figure out the problem with that line of code.

If all imports and function definitions happen at the beginning of your program, you won't
have to look far to find thing2. Keeping all imports and function definitions grouped
together at the beginning of your program ensures they will be available when you call
them later in the program, and they can be easily located if there is an issue.

Lesson 2 – Functions Page 41

Prepared exclusively for [email protected] Transaction: 5095


USING FUNCTIONS TO GROUP TOGETHER RELATED PROGRAM BLOCKS
Functions can also be good for keeping a portion of your program grouped together in
one place. You could have setup related commands that all need to be run to configure
things like GPIO pins before they can be used by your program. These setup related
commands could be scattered around the beginning of your program, or you can group
them together in a function:

def setup():

GPIO.setmode(GPIO.BCM)

GPIO.setup(16, GPIO.IN)

GPIO.setup(20, GPIO.OUT)

GPIO.setup(21, GPIO.OUT)

GPIO.output(21, GPIO.OUT)

Later in the program you can call this setup function using setup(). This is a good way
to keep all of your setup related code together for easier modification or troubleshooting
later, if required.

Lesson 2 – Functions Page 42

Prepared exclusively for [email protected] Transaction: 5095


GLOBAL AND LOCAL VARIABLES IN FUNCTIONS
There are two types of variables in Python, global and local. Global variables are named
in the main program and can be used anywhere in the program, including inside
functions. Local variables are named inside of a function and can only be used within
that function.

The variable x being defined as a global x = 5


variable as part of the main program.

The variable x being defined as a local def thing():


variable accessible only while inside the x = 5
thing() function.

Since x is defined in the main program as x = 5


a global variable, it can be printed from
inside the function. This is because
functions can access global variables. def thing():
print(x)

thing()

This code will result in an error that x has def thing():


not been defined. It was defined inside x = 5
the function, however since this variable
is local to the function, it is not available
to the rest of the main program or other thing()
functions.
print(x)

Lesson 2 – Functions Page 43

Prepared exclusively for [email protected] Transaction: 5095


This may seem like odd behavior, but it's Python's way of keeping variables from being
unintentionally modified inside functions. One example might be a loop in your main
program that is using the variable i to keep track of how many times the loop has run.
Part of this loop might be to call a function that also has a loop inside that uses i as the
counting variable. Something like this:

def thing()

for i in range(0,5):

print('output')

for i in range(0,2):

thing()

The result of this code will be the word output being printed 10 times. The main loop
calls the thing function two times, once when i = 0 initially and again when i = 1. The
main loop stops when i = 2 and the program terminates.

The function is also using a loop to print the word output and it's using i as the loop
counter. The loop continues to print until i = 5 and the loop exits back to the main
program. If the value of i was updated globally to be 5 then the main loop would exit
after only one call to the function, since 5 is greater than 2.

The value of i in the main program and the value of i in the function do not interact due
to global vs. local variables, so 10 copies of output are printed to the console.

NOTE: There are ways to define globally accessible variables from within a
function, however that falls outside the scope of this lesson.

Lesson 2 – Functions Page 44

Prepared exclusively for [email protected] Transaction: 5095


FUNCTIONS WITH ARGUMENTS
You can think of a function like a robot that performs pre-programmed tasks. Imagine
you have a robot that is programmed to make a sandwich. It would look something like
this:

def robot():

print(‘piece of bread’)

print(‘peanut butter + jelly’)

print(‘piece of bread’)

Every time you call robot it will make you a peanut butter and jelly sandwich. Someday
you might get tired of peanut butter and jelly, and want some different options, say 20
different options. You could write 20 different functions, each using different names like
robot_pbj or robot_ham, each specifying a different sandwich filling. But there is a
much more efficient way to do this by adding parameters and arguments to the function.

An argument is additional information you can provide when calling a function that can
be used during the execution of that function. The argument is placed inside the
parentheses when calling the function. Continuing with the sandwich example from
above, you could send the robot() function the filling of the sandwich that you would
like by including an argument like robot('salami') or robot('cheese'). This will call
the robot() function while including the argument you supplied. Sending the filling
information from the program when you call the function is referred to as passing an
argument to a function.

The function needs to be configured to accept the argument and this is done with a
parameter. A parameter is added inside the parentheses when a function is defined.
The name of the parameter will determine how the incoming argument will be referred
to while inside the function:

def robot(filling):

The filling variable will now be assigned the value of the inbound argument. Inside
the function you can now use (filling) anywhere that you would like to substitute
your requested ingredient.

Lesson 2 – Functions Page 45

Prepared exclusively for [email protected] Transaction: 5095


def robot(filling):

print(‘piece of bread’)

print(filling)

print(‘piece of bread’)

The filling parameter has been used to determine what will be included in the middle
of the sandwich.

All that’s left is to send the function your choice of filling, or the argument, when calling
the function. Now you can call the function robot using the argument (turkey), and the
function will substitute the string ‘turkey’ in place of (filling).

robot(‘turkey’)

The output from calling the function robot() using the argument ‘turkey’ will be the
following sandwich being printed to the shell:

piece of bread

turkey

piece of bread

Now the robot can make you any kind of sandwich you want, without having to program
in a function for every possibility of sandwich. You can ask for a robot(‘banana’)
sandwich or a robot(‘ham’) sandwich and the robot will know how to make all of
them.

Lesson 2 – Functions Page 46

Prepared exclusively for [email protected] Transaction: 5095


USING FUNCTIONS WITHIN A PROGRAM
You can use this function/argument format to call a function multiple times within a
program. Here, you have a function named print_age and (number) will be used to
represent any values that are passed to this function.

def print_age(number):

print(number)

print_age(12)

print_age(13)

print_age(14)

print_age will call the function print_age and pass it the value in parenthesis. In this
code, the function will be called three times with a different value each time. The result
will be the values 12, 13, and 14 being printed to the console:

12

13

14

In this example, the function isn’t saving us any lines of code, since you could
accomplish the same output using three print statements. Once the function has
multiple lines of code, or is called very often in a program, it will begin to save you large
amounts of code.

Lesson 2 – Functions Page 47

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
The following activities will help you help you exercise some of the function
programming skills you've learned throughout this lesson.

ACTIVITY #1 – SIMPLE FUNCTION PROGRAM


In this activity you will build a program that defines a function that can be called on to
generate a print statement.

STEP #1
Open Thonny and create a new Python program.

STEP #2
Create the function that will contain the print statement. Name the function abc.

def abc():

STEP #3
Add a print statement that will print Here is the function to the console.

Make sure this is code is indented so it is seen as part of the function. Also be sure to
include the colon after abc().

def abc():

print('Here is the function')

Lesson 2 – Functions Page 48

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
The function has now been fully defined. The next step is to create a print statement
that's not included in the function. Print Not part of the function so it's clear which
part of the program is being used to print each statement. Remember to remove the
indentation so Python does not think this statement is part of the function.

def abc():

print('Here is the function')

print('Not part of the function')

STEP #5
Call the function so it can be used to print its own statement. You can do this by using
abc() in the main program:

def abc():

print('Here is the function')

print('Not part of the function')

abc()

Lesson 2 – Functions Page 49

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Add the final print statement. Make the program print Also not part of the
function as the last command.

def abc():

print('Here is the function')

print('Not part of the function')

abc()

print('Also not part of the function')

STEP #7
Run the program. The program will print the following to the console:

Not part of the function

Here is the function

Also not part of the function

This illustrates that the main program is printing one statement, the function is being
called and printing its statement, and then the main program is printing the last
statement.

Lesson 2 – Functions Page 50

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – CALLING A FUNCTION WITH A LOOP
In this activity you will build a program that defines a function and calls it from the main
program multiple times using a loop.

STEP #1
With Thonny open from the last activity, create a new file.

STEP #2
Create a function that can be accessed by calls from the main program. Create a
function called abc() that contains a print statement that will print Here is the
function.

def abc():

print('Here is the function')

STEP #3
Create a loop that will run 10 times. This can be accomplished by using for i in
range(0,10):. 0 is the starting count and the loop will stop when i = 10.

def abc():

print('Here is the function')

for i in range(0,10):

Lesson 2 – Functions Page 51

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Call the function abc from within the main loop. Do this by adding abc() inside the main
loop, making sure to indent.

def abc():

print('Here is the function')

for i in range(0,10):

abc()

STEP #5
Run the program. Here is the function will be printed to the console 10 times.

This illustrates that the main program is calling the function, allowing the function to print
its statement, and returning back to the loop, with i being counted (incremented) each
time the loop runs. When i reaches 10, the loop will exit, and the program will
terminate.

Lesson 2 – Functions Page 52

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – FUNCTIONS WITH PASSED ARGUMENTS
In this activity you will build functions that can accept arguments passed to them by the
main program. This program will greet students of a class and let them know what
topics will be covered in that class.

STEP #1
Open Thonny and create a new Python program.

STEP #2
Create a function named hello that will greet each person with their name and their
location. The function will use the parameters name and city that will be passed as
arguments when hello is called. The body of the function needs to include a greeting
that will print "Hello name from city". The print statement will merge the passed
arguments along with the other strings to complete the sentence:

def hello(name, city):

print ('Hello ' + name + ' from ' + city)

Be sure to include the extra spaces with the words 'Hello ' and ' from ' to properly
space out the sentence. Without them you will get an output like this:
HelloBeckyfromSomewhere

STEP #3
Create a function that will let everyone know what topics will be covered in the class.
Name the function course and use parameters topic1 and topic2 for the arguments
that will be passed when the function is called. The first line of the function will look like
this:

print ('Hello ' + name + ' from ' + city)

def course(topic1, topic2):

Lesson 2 – Functions Page 53

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Create the print statement that will be used when this function is called with arguments.
Make the program say "Today we will be learning about topic1 and topic2",
replacing topic1 and topic2 with the arguments supplied when the function is called.
Remember to indent the print statement and include proper spacing on the strings.

def course(topic1, topic2):

print('Today we will be learning about ' + topic1 + ' and ' + topic2)

STEP #5
Now that the functions have been defined, you can build the main program. All that's left
is to call the functions and pass some arguments. For this example, say you have two
students: Bob from Anytown and Becky from Somewhere. Call the hello function two
times with the information for each student. For the course topics use 'functions' and
'arguments'. Call the course function with those arguments.

print('Today we will be learning about ' + topic1 + ' and ' + topic2)

hello('Bob', 'Anytown')

hello('Becky', 'Somewhere')

course('functions', 'arguments')

Lesson 2 – Functions Page 54

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Run the program. Three lines should be printed; two lines will contain Hello messages
to the two students, and a third that will let them know about the course.

Hello Bob from Anytown

Hello Becky from Somewhere

Today we will be learning about functions and arguments

Here is the fully assembled program in case you need to troubleshoot anything with
word spacing or arguments:

def hello(name, city):

print('Hello ' + name + ' from ' + city)

def course(topic1, topic2):

print('Today we will be learning about ' + topic1 + ' and ' + topic2)

hello('Bob', 'Anytown')

hello('Becky', 'Somewhere')

course('functions', 'arguments')

Lesson 2 – Functions Page 55

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Will a function run as soon as it is defined in the program or does it need to be
called from within the program before the code in the function will be executed?

2. Are function definition names and function call names case-sensitive?

3. Is it better to import modules and define functions at the beginning of your


program or anywhere that's convenient throughout the program?

Answers can be found on the next page.

Lesson 2 – Functions Page 56

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Will a function run as soon as it is defined in the program or does it need to be
called from within the program before the code in the function will be executed?

ANSWER: Defining a function only sets up that function to be used later in the
program. A function must be called from elsewhere within the program before its
block of code will run.

2. Are function definition names and function call names case-sensitive?

ANSWER: Yes, both function definition names and function call names are case
sensitive. A function defined by def Func1(): must be called using Func1().
Attempting to call this function using func1() with a lowercase f will result
program errors

3. Is it better to import modules and define functions at the beginning of your


program or anywhere that's convenient throughout the program?

ANSWER: Module imports and function definitions must happen before they can
be used in a program. Keeping all imports and function definitions near the top of
a program ensures that they will be available throughout the rest of the program
below.

Lesson 2 – Functions Page 57

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to work with functions which are a common way to save
space and promote organization when coding in Python. You are likely to commonly
use functions as you progress in your skills and find new projects to tackle.

In the next lesson you will continue to make progress in your coding skills. Lesson 3 will
have you work with advanced program layout options as well as learn more advanced
ways to use strings when coding.

Lesson 2 – Functions Page 58

Prepared exclusively for [email protected] Transaction: 5095


LESSON 3

PROGRAM LAYOUT OPTIONS AND


ADVANCED STRING CONCEPTS

OBJECTIVE
In this lesson you will learn to build programs that run until manually interrupted, as well
as several advanced techniques for working with strings in programs.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse

REVIEW CONCEPTS

If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Strings, Print Commands, Order (Lesson A-11)


• Code Organization (Lesson A-12)
• Importing Modules (Lesson A-13)
• Loops (Lesson A-17)

Lesson 3 – Program Layout Options and Advanced String Concepts Page 59

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will learn different options for building your main program loops
based on the functionality you need from the program. You will also learn advanced
ways of workings with values saved in a string.

PROGRAM LAYOUT OPTIONS


Until now, the Python programs you've created have followed a very simple structure.
Setup items are at the top, the main program is below, and loops were used if you
wanted something to happen multiple times before exiting. This works fine for simple
programs. However, at some point you will want to branch out to more complicated
programs that require a more complicated program structure.

WHILE TRUE LOOPS


Some programs need to run "forever" which would mean they never end. These
programs can also be referred to as endless loops since they loop forever. Programs
like this might be useful for displaying a message on a screen or monitoring switches for
input changes. There are a couple of ways to accomplish this behavior using a program.

One way would be to create a loop that is always true. Something like:

imports_and_function_definitions

end = 0

while end == 0:

print('Still looping')

The variable end will be set equal to zero. The while loop will compare the value of end
to zero, and it will continue to run as long as they are equal. If nothing inside the loop
modifies the value of end, end will continue to equal zero, and the loop will run forever.

This method works, however Python has a better way to make a loop run forever. It's
called a while True: loop. Creating a loop using while True: will make the loop run
forever without any additional variables:

Lesson 3 – Program Layout Options and Advanced String Concepts Page 60

Prepared exclusively for [email protected] Transaction: 5095


imports_and_function_definitions

while True:

things_to_do_forever

Since a program like this will loop forever, the only way to exit the program is to use the
Stop button in Thonny or CTRL-C. Holding Control and pressing the c key will send an
interrupt signal to the program, causing it to exit. Some programs that are built like this
will print a message about pressing CTRL-C to exit the program:

imports_and_function_definitions

print('Program running – press CTRL-C to exit')

while True:

things_to_do_forever

This is helpful to let the user know that there is no way to exit the program gracefully,
and that CTRL-C is the only option for ending the program. If exiting the program
gracefully is required for your program, then the next section will be helpful.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 61

Prepared exclusively for [email protected] Transaction: 5095


GRACEFUL EXITS: TRY, EXCEPT, FINALLY PROGRAM LAYOUT
The try, except, finally format can be used to control how your program handles
exceptions (like CTRL-C being pressed), and any cleanup operations that need to be
completed before the program exits.

The try block will contain the main part of the program. This will be run automatically
once the program has executed any setup code above it in the program. The except
block will not run unless the program encounters an exception. The finally block is not
required, but if used, it should contain any cleanup operations that need to run before
the program exits.

The except block is often configured to catch keyboard interrupts. You can trigger this
behavior when a program is running by pressing CTRL-C on the keyboard whether the
program was launched from the command-line or Thonny. The stop button in Thonny
will only generate an exception for programs that were launched in Thonny.

The finally block is a good place for the GPIO.cleanup() command if your program is
interacting with GPIO pins. This will ensure that all GPIO pins are returned to their
default state before the program exits.

imports_and_function_definitions

try:

main_part_of_program

except:

things_to_do_in_case_of_an_exception

finally:

things_to_do_before_exiting_the_program

Lesson 3 – Program Layout Options and Advanced String Concepts Page 62

Prepared exclusively for [email protected] Transaction: 5095


The code running inside each of these blocks should be indented to indicate that it's
part of the block.

The try block will not run repeatedly. Just like the rest of the program, it will only run one
time by default. To get the try block to loop repeatedly, you can combine this format with
a while True: loop:

imports_and_function_definitions

try:

while True:

main_part_of_program_that_will_loop_forever

except:

things_to_do_in_case_of_an_exception

finally:

things_to_do_before_exiting_the_program

Indentation is important for this layout to work properly. Each line of the main part of the
loop must be double-indented or 8 spaces to be considered part of the while True:
loop.

The try block is the only required block for this format. The except and finally
blocks are optional. It may make sense to add an except block, a finally block, or both
based on the needs of your program.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 63

Prepared exclusively for [email protected] Transaction: 5095


ADVANCED STRING CONCEPTS
You've already been using strings in print commands and user input, but there are many
other useful things that can be done with strings.

DETERMINING THE LENGTH OF A STRING


The length of a string can be determined by using len() pointed at the string. For
example, the length of a string named test could be attained by:

len(test)

This value could be used for all sorts of things like printing an error if a string is not a
certain length or is too long. Those possibilities might look something like this:

if len(test) < 4:

print('String too short')

if len(test) > 12:

print('String too long')

ACCESSING THE VALUE OF A SPECIFIC STRING POSITION


Specific positions in a string can be accessed by their index position. The index position
is the character's position in the string, starting at zero on the left and increasing in
value as you move to the right, until the end of the string:

The desired index position can be enclosed in square brackets and used to get a
string's value at that index position. If the string above was named test then:

test[1] will return the value in the string named test, index position 1. The value
returned will be 'h'

Lesson 3 – Program Layout Options and Advanced String Concepts Page 64

Prepared exclusively for [email protected] Transaction: 5095


test[4] will return the value in the string named test, index position 4. The value
returned will be 'g'

Say you have multiple strings in your program that all have a unique digit in position 5.
You might want to only print the unique information at index position 5 and not the rest
of the string:

print(test[5])

This will print the value in index position 5 of the string named test. This value in the
example image of 'thing3' would be '3'.

ACCESSING VALUES FROM MULTIPLE STRING POSITIONS


In addition to accessing a single index from a string, it's also possible to access a range
of index positions by specifying the starting and ending index value, separated by a
colon:

test[3:5]

This will return index values 3 through 5 of the string named test. Note that the starting
value will be included, but the ending value will not. The result would be the third and
fourth index positions, excluding the fifth, which will return the string 'ng'.

The example above of returning 'ng' isn't very useful, but that all depends on the data. It
might be more useful for something like grabbing a three-digit employee number from a
string, and comparing it to a known value:

id = 'employee042'

if id[8:11] == '042':

print('Access Granted')

This code will grab the index digits 8, 9, and 10 from the string named id and compare it
to the string '042'. If the two are equal, then Access Granted will be printed to the
console. If they are not equal, then nothing will be printed to the console.

Perhaps your employee numbers range from 'employee1' to 'employee9999'. With


varying string lengths, it might be difficult to specify the proper ending index value. The
solution for this is to use the same format as above but omit the ending index value.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 65

Prepared exclusively for [email protected] Transaction: 5095


Python will automatically return the rest of the string, regardless of how many
characters are after the specified starting index position:

id = 'employee0421'

if id[8:] == '042':

print('Access Granted')

The if statement above will check to see if index positions 8 and above are equal to
'042'. In this case '0421' does not equal '042' so Access Granted will not be printed.
If id[8:11] were being used to do this check then only the first three digits of the
employee number would be checked, which would match, and employee0421 would be
given access permission reserved for employee042.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 66

Prepared exclusively for [email protected] Transaction: 5095


ACCESSING VALUES IN A LIST USING THEIR INDEX POSITION
The items in a list are also assigned the index value positions:

things = ['apple', 42, 'blue', 7]

index position 0 of things = 'apple'

index position 1 of things = 42

index position 2 of things = 'blue'

index position 3 of things = 7

These items are accessed just like the strings in the last section. By using the name of
the list along with the index position of one of its items, you can get the entire value
contained in that position of the list:

things[0] = 'apple'

things[3] = 7

The same format applies for multiple positions in a list as well:

things[2:4] = ['blue', 7]

things[1:] = [42, 'blue', 7]

Lesson 3 – Program Layout Options and Advanced String Concepts Page 67

Prepared exclusively for [email protected] Transaction: 5095


UPPERCASE AND LOWERCASE
Another way to interact with strings is to print them in all uppercase or lowercase. You
can do this with the .upper() and .lower() commands. To print a string named
alpha in all uppercase you can use print(alpha.upper()). All lowercase is much
the same with print(alpha.lower()).

alpha = 'Here is a Sentence with Mixed Case'

print(alpha.upper())

print(alpha.lower())

Printing with these case modifications will not modify the original alpha string. You can
save a new uppercase copy of the string by using something like:

alpha = 'Here is a Sentence with Mixed Case'

upper_case = alpha.upper()

This new variable named upper_case will contain a copy of the sentence that has been
converted to all uppercase letters:

upper_case = 'HERE IS A SENTENCE WITH MIXED CASE'

Lesson 3 – Program Layout Options and Advanced String Concepts Page 68

Prepared exclusively for [email protected] Transaction: 5095


REPLACING CHARACTERS IN A STRING
At some point you may want to print a string, while replacing one word for another. This
can be accomplished by the .replace() command. Consider the following string:

hello = “Welcome name, it's good to see you name!”

It's much better to refer to people by their actual name rather than calling them "name".
You can personalize this statement during printing by replacing the word name with
someone's actual name:

hello = “Welcome name, it's good to see you name!”

print(hello.replace('name', 'Bob'))

This print statement will find the string named hello, locate any instances of the word
name, replace those with Bob, and print the new string. Remember that the original
string hello will not actually be modified, only printed with the requested replacements. If
you wanted to save a personalized greeting, you could create a new string:

hello = “Welcome name, it's good to see you name!”

greeting_bob = hello.replace('name', 'Bob')

You now have a personalized greeting saved as a new string that can be used for
whatever you need.

NOTE – In Python, a string can be enclosed by single-quotation marks (‘) or


double-quotation marks (“). A single-quotation mark happens to be the same
character as an apostrophe, so words in your string like it’s or that’s can cause
problems if you’re enclosing your string in single-quotation marks. Python will
interpret the apostrophe as the end of the string, and it will not know what to do
with the remainder of the string. In these cases, enclosing your string in double-
quotation marks will allow Python to properly interpret the apostrophe as part of
the string.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 69

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities, you will build programs using the program layout options
introduced in this lesson, as well as build a program that will demonstrate some string
manipulation techniques.

ACTIVITY #1 – WHILE TRUE LOOP


In this activity you will use a while True loop that will print a statement once per second,
until you click the stop button.

STEP #1
Open Thonny and create a new program.

STEP #2
The time module will need to be imported so you can add a delay between statements
being printed. Import the time module:

import time

STEP #3
Since you need this program to loop forever, create a while True: loop. Don't forget
the colon after True. Also, True must be capitalized to be valid.

import time

while True:

Lesson 3 – Program Layout Options and Advanced String Concepts Page 70

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Add a print statement to the loop that will print Still looping every time the loop runs.

Remember to indent the print statement so it will execute as part of the while True:
loop.

import time

while True:

print('Still looping')

STEP #5
The way the loop is currently configured would print far out too fast to see. Add a one
second time.sleep delay to the loop to slow it down.

Make sure the sleep command is indented so it's executed as part of the while True
loop.

import time

while True:

print('Still looping')

time.sleep(1)

STEP #6
Run the program. You will see Still looping printed to the console once every
second. Since this is an endless loop, this program will run forever. Press the stop
button in Thonny to end the program. The console will print an error message when the
program is stopped manually.

Leave the program open to use in the next activity.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 71

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – ERROR HANDLING PROGRAM
In this activity you will modify the program from Activity #1 to add error handling
behavior.

STEP #1
You will be adding the try, except, and finally program blocks to the program from
Activity #1. With the program from Activity #1 open, insert a carriage return above the
while True loop, and add the try: block above the while True loop.

Make sure to add appropriate indentation to the while True loop and its contents. After
the changes it should look like this:

import time

try:

while True:

print('Still looping')

time.sleep(1)

Lesson 3 – Program Layout Options and Advanced String Concepts Page 72

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
You need an except block to let the program know what to do in case an exception is
encountered, like stopping the program manually.

Add an except block below the time.sleep command that will catch keyboard
interrupts.

import time

try:

while True:

print('Still looping')

time.sleep(1)

except KeyboardInterrupt:

This will configure the except block to only execute if a specific type of exception
occurs, a keyboard interrupt exception.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 73

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The except block now needs an activity to execute when it gets triggered. Add a print
statement that lets you know when a keyboard exception has occurred.

import time

try:

while True:

print('Still looping')

time.sleep(1)

except KeyboardInterrupt:

print('Stop or CTRL-C was pressed')

This will print Stop or CTRL-C was pressed if a keyboard interrupt exception is
encountered.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 74

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
While the program will run as it is now, add a finally block to show how it runs when
exiting a program. This will be more important once you use this format to build
programs to interact with GPIO pins, but for now, just add another print statement.

Add a finally block with a print statement that will let you know when the program is
ending:

import time

try:

while True:

print('Still looping')

time.sleep(1)

except KeyboardInterrupt:

print('Stop or CTRL-C was pressed')

finally:

print('Program is ending')

STEP #5
Run the program. It will print Still looping at one second intervals. Press CTRL-C or
the stop button in Thonny.

The keyboard exception will be triggered, and CTRL-C was pressed will be printed.
After the except block runs the finally block will be triggered and Program is ending
will be printed.

No Python error messages should be printed to the console since the program has
been instructed on what to do in case of a keyboard exception.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 75

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – PRACTICE WITH STRINGS
In this activity you will practice the string commands from this lesson by making a
program that will manipulate employee numbers and a company name.

STEP #1
In Thonny, create a new program.

Start the program off by adding some user and employee numbers that we can use
throughout the program.

Create three variables named user1 through user3 that contain employee numbers
123, 456, and 789:

user1 = 'Employee123'

user2 = 'Employee456'

user3 = 'Employee789'

STEP #2
Create a generic welcome message.

Create a variable called message that contains the string 'Welcome to the company':

user1 = 'Employee123'

user2 = 'Employee456'

user3 = 'Employee789'

message = 'Welcome to the company!'

Lesson 3 – Program Layout Options and Advanced String Concepts Page 76

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Find out if the word employee is capitalized or not in the string held by user1. You can
do this by printing the first character, or index value 0, from the string user1:

user1 = 'Employee123'

user2 = 'Employee456'

user3 = 'Employee789'

message = 'Welcome to the company!'

print(user1[0])

Lesson 3 – Program Layout Options and Advanced String Concepts Page 77

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Next, strip the employee number digits out of the strings held by user1, user2, and
user3.

Using the variables e_num1 through e_num3, set each equal to index values 8 through
10 of the corresponding user string value. e_num1 should pull from user1, and so on.

Remember that the ending index value in the range will not be included, so [8:10] will
only include digits 8 and 9. To include 10 your range must be [8:11].

user1 = 'Employee123'

user2 = 'Employee456'

user3 = 'Employee789'

message = 'Welcome to the company!'

print(user1[0])

e_num1 = user1[8:11]

e_num2 = user2[8:11]

e_num3 = user3[8:11]

Lesson 3 – Program Layout Options and Advanced String Concepts Page 78

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
You now have the 3-digit employee numbers stored as e_num1 through e_num3.

Create a print statement that will list all three of the employee numbers by printing
'Employee numbers are ' and adding e_num1 through e_num3.

Make sure to include proper punctuation like commas and the word 'and' before the
last value in the list:

user1 = 'Employee123'

user2 = 'Employee456'

user3 = 'Employee789'

message = 'Welcome to the company!'

print(user1[0])

e_num1 = user1[8:11]

e_num2 = user2[8:11]

e_num3 = user3[8:11]

print('Employee numbers are ' + e_num1 + ', ' + e_num2 + ', and ' + e_num3)

Lesson 3 – Program Layout Options and Advanced String Concepts Page 79

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Print a couple of statements that will use the string stored in the message variable.

For the first message, print 'WELCOME TO THE COMPANY!' in all uppercase using the
.upper() command with the message string as input.

Next, print the message, but replace 'the company' with '42 Electronics':

user1 = 'Employee123'

user2 = 'Employee456'

user3 = 'Employee789'

message = 'Welcome to the company!'

print(user1[0])

e_num1 = user1[8:11]

e_num2 = user2[8:11]

e_num3 = user3[8:11]

print('Employee numbers are ' + e_num1 + ', ' + e_num2 + ', and ' + e_num3)

print(message.upper())

print(message.replace('the company', '42 Electronics'))

Lesson 3 – Program Layout Options and Advanced String Concepts Page 80

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Run the program. The following should be printed to the console:

Employee numbers are 123, 456, and 789

WELCOME TO THE COMPANY!

Welcome to 42 Electronics!

If your output does not look like this, find the code that's generating the problematic
output and make sure it matches the program from Step #6 exactly.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 81

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Which method below is a better way of creating an endless loop and why?

a. x = 0
while x == 0:
keep_doing_this_forever

b. while True:
keep_doing_this_forever

2. Is the finally: block required in a program using the try and except blocks?

3. What command would you use to check if index position 4 of a string named
test is equal to 'z'?

Answers can be found on the next page.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 82

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Which method below is a better way of creating an endless loop and why?

a. x = 0
while x == 0:
keep_doing_this_forever

b. while True:
keep_doing_this_forever

ANSWER: Option B. The while True loop is the better choice as it doesn’t
require the program to look for a specific value to continue to loop.

2. Is the finally: block required in a program using the try and except blocks?

ANSWER: No, only the try block is required. Whether the except and finally
blocks are used just depends on the program and the functionality you require.

3. What command would you use to check if index position 4 of a string named
test is equal to 'z'?

ANSWER: You would use the conditional statement:

if test[4] == 'z':

This will check index position 4 in the string named test to see if it's equal to z. If
it is, the indented code block below this conditional statement will run.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 83

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to set up more advanced programs including how to format
programs to run forever (or until they are manually interrupted) using the try, except,
finally program layout. You also learned to some more advanced techniques for working
with strings in programs.

In the next lesson, you will switch gears a bit and learn to work with pulse width
modification including when and how to use it in programs.

Lesson 3 – Program Layout Options and Advanced String Concepts Page 84

Prepared exclusively for [email protected] Transaction: 5095


LESSON 4

IMPORT METHODS AND


PULSE WIDTH MODULATION

OBJECTIVE
In this lesson, you’ll learn new ways to import modules, as well as how to use Pulse
Width Modulation (PWM) which allows a GPIO pin to behave somewhat like an analog
output.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse


• 1 x Breadboard
• 1 x Ribbon Cable
• 1 x Wedge
• 1 x 220-ohm Resistor
• 1 x LED
• 4 x Short Jumper Wires**
• 1 x Piezo Speaker

**Please note, you received two types of jumper wires in your parts kit: male-to-male
and male-to-female. In Lesson 17 you will use the male-to-female jumper wires. For all
other lessons (including this one) you will use the male-to-male jumper wires.

Lesson 4 – Import Methods and Pulse Width Modulation Page 85

Prepared exclusively for [email protected] Transaction: 5095


REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Importing Modules, Module Commands (Lesson A-13)


• Using a ribbon cable and wedge to attach the Raspberry Pi to the breadboard;
Configuring GPIO Pins (Lesson A-16)
• Loops (Lesson A-17)

Lesson 4 – Import Methods and Pulse Width Modulation Page 86

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will learn to recognize some different import methods, as well as
when and how to use pulse width modulated outputs in your programs.

IMPORT METHODS
Until now, the import methods in your programs have been limited to standard imports
like import time or import RPi.GPIO as GPIO. These will be explained in depth
below as well as a few new import concepts.

STANDARD MODULE IMPORT

import time

This statement will import all functions and information contained in the module named
time. The functions within this module can be called in your program by using the
module_name.function format. Sleep is a function contained within the time module so it
is called by using the time.sleep statement followed by the value in seconds that you
wish to pass to the time.sleep function. For example, time.sleep(1) will execute the
sleep() function within the time module. The value of 1 will be passed to the sleep()
function which will result in a program delay of 1 second.

Lesson 4 – Import Methods and Pulse Width Modulation Page 87

Prepared exclusively for [email protected] Transaction: 5095


IMPORTING A MODULE USING AN ALIAS

import RPi.GPIO as GPIO

This statement will import the RPi.GPIO module, just like the first example above, but
the inclusion of as will create an alias, or secondary name, called GPIO for the
RPi.GPIO module. Calls to the RPi.GPIO module can now be simplified to just GPIO.
This is why you will find statements like GPIO.setup or GPIO.output in programs that
import the RPi.GPIO module as GPIO.

This can be handy when a module has a very long name like module_doing_things. If
module_doing_things contained a function called thing1 then calling that function
would require a long statement like module_doing_things.thing1(). Instead you can
give this function an alias on import:

import module_doing_things as module1

Now that you have created an alias called module1 for the module, you can use
module1.thing1() instead and the program will call the same function, while using a
shorter name.

Lesson 4 – Import Methods and Pulse Width Modulation Page 88

Prepared exclusively for [email protected] Transaction: 5095


IMPORTING MULTIPLE MODULES
In all programs you have created until this point, the module imports have been on their
own line:

import time

import random

import os

This is a good way to keep your code organized, but there is a way to combine these
into one line using commas between items:

import time, random, os

These are two different ways of importing multiple modules, and Python will treat both
exactly the same. Which format you choose to use is entirely up to you, but you may
encounter either of these formats in programs you find around the internet, so it's good
to understand that they are interchangeable.

Lesson 4 – Import Methods and Pulse Width Modulation Page 89

Prepared exclusively for [email protected] Transaction: 5095


IMPORTING SPECIFIC FUNCTIONS FROM A MODULE
When you import a module using the import module1 statement, your program will
have access to everything in that module. To access these functions, you must specify
the name of the module and function using dot notation or module1.thing1().

Another option to simplify your code is to import specific parts of a module using the
from module import specific_element format. Using the same module and
function names from above, this example would be:

from module1 import thing1

This will make the function named thing1 available as if it was defined as part of your
program. Since it was imported using this method, the module name can be omitted
when the function is called within your program:

thing1()

This can simplify your programs and make it easier to call frequently used functions
without having to include the module name.

Some programs may import everything defined in a library using the asterisk (*):

from module1 import *

This will make every function definition available locally in the program without
specifying the module name, however this can cause problems if a function is defined in
your program and also in the module. For example:

If module1.py contains:

def setup():

print('Setup from module1')

Lesson 4 – Import Methods and Pulse Width Modulation Page 90

Prepared exclusively for [email protected] Transaction: 5095


And your local program contains:

from module1 import *

def setup():

print('Setup from local program')

All function definitions in module1.py will be imported and made local using the from
module1 import * statement, including the function called setup. However, your local
program already includes a definition for a function called setup.

When you call the setup function later in the main program using setup(), the definition
from the module will be ignored and your local definition of setup() will be used. This
could cause parts of your program to break if they were relying on setup information that
was intended to be passed in from module1.

If you run into any odd issues when working with programs that import modules, check
that there isn't a conflict of function names between your program and the modules that
you're importing. If there is, you can always rename the functions in your program to
eliminate the conflicts.

Lesson 4 – Import Methods and Pulse Width Modulation Page 91

Prepared exclusively for [email protected] Transaction: 5095


ANALOG SIGNALS VS DIGITAL SIGNALS
The Raspberry Pi output pins are limited to two states, high (3.3V) or low (ground). This
type of on/off behavior is considered a digital signal. This type of signal is good for
turning things on and off and using very fast switching between on and off can allow
some types of different devices to communicate. This digital technology is what allows
data from the internet to be sent into your home over a single copper or fiber optic
cable.

While digital signals are very useful, there are some jobs they are not very good at, like
driving speakers. The on/off nature of digital signals do not mix well with the smooth
signal transitions required to drive a speaker. To do this well, you need an analog
signal. You will dive deeper into the differences between analog and digital signals in
Lesson 8.

Unfortunately, the Raspberry Pi does not have the capability to input or output analog
signals using its GPIO pins. The good news is that there are pieces of hardware that
can be paired with the Pi to provide this functionality, and you will explore some of those
in later lessons.

Lesson 4 – Import Methods and Pulse Width Modulation Page 92

Prepared exclusively for [email protected] Transaction: 5095


PULSE WIDTH MODULATION (PWM)
While the Raspberry Pi does not have true analog output, there is a way to make a
GPIO pin behave somewhat like an analog output. This method is called pulse width
modulation or PWM. PWM signals can be used to dim an LED or make buzzing sounds
using a piezo-electric speaker. PWM is accomplished by turning a digital signal on and
off very quickly.

This is similar to Activity #3 in Level A, Lesson 16 where you controlled an LED with a
GPIO pin. The Raspberry Pi can turn GPIO pins on and off so fast that you had to add
delays between turning the LED on or off, to allow the LED time to react to the changes:

Without Delays With Delays


GPIO.output(27,GPIO.HIGH) GPIO.output(27,GPIO.HIGH)
GPIO.output(27,GPIO.LOW) time.sleep(1)
GPIO.output(27,GPIO.HIGH) GPIO.output(27,GPIO.LOW)
GPIO.output(27,GPIO.LOW) time.sleep(1)
GPIO.output(27,GPIO.HIGH)
time.sleep(1)
GPIO.output(27,GPIO.LOW)

These one second delays add enough time for the LED to react to the change in state
on the GPIO pin and turn on or off. Without this delay the GPIO pins can reportedly
switch at speeds up to 70MHz. That means changing states every 0.0000000143
seconds or 14.3 nanoseconds. The LED may be fast enough to begin to turn on, but not
enough electrons will flow through the LED to generate light before the pin is low again.
This results in the LED appearing to stay off despite the GPIO pin going high twice in
this program.

The two states of an LED are fully on and fully off, but if you turn it on and off fast
enough, you can simulate brightness levels in between. This is the basic concept
behind PWM.

Lesson 4 – Import Methods and Pulse Width Modulation Page 93

Prepared exclusively for [email protected] Transaction: 5095


PWM DUTY CYCLE AND FREQUENCY
PWM signals are based on two factors: duty cycle and frequency.

Duty cycle is measured in percent and refers to how much time the signal spends high
over a given period of time. The period of time is based on the frequency, measured in
hertz (HZ), which is how many times per second the signal switches between high and
low.

If, over a one second period, a signal transitions only one time from positive to negative,
back to positive, then that signal has a frequency of 1Hz:

If, over a one second period, a signal transitions 10 times from positive to negative, and
back to positive, then that signal has a frequency of 10 Hz:

Lesson 4 – Import Methods and Pulse Width Modulation Page 94

Prepared exclusively for [email protected] Transaction: 5095


The period of the signal, measured in seconds, can be obtained by dividing 1 by the
frequency in Hz:

Now that you have the period of the signal, the PWM duty cycle will determine what
percentage of the time the signal will be high during that period. The duty cycle can be
0% (always low), 100% (always high), or any percentage in between:

The PWM frequency used will be determined by the specific device you are trying to
control. LEDs may work at 30hz, but there might be some noticeable flickering based on
your specific LED, so a higher frequency may be required. The duty cycle can be
adjusted to obtain the desired brightness.

Due to the digital nature of PWM signals, they should not be used to drive standard
speakers. Piezoelectric speakers (piezos for short) are a specially designed type of
speaker with a tiny coil that can move very quickly to produce audio tones. Piezo
speakers are often found on PC motherboards and are used to generate bootup and
system error beeps. Piezos generally sound best with a 50% duty cycle to keep the
high/low timing equal, but the frequency can be adjusted to obtain the desired sound.

Lesson 4 – Import Methods and Pulse Width Modulation Page 95

Prepared exclusively for [email protected] Transaction: 5095


You may also see a different type of piezo called a buzzer. A buzzer is different than a
standard piezo in that a piezo must be driven by a PWM signal. Buzzers contain
circuitry inside that will generate the PWM signal, so all that needs to be supplied is
voltage. This built-in PWM generation make it easier to use than a speaker, but since
the PWM is built into the buzzer, the frequency or duty cycle cannot be adjusted. This
means that the buzzer can only make one sound, and the only control you have is
voltage or no voltage, on or off.

HARDWARE PWM VS SOFTWARE PWM


Hardware controlled PWM means that an integrated circuit (IC) in the form of a
processor or timer is generating the PWM signal. Using this approach, the Python
program sends the IC the frequency and duty cycle of the desired PWM signal and the
hardware takes care of the rest. The Raspberry Pi only has one GPIO pin that can be
configured as a hardware controlled PWM output and it's located at GPIO18.

Another option is software controlled PWM. Software controlled means that a module or
program is in charge of turning GPIO pins on and off to generate a PWM signal. The
benefit to this is that multiple GPIO pins can be configured as PWM outputs. One
drawback is that software controlled PWM can consume extensive CPU resources, and
depending on the complexity of your program, this additional CPU usage could cause
problems. That being said, lots of programs use software controlled PWM without any
problems, and they run well due to the CPU (Central Processing Unit) resources
available on the Raspberry Pi.

Lesson 4 – Import Methods and Pulse Width Modulation Page 96

Prepared exclusively for [email protected] Transaction: 5095


SOFTWARE PWM CONTROL
The RPi.GPIO module used throughout previous lessons already has built-in
commands for generating PWM signals, so we will continue to use RPi.GPIO for
programs that need PWM signals. Here are some of the PWM-specific commands from
the RPi.GPIO module:

pwm = GPIO.PWM(channel, frequency)

Creates a variable named pwm and set it equal to a command that will let the RPi.GPIO
module know that you intend to use this pin for PWM. The channel is the pin number
and the frequency is the desired frequency of the PWM signal in hertz. Any PWM
commands that refer to this channel will start with pwm. as seen below.

pwm.start(duty_cycle)

Starts the PWM signal on the pin specified in the GPIO.PWM command above.
duty_cycle is the desired percentage of time the signal should be high. This value can
be 0, 100 or anywhere in between.

pwm.ChangeFrequency(new_frequency)

This command can be used to change the frequency of a PWM that is currently running.
new_frequency represents the new value for the PWM frequency in hertz.

pwm.ChangeDutyCycle(duty_cycle)

This command can be used to change the duty cycle of a PWM that is currently running.
duty_cycle represents the new value for the PWM duty cycle.

Lesson 4 – Import Methods and Pulse Width Modulation Page 97

Prepared exclusively for [email protected] Transaction: 5095


pwm.stop()

Stops the PWM output from the pin specified when the variable of pwm was declared.

The pwm variable we selected above can be any variable, as long as it's referenced
when controlling that channel later in the program. If you have multiple PWM pins then
you might want the variables to me more descriptive. If PWM output is required on
GPIO19 and GPIO26 then you might assign descriptive variables like pwm_19 or
pwm_26. The configuration and control commands would then begin with pwm_19 or
pwm_26, depending on the pin you want to control:

pwm_19 = GPIO.PWM(19, 10) Creates pwm_19 to control GPIO19 with a


frequency of 10Hz

pwm_26 = GPIO.PWM(26, 5) Creates pwm_26 to control GPIO26 with a


frequency of 5Hz

pwm_19.start(50) starts pwm_19 with a duty cycle of 50%

pwm_26.start(25) stops pwm_26 with a duty cycle of 25%

pwm_19.ChangeFrequency(20) Changes frequency of GPIO19 to 20Hz

pwm_26.ChangeDutyCycle(35) Changes the duty cycle of GPIO26 to 35%

Using these commands, you will be able to control multiple devices using PWM signals.

Lesson 4 – Import Methods and Pulse Width Modulation Page 98

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will practice importing modules as well as build multiple
programs that use PWM signals to control devices.

ACTIVITY #1 – BUILDING AN LED AND PIEZO CIRCUIT


In this activity you will build a circuit with an LED and piezoelectric speaker to use for
creating PWM programs. Please note, circuits built from this point forward will have less
explanation included in the instructions. You no longer need as lengthy of instructions
due to all the previous experience you’ve had.

STEP #1
Connect the wedge to breadboard rows 1-20. Connect ground from J3 to ground rail at
N2-3.

Lesson 4 – Import Methods and Pulse Width Modulation Page 99

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
This step will add a piezo speaker to the circuit on GPIO19. Add the following
components:

Short jumper wire between A18 and A31

Piezo speaker between E31 and F31

Short jumper wire between J31 and N2-3

Lesson 4 – Import Methods and Pulse Width Modulation Page 100

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Add an LED to the circuit on GPIO26. Add the following components:

Short jumper wire between A19 and A37

LED between E37 and F37. Make sure to insert the cathode (shorter lead) of the LED
into F37 so that it's correctly polarized

220-Ohm resistor between J37 and N2-37

STEP #4
You will now test the circuit. Verify all connections are correct per the image in Step #3.
If not already connected, attach the ribbon cable to the wedge and power up the
Raspberry Pi.

Lesson 4 – Import Methods and Pulse Width Modulation Page 101

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
Now you will build a program that will test the piezo speaker and LED. Open Thonny to
create a new program. In your program, import the time and RPi.GPIO modules on the
same line, set the pin mode to BCM, and configure pins 19 and 26 as outputs:

import time, RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

GPIO.setup(19, GPIO.OUT)

GPIO.setup(26, GPIO.OUT)

The initial program setup is now complete.

STEP #6
The next step will be to test the LED on GPIO26. Add a few lines of code that will push
GPIO26 high, wait one second, and pull it back low. Don't forget to add a cleanup
command at the end of the program.

import time, RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

GPIO.setup(19, GPIO.OUT)

GPIO.setup(26, GPIO.OUT)

GPIO.output(26, GPIO.HIGH)

time.sleep(1)

GPIO.output(26, GPIO.LOW)

GPIO.cleanup()

Lesson 4 – Import Methods and Pulse Width Modulation Page 102

Prepared exclusively for [email protected] Transaction: 5095


Run your program to ensure that the LED turns on for one second and turns back off. If
it doesn’t, then double-check the LED polarity, all connections on that circuit branch,
and your program. Don’t proceed to the next step until the LED is turning on properly.

STEP #7
Next, add some code to test the piezo speaker. Since the piezo only makes sound
when it's rapidly transitioning from low to high or from high to low, a simple on/off test
like the LED won't work. You will need to send the piezo a PWM signal to hear any
sound.

Insert the following code between the LED test code and the cleanup command.
Comments have been added to explain each line of PWM code:

GPIO.output(26, GPIO.LOW)

pwm_19 = GPIO.PWM(19, 300) #assigns PWM signal to pin 19 at 300Hz

pwm_19.start(50) #starts PWM on pin 19 at 50% duty cycle

time.sleep(0.5) #program sleeps for .5 seconds

pwm_19.stop() #stops PWM output on pin 19

GPIO.cleanup()

Lesson 4 – Import Methods and Pulse Width Modulation Page 103

Prepared exclusively for [email protected] Transaction: 5095


Once this code is inserted, run your program. Your program should turn on the LED for
one second and turn it back off, then the piezo should produce a 300Hz tone for 0.5
seconds. If the piezo doesn't make sound, double-check all connections on that circuit
branch, and your program. Don’t proceed to the next activity until the piezo and LED are
both operating properly. Here is the fully assembled program for reference:

import time, RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

GPIO.setup(19, GPIO.OUT)

GPIO.setup(26, GPIO.OUT)

GPIO.output(26, GPIO.HIGH)

time.sleep(1)

GPIO.output(26, GPIO.LOW)

pwm_19 = GPIO.PWM(19, 300) #assigns PWM signal to pin 19 at 300Hz

pwm_19.start(50) #starts pin 19 at 50% duty cycle

time.sleep(0.5) #program sleeps for 0.5 seconds

pwm_19.stop() #stops PWM output on pin 19

GPIO.cleanup()

Lesson 4 – Import Methods and Pulse Width Modulation Page 104

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – CONTROLLING AN LED WITH PWM
In this activity you modify the program from Activity #1 to add PWM control to the LED
signal to allow dimming of the LED.

STEP #1
First, you will need to remove the block of code that is currently controlling the LED on
GPIO26. Remove these lines of code from the program in Activity #1:

GPIO.output(26, GPIO.HIGH)

time.sleep(1)

GPIO.output(26, GPIO.LOW)

STEP #2
Next, you will need to configure pin 26 as a PWM pin. Insert the following code in place
of the old LED code:

GPIO.setup(26, GPIO.OUT)

pwm_26 = GPIO.PWM(26, 20) #assigns PWM signal to pin 26 at 20Hz

pwm_26.start(10) #starts PWM on pin 26 at 10% duty cycle

time.sleep(2) #program sleeps for 2 seconds

pwm_26.stop() #stops PWM output on pin 26

pwm_19 = GPIO.PWM(19, 300)

Run your code to verify that the LED flashes very quickly for two seconds, and then
runs the piezo for .5 seconds.

Lesson 4 – Import Methods and Pulse Width Modulation Page 105

Prepared exclusively for [email protected] Transaction: 5095


You might notice that the LED frequency and duty cycle values are very different than
the piezo. This is due to the response characteristics of LEDs. Frequency values above
around 30Hz will make the LED appear to stay on continually even though it's being
driven by a signal that is turning on and off very quickly. Piezo speakers require higher
frequencies to produce tones that are audible to humans. Low frequencies in a piezo
speaker will sound like very quiet clicks as it transitions between high and low.

STEP #3
To demonstrate the effect of dimming the LED, let's modify the values of the LED PWM
signal. Change the frequency from 20Hz to 50Hz so the LED will appear to be on all of
the time. Also change the duty cycle from 10% to 1%. Those settings should result in a
pretty dim LED. Here is the code after the changes:

pwm_26 = GPIO.PWM(26, 50)

pwm_26.start(1)

time.sleep(2)

pwm_26.stop()

Run the code to verify the LED is dim as expected. If not, double-check your code for
any problems.

Lesson 4 – Import Methods and Pulse Width Modulation Page 106

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – ADJUSTING LED BRIGHTNESS
In this activity you will modify the program from Activity #2 to sweep the LED through
brightness levels between fully off to fully on. Due to the natural refresh rate of the
human eye, LED duty cycles above around 50% will be imperceptible to us. For this
reason, sweeping the duty between 0 and 100 for this project will cause the brightness
to appear to hang at the higher duty cycles. Therefore, this activity only sweeps
between 0% and 50% duty cycle, and not the full 100% range, while still achieving full
brightness.

STEP #1
First, simplify the program from Activity #2 by removing the blocks controlling the LED
and piezo. The program should now be only imports, setmode, setups, and cleanup:

import time, RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

GPIO.setup(19, GPIO.OUT)

GPIO.setup(26, GPIO.OUT)

GPIO.cleanup()

Lesson 4 – Import Methods and Pulse Width Modulation Page 107

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Next, you will set the frequency and channel for the PWM. Set up the variable pwm_26
equal to GPIO.PWM with a channel of 26 and frequency of 50hz. Below that, start
pwm_26 with a duty cycle of 0% (always off):

import time, RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

GPIO.setup(19, GPIO.OUT)

GPIO.setup(26, GPIO.OUT)

pwm_26 = GPIO.PWM(26, 50)

pwm_26.start(0)

GPIO.cleanup()

Lesson 4 – Import Methods and Pulse Width Modulation Page 108

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The next step will be to vary the duty cycle to increase and decrease the brightness.
You will do this using two different for loops; one to increase the brightness and the
other to decrease the brightness. A variable called b will be used to represent the duty
cycle value within the loops:

pwm_26 = GPIO.PWM(26, 50)

pwm_26.start(0)

for b in range(0, 51, 1):

pwm_26.ChangeDutyCycle(b)

time.sleep(0.02)

for b in range(50, -1, -1):

pwm_26.ChangeDutyCycle(b)

time.sleep(0.02)

GPIO.cleanup()

The first loop will check the value of b. b must fall between 0 and 51 for the loop to run
and each time the loop runs, 1 will be added to b due to the third value at (0, 50, 1).
Each time this loop runs the duty cycle for pwm_26 will be changed to the current value
of b and held for 0.02 seconds by the time.sleep command. Once b reaches 50, the
loop will exit, and the rest of the program will continue.

The second loop is also setup to check the value of b. b must fall between 50 and -1 for
the loop to run and each time the loop runs, 1 will be subtracted from b due to the third
value at (50, -1, -1). Each time this loop runs the duty cycle for pwm_26 will be changed
to the current value of b and held for 0.02 seconds by the time.sleep command. Once
b reaches 0, the loop will exit, and the rest of the program will continue.

Lesson 4 – Import Methods and Pulse Width Modulation Page 109

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Run the program. The program will run the LED from 0% duty cycle or completely off,
through 50% duty cycle, and then back to 0%. The LED will appear off, gradually get
brighter, and then gradually get dimmer again.

Leave the circuit assembled for use in Lesson B-5.

Lesson 4 – Import Methods and Pulse Width Modulation Page 110

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. What does the acronym PWM stand for?

2. Write the words piezo and buzzer next to their definition below:

Must be driven by a square wave and sound can be


adjusted by changing frequency of the signal
Sound will be created as soon as voltage is applied
because it contains internal circuitry for generating
drive signals

3. What command would be used to import only a function named test from a
module named software?

Answers can be found on the next page.

Lesson 4 – Import Methods and Pulse Width Modulation Page 111

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. What does the acronym PWM stand for?

ANSWER: Pulse Width Modulation.

2. Write the words piezo and buzzer next to their definition below:

ANSWER:
Must be driven by a square wave and sound can be
piezo
adjusted by changing frequency of the signal
Sound will be created as soon as voltage is applied
because it contains internal circuitry for generating buzzer
drive signals

3. What command would be used to import only a function named test from a
module named software?

ANSWER: You would use from software import test to import a function
named test from the module named software.

Lesson 4 – Import Methods and Pulse Width Modulation Page 112

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned about module import methods and the differences between
analog and digital signals which you will explore further in later lessons. You then
moved on to pulse width modulation (PWM) and learned to use it to control devices.

In the next lesson you will move on to learning to use more complex switches including
writing computer programs to overcome the electrical shortcomings of some switches.
You’ll also learn to use logical operators to control program flow.

Lesson 4 – Import Methods and Pulse Width Modulation Page 113

Prepared exclusively for [email protected] Transaction: 5095


LESSON 5

SWITCHES AND CORRECTING


FOR SWITCH BOUNCE

OBJECTIVE
In this lesson, you will learn to work with different types of switches and learn to use
programs to overcome the electrical shortcomings of some switches.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Circuit from Lesson B-4
• 1 x Slide Switch
• 1 x Pushbutton Switch
• 3 x Long Jumper Wires
• 2 x Short Jumper Wires

REVIEW CONCEPTS

If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Working with switches (Lesson A-5)


• Boolean Logic, If/Else Statements (Lesson A-14)
• GPIO Pin States, GPIO Pin Cleanup (Lesson A-16)

Lesson 5 – Switches and Correcting for Switch Bounce Page 114

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will learn to work with different types of switches including both slide
and pushbutton switches to accomplish various tasks. You will also learn to write code
to overcome an electrical shortcoming of switches known as switch bounce.

PULL-UP / PULL-DOWN OPTIONS


In Level A, you learned how to use resistors in a specific configuration to keep inputs
reading high or low, when the pushbutton switch was not pressed. This was opposed to
inputs being left floating, where an input is not connected to anything until a switch is
pressed, which can lead to inputs being read unreliably by your program.

While it's good to understand the basics behind using resistors for pull-up and pull-
down, the Raspberry Pi can help you simplify circuit design by doing the pull-up and
pull-down work for you. The Raspberry Pi contains internal resistors that can be
accessed when setting up a GPIO pin as an input. This will simplify your switch wiring to
a single jumper wire from the GPIO pin to the switch, and another wire from the switch
to 3.3V or ground, depending on whether you want a high or low to trigger the input.

Lesson 5 – Switches and Correcting for Switch Bounce Page 115

Prepared exclusively for [email protected] Transaction: 5095


In Python, pulling an input up or down can be done when the pin is assigned as an
input, by supplying a third parameter called pull_up_down:

GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_UP)

This command will initialize GPIO12 as an input, and configure that input to be internally
pulled up. This input will be an active low, which means this input will remain high until it
is made active by connecting it to ground or low, hence the name "active low". GPIO12
will now only need to be connected to ground to trigger the input, which can be done
very simply using a switch and two wires.

The opposite of active low is active high, which means the input will remain grounded or
low until it is made active by connecting to 3.3V. This can be accomplished by using
GPIO.PUD_DOWN after the pull_up_down parameter:

GPIO.setup(20, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

This command will initialize GPIO20 as an input and configure that input to be internally
pulled down. GPIO20 will now only need to be connected to 3.3V to trigger the input,
which can be done with a switch and two wires.

Lesson 5 – Switches and Correcting for Switch Bounce Page 116

Prepared exclusively for [email protected] Transaction: 5095


SLIDE OR TOGGLE SWITCH
Until now, you have only worked with pushbutton switches that make connection when
they are pressed, but lose that connection when they are released. There may come a
time when you want a switch to stay connected without being pressed. This type of
switch is called a toggle switch.

A toggle switch "toggles" one or more inputs between


one or more outputs. One very common type of toggle
switch is a two-position slide switch:

In a two-position
slide switch the
center pole can
connect to one of the
throws at a time,
based on the position
of the slider:

Lesson 5 – Switches and Correcting for Switch Bounce Page 117

Prepared exclusively for [email protected] Transaction: 5095


These switches can be used as power on/off switches for electronic devices by
connecting power through the center pole as well as one of the throws:

A slide switch can also be used as a selector between two different inputs. In the
diagram below, a slide switch is connected to two GPIO pins, with the center terminal
attached to ground.

Lesson 5 – Switches and Correcting for Switch Bounce Page 118

Prepared exclusively for [email protected] Transaction: 5095


In one position, GPIO5 is grounded and a GPIO6 will be internally pulled up by the
Raspberry Pi. In the other position, GPIO6 will be grounded and GPIO5 will be internally
pulled up by the Raspberry Pi.

Programming for a slide switch is identical to the programming you've already done for
pushbutton switches. Your program could use these switch inputs to light up different
LEDs, modify the frequency of a piezo buzzer, or anything else you might want to
control.

Lesson 5 – Switches and Correcting for Switch Bounce Page 119

Prepared exclusively for [email protected] Transaction: 5095


SWITCH BOUNCE
When switches are opening or closing, metal plates called contacts must come together
or move apart to control the flow of electricity through the switch. These plates are
made of metal, so they will conduct electricity, but this metal-to-metal contact can result
in the two plates bouncing off each other a very tiny amount, until they settle either fully
together, or fully apart. This microscopic movement can result in the switch being open
and closed very quickly, typically less than one millisecond, until the switch settles into
the desired state. This phenomenon is referred to as switch bounce.

All mechanical switches will exhibit some level of switch bounce, but it may not be a
problem in their specific application. For example, the light switch in your room has
switch contact bounce but you don't see the light flash on and off multiple times when
you flip the switch on. This is due to the latency in overhead lighting and most other
electronics.

Switch bounce may become a problem depending on how you plan to use the switch
input. If the program just turns on an LED based on switch input, then bounce won't
really be a problem. Extremely rapid on/off LED activity will be masked by the amount of
time it takes the LED to turn on and off, so the LED will appear to turn on and off
smoothly, even though switch bounce is still occurring.

The problem occurs when you have the ability to check a switch very quickly, like the
Raspberry Pi is able to do, thousands of times per second. Imagine you have program
that is supposed to keep track of how often you drink water each day. Each time you
drink a glass of water you press a pushbutton, and the program keeps track of how
many times the switch went from low to high, adding to that count throughout the day.

Pressing the button quickly, only one time, should result in one being added to the
current count. Due to switch bounce, there may be a few extra low to high transitions for
every time the button is pushed, and these will all be logged to the counter.

Lesson 5 – Switches and Correcting for Switch Bounce Page 120

Prepared exclusively for [email protected] Transaction: 5095


Pressing the button 8 times throughout the day might result in a final count of 32
glasses of water, which is obviously very inaccurate. Removing these extra counts is
called debouncing, which can be done via hardware using additional components, or via
software. In the interest of keeping electronic connections to the Pi as simple as
possible, we will focus on debouncing using software in Python. You may find many
effective methods for software debouncing in programs online. Below, we will detail a
few of the simplest options.

Lesson 5 – Switches and Correcting for Switch Bounce Page 121

Prepared exclusively for [email protected] Transaction: 5095


One method of debouncing involves checking the input a certain amount of time after
the first time an input is triggered. This will allow time for the bouncing to settle and the
input state can be confirmed once it is steady. This delay is usually not very long, as a
delay of around 0.02 seconds should be enough to allow the input to settle into either a
steady high or low state. If GPIO6 was configured as an active low input, that code
would look something like this:

while True:

if GPIO.input(6) == False:

time.sleep(0.02)

if GPIO.input(6) == False:

water_count = water_count + 1

The first if statement will trigger the very first time GPIO6 goes low. The program will
sleep for 0.05 seconds, and then check GPIO6 again. If GPIO6 is still False, then one
will be added to water_count. If this second check of GPIO6 is True then nothing else
will happen, and the value of water_count will not be changed.

There are a couple of problems with this method. If you somehow managed to press the
button for less than .02 seconds, no water would be recorded, as the second level if
statement would not evaluate as False. Also, since this entire loop will be evaluated
every .02 seconds, you can easily end up with multiple water_count increments for a
single button press, which defeats the purpose of the delay.

You could increase the amount of delay to avoid multiple water_count events being
counted when the button is held down. Increasing this delay will however increase the
amount of time between the first and second checks, which could allow you a greater
chance of pressing the button and releasing without being counted. You could tune this
value to find a balance between quick presses not registering and longer presses
registering more than once, but it may still not give you performance you would like.

Lesson 5 – Switches and Correcting for Switch Bounce Page 122

Prepared exclusively for [email protected] Transaction: 5095


Another method of debouncing includes adding a delay to the loop that runs after an
input is triggered:

while True:

if GPIO.input(6) == False:

water_count = water_count + 1

time.sleep(.1)

The very first time GPIO6 goes low the loop will be triggered. One will be added to
water_count and the program will sleep for .1 seconds before resuming further checks.
This will eliminate the possibility of quick button presses not being counted, and the
delay in this loop can be modified to eliminate single button presses registering more
than one count per press, without affecting the responsiveness of the first button press.

The only problem left with this code is that button presses longer than .1 seconds will
continue to register additional water_count events. This can be fixed by adding a while
loop to hold the program as long as GPIO6 remains low:

while True:

if GPIO.input(6) == False:

water_count = water_count + 1

time.sleep(.1)

while GPIO.input(6) == False:

pass

time.sleep(0.02)

Lesson 5 – Switches and Correcting for Switch Bounce Page 123

Prepared exclusively for [email protected] Transaction: 5095


The pass command in the new while loop tells that loop to do nothing when triggered,
which is exactly what we want. As long as GPIO6 remains low the program will remain
stuck in this while loop, doing nothing, and not adding to the water_count value. As
soon as GPIO6 is no longer False, the rest of the initial if statement will run. The final
time.sleep(0.02) was added to debounce the opening of the switch, as without the
delay, the bouncing during release of the switch might cause water_count to be
increased unintentionally.

Lesson 5 – Switches and Correcting for Switch Bounce Page 124

Prepared exclusively for [email protected] Transaction: 5095


USING A DELAY TO SAVE SYSTEM RESOURCES
Due to its tiny size, your Raspberry Pi has a limited amount of processing power that
can be devoted to tasks. This processing power is how much work can be done by the
CPU, which stands for Central Processing Unit. CPU utilization is a value that indicates
how hard the CPU is working. A utilization value of 100% indicates that the CPU cannot
handle any more work, and system performance will be highly degraded. A low CPU
utilization value like 2% means that 98% of the CPU is still available and ready to do
processing work.

The current utilization of the CPU is displayed in the top-right corner of the menu bar:

In this image, the CPU utilization is at 25%, meaning 25% of the available processing
power is being used, with 75% still free.

If CPU utilization is too high, no processing power is left over for routine operating
system tasks. By running a loop in your program with no delay, you can easily consume
more system resources than you really need, causing instability in the Raspbian
Operating System.

If you have a loop checking an input in a program, adding a small delay like 0.1 seconds
will free up valuable resources, allowing the OS to complete all the tasks it needs in the
background. Your program will likely run no different, however you will see a big
difference in the CPU utilization number, as well as how much heat is being generated
by your Raspberry Pi.

Lesson 5 – Switches and Correcting for Switch Bounce Page 125

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will practice the input switch handling techniques from this
lesson. Slide and pushbutton switches will be added to the circuit from Lesson #B-4 to
allow for the programming of additional functionality.

ACTIVITY #1 – ADDING SWITCHES TO THE CIRCUIT


In this activity you will add switches to the circuit you built in Lesson B-4

STEP #1
Make sure your Raspberry Pi is powered off. Using the circuit from Lesson B-4 as a
starting point, add a slide switch connected between these points:

Slide switch in J44 through J46

Long jumper wire from F44 to A7

Short jumper wire from F45 to N2-41

Long jumper wire from F46 to A8

Lesson 5 – Switches and Correcting for Switch Bounce Page 126

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Next, you will add a pushbutton switch that will be used as a stop button for your
program. Add a pushbutton switch connected between GPIO13 and ground.

Pushbutton switch in D49, D51, G49, and G51

Long jumper wire from A49 to A17

Short jumper wire from A51 to N2-54

The breadboard circuit is now ready for you to create a program that will use the
switches to control some program functions.

Lesson 5 – Switches and Correcting for Switch Bounce Page 127

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – COUNTING BUTTON PRESSES
In this activity, you will create a program that will count each time the pushbutton
connected to GPIO13 is pressed and print the current count to the console.

STEP #1
The first step will be to create an empty program that you can use to make a program
that counts button presses. Create a new program in Thonny and save it to your
Desktop as button_counter.

STEP #2
Since this program will interact with GPIO pins and have a delay, you must import the
RPi.GPIO and time modules. Import these modules at the top of your program:

import RPi.GPIO as GPIO, time

STEP #3
Next, the GPIO pin mode will need to be set to BCM. GPIO13 will also need to be
configured as an active-low input since it's connected to ground. Since this switch does
not have any pull-up resistors attached, this will need to be configured within the
program. Add the following two highlighted lines just below your import code:

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)

GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)

Lesson 5 – Switches and Correcting for Switch Bounce Page 128

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
A variable will need to be used to hold the amount of button presses that have occurred,
and this value should be zero when the program starts. Create a variable called
button_count and set it equal to zero:

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)

GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)

button_count = 0

STEP #5
Since this program is interacting with GPIO pins, use a try/except loop so the
except: condition can catch keyboard interrupts and run a GPIO.cleanup() before the
program exits. Add the following code to the bottom of your program:

button_count = 0

try:

except KeyboardInterrupt:

GPIO.cleanup()

Your program can now exit smoothly, and without any errors, when the stop button is
pressed in Thonny.

Lesson 5 – Switches and Correcting for Switch Bounce Page 129

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Now it's time to build the main loop that will watch GPIO13 for a low or False state. If
GPIO13 is low, the button has been pressed, and the value of button_count will be
incremented by 1. You will also use this loop to print the current value of
button_count. Add the highlighted block of code to the end or your program:

button_count = 0

try:

while True:

if GPIO.input(13) == False:

button_count = button_count + 1

print(button_count)

except KeyboardInterrupt:

GPIO.cleanup()

Make sure that the print statement is indented inside the if statement. If not, the value
of button_count will be printed every time the main loop runs, which is very, very
often.

Lesson 5 – Switches and Correcting for Switch Bounce Page 130

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Since this program will be checking GPIO13 very often, it will consume more resources
than it really needs. Adding a delay of 0.05 seconds to the main loop will slow down the
button checks a small amount, while drastically reducing the CPU load:

try:

while True:

if GPIO.input(13) == False:

button_count = button_count + 1

print(button_count)

time.sleep(0.05)

except KeyboardInterrupt:

Make sure the indentation on this delay is aligned with the if: block above so that it
runs even if the if: block does not get triggered by GPIO13.

Lesson 5 – Switches and Correcting for Switch Bounce Page 131

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
Run the program and press the button a few times. Do you notice anything about the
count? It's not very accurate, at all! You didn't add any code to slow down the checking
of GPIO13 which is happening about 1000 times per second. Every time that GPIO13 is
checked, and found to be low (button is pressed), button_count is incremented by 1
and the value is printed to the console. You will need to tune up this loop with a delay to
make it run more accurately, which you will do in the next activity.

Below is a copy of the whole program for your reference:

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)

GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)

button_count = 0

try:

while True:

if GPIO.input(13) == False:

button_count = button_count + 1

print(button_count)

time.sleep(0.05)

except KeyboardInterrupt:

GPIO.cleanup()

Lesson 5 – Switches and Correcting for Switch Bounce Page 132

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – TUNING A LOOP USING A DELAY
In this activity, you will modify the program from the last activity to make it count button
presses more accurately.

STEP #1
You will be adding a delay to the main program to slow down how often the state of
GPIO13 is checked. Use a delay value of 2 seconds and see how the program
responds. Add a time.sleep(2) just below the print statement:

try:

while True:

if GPIO.input(13) == False:

button_count = button_count + 1

print(button_count)

time.sleep(2)

time.sleep(0.05)

except KeyboardInterrupt:

GPIO.cleanup()

Lesson 5 – Switches and Correcting for Switch Bounce Page 133

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Run the program and press the button a few times. Pressing the button results in
button_count being incremented by 1, the new value of button_count being printed
to the console, and then a two second delay. This delay works well to slow down the
loop and make counts more accurate, but the program feels a little unresponsive during
those large two second delays when new presses are ignored.

Reduce the value of the delay from 2 down to 0.2:

try:

while True:

if GPIO.input(13) == False:

button_count = button_count + 1

print(button_count)

time.sleep(0.2)

time.sleep(0.05)

except KeyboardInterrupt:

GPIO.cleanup()

STEP #3
Run the program again with the new delay value and press the button a few times. You
will find that a value of 0.2 for the delay is just right for keeping the program responsive
after a button press, as well as eliminating a single press being counted multiple times.

The word "tuning" was used in title of this section because a loop might need slight
timing changes based on how it's causing your program to behave. No delay caused
multiple counts, while 2 full seconds made the program feel unresponsive. For this
specific program, a delay of 0.2 was a good balance between those two to ensure the
most accurate counting performance from the program.

Lesson 5 – Switches and Correcting for Switch Bounce Page 134

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #4 – ADDING LED CONFIRMATION USING THE SLIDE SWITCH
In this activity, you will modify the program from the last activity to flash the LED each
time the value of button_count is incremented. Input from the slide switch will be used
to enable or disable the LED indicator.

STEP #1
The GPIO pins for the slide switch and LED will need to be configured so they can be
used in this program. GPIO22 will need to be configured as an active-low input (software
pull-up required), and GPIO26 will need to be an output. Using your program from the
last activity as a starting point, make these additions just below the existing GPIO.setup
line:

GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(26, GPIO.OUT)

button_count = 0

Lesson 5 – Switches and Correcting for Switch Bounce Page 135

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The main loop will already be running and checking the pushbutton switch on GPIO13.
We only want the LED to come on when an increment event happens, so we can nest
the LED code inside of the existing if block. After the value is button_count is printed,
add an if condition that will check to see if GPIO22 is low or False, and if so, turn on
the LED at GPIO26:

try:

while True:

if GPIO.input(13) == False:

button_count = button_count + 1

print(button_count)

if GPIO.input(22) == False:

GPIO.output(26, GPIO.HIGH)

time.sleep(0.2)

The time delay from the last lesson can now perform the additional task of keeping the
LED on for 0.2 seconds, which is a good quick flash for an indicator light.

Lesson 5 – Switches and Correcting for Switch Bounce Page 136

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The only thing missing is a way to turn the LED off. We can fix this by adding a
GPIO.output line to the end of the main loop that will turn off the LED:

try:

while True:

if GPIO.input(13) == False:

button_count = button_count + 1

print(button_count)

if GPIO.input(22) == False:

GPIO.output(26, GPIO.HIGH)

time.sleep(0.2)

GPIO.output(26, GPIO.LOW)

The LED will be turned off every time the main loop runs. If the slide switch is applying
ground to GPIO22 when the main loop runs, then the LED will be turned on, delayed for
0.2 seconds, and then turned off at the end of the main loop.

If the slide switch is not applying a ground to GPIO22 when the main loop runs, the LED
will not turn on, the program will still delay 0.2 seconds, and then the LED will be told to
turn off, even though it's already off. Sending a GPIO.LOW command to a GPIO pin that's
already low will not harm anything. This just ensures that the LED gets turned off at the
end of the main loop, regardless of whether it happens to be on or off.

Lesson 5 – Switches and Correcting for Switch Bounce Page 137

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Run the program. Press the pushbutton to accumulate some counts, and then move the
slide switch to the opposite position, and verify the LED reacts as expected. With the
slide switch selector closest to the pushbutton switch, counts will be indicated by the
LED flash, With the slide switch in the opposite position, the LED will stay off, but counts
will continue to accumulate and be printed when the pushbutton is pressed.

A copy of the entire program is available on the next page for reference.

Leave the circuit assembled for use in Lesson B-6.

Lesson 5 – Switches and Correcting for Switch Bounce Page 138

Prepared exclusively for [email protected] Transaction: 5095


import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)

GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(26, GPIO.OUT)

button_count = 0

try:

while True:

if GPIO.input(13) == False:

button_count = button_count + 1

print(button_count)

if GPIO.input(22) == False:

GPIO.output(26, GPIO.HIGH)

time.sleep(0.2)

GPIO.output(26, GPIO.LOW)

time.sleep(0.05)

except KeyboardInterrupt:

GPIO.cleanup()

Lesson 5 – Switches and Correcting for Switch Bounce Page 139

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Can pull-ups and pull-downs be added within your program, or does every input
switch require connections to physically pull-up or pull-down resistors?

2. Why is switch contact bounce a problem for programs that are counting switch
presses?

3. Does an active-low input need to be connected to 3.3V or ground to trigger that


input?

Answers can be found on the next page.

Lesson 5 – Switches and Correcting for Switch Bounce Page 140

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Can pulling inputs up and down be done within your program, or does every
switch require connections to physically pull-up or pull-down resistors?

ANSWER: Pulling inputs up and down can be done within your program, when
you configure the GPIO pin as an input.

2. Why is switch contact bounce a problem for programs that are counting switch
presses?

ANSWER: If your application for counting switch contact is very accurate like a
computer and can register multiple switch contacts per second, so a single
button press could result in several switch counts, rather than only one being
counted.

3. Does an active-low input need to be connected to 3.3V or ground to trigger that


input?

ANSWER: An active-low input means that the input must be low to activate the
input. That input will need a ground applied to over come the pull-up and
activate the input in software.

Lesson 5 – Switches and Correcting for Switch Bounce Page 141

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to work with both slide and pushbutton switches as well as
how to account for switch bounce.

In the next lesson, you will learn to work with logical operators that will allow you to
create programs with more advanced behavior based on multiple GPIO inputs or
variables.

Lesson 5 – Switches and Correcting for Switch Bounce Page 142

Prepared exclusively for [email protected] Transaction: 5095


LESSON 6

LOGICAL OPERATORS

OBJECTIVE
In this lesson, you will learn to work with logical operators to control program flow as
well as continue to expand your knowledge of using switches.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Circuit from Lesson B-5

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Working with switches (Lesson A-5)


• Boolean Logic, If/Else Statements (Lesson A-14)
• GPIO Pin States, GPIO Pin Cleanup (Lesson A-16)

Lesson 6 – Logical Operators Page 143

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will revisit logical operators and expand your knowledge of this code
to learn to control program flow as well as continue to work with both pushbutton and
slide switches.

LOGICAL OPERATORS IN PYTHON


Logical operators add more complexity to the decisions that programs can make.
Instead of being limited to whether one condition is true or false, you can use the logical
operators and, or, and not to create more complicated conditions than a standard
statement. These operators can be used to add extra conditions to a statement:

if x == '42': Will only trigger if x is '42'


if x == '42' and y == 'start': Will only trigger if x is '42' and y is 'start',
any other combination of values for these
two variables will not trigger this block
if x == '42' or y == 'start': Will trigger if x is '42' or y is 'start, if either
of these conditions is met this block will
run

Each comparison in the statements above will be evaluated separately. The behavior of
the first statement is very simple because there are only two possibilities:

if x equals '42', this condition will evaluate as True, and the if block will run

if x doesn't equal '42', this condition will evaluate as False, and the if block will not run

Lesson 6 – Logical Operators Page 144

Prepared exclusively for [email protected] Transaction: 5095


The and and or logical operators operate using what's known as a truth table. Each
comparison in an and or or statement will be evaluated as True or False, and those
True or False results will be used to determine if the entire condition results in a True or
False result.

Truth table for and logic


Condition 1 Condition 2 Result
True True True
True False False
False True False
False False False

The only way to get a True result out of two conditions using and is for both conditions
to evaluate as True. Any other combination of True/False conditions will result in the
and statement evaluating as False, and that block of code being skipped.

The or operator is much more flexible, as it will allow True results if any of the
conditions are True:

Truth table for or logic


Condition 1 Condition 2 Result
True True True
True False True
False True True
False False False

The not operator can be used a couple of different ways. It can be used to get the
opposite of a True or False value:

var = False

if not var:

print('Message')

Lesson 6 – Logical Operators Page 145

Prepared exclusively for [email protected] Transaction: 5095


The value of var is set equal to False. The if statement will check the value of var but
the not operator will invert the value from False to True, allowing the condition to
evaluate as True, and the message to print. The value of var was never actually
modified so it will still be False for the rest of your program.

The not operator can be used in another way to modify a variable's True/False value to
be the opposite.

var = not var

This will take the current True/False value of var and change it to the opposite. If True it
will become False, and if False it will become True. This seems like it might not be very
useful, but you will use this switching in the next section to toggle a variable between
True/False to control the state of an LED.

Lesson 6 – Logical Operators Page 146

Prepared exclusively for [email protected] Transaction: 5095


USING A PUSHBUTTON SWITCH AS A TOGGLE
Pushbutton switches are nice for turning a device like an LED on temporarily, but you
might want that LED to stay on after you have released the button. Pressing the button
again would turn the LED off. This can be done in software by using a variable to store
the current on/off status and changing the status only when the button is pressed. In the
example below, GPIO13 would be configured as an active low input and GPIO26 would
be configured as an output above this section of the program:

led = False

old_switch = True

while True:

new_switch = GPIO.input(13)

if new_switch == False and old_switch == True:

led = not led

time.sleep(.2)

GPIO.output(26, led)

old_switch = new_switch

The initial states for two variables are set at the top so they can be used (and reused)
throughout the program. The led variable is set equal to False, so the LED will initially
be off. The old_switch variable is set equal to True so it's initial value will match the
high state of GPIO13. Remember that True is the same as high in Python, so you can
compare True and False to an input to determine if it's high or low.

The while True: loop will run its contents forever, so it's a handy way to check a
switch over and over in a program.

The value of new_switch will be set equal to the value of GPIO13. If GPIO13 is high (the
switch is not pressed) then new_switch will be set to True, and if GPIO13 is low (the
switch is pressed) then new_switch will be set to False.

The if block will only run if new_switch is False and old_switch is True. Any other
pair of conditions like True/True, True/False, or False/False will cause this block to be

Lesson 6 – Logical Operators Page 147

Prepared exclusively for [email protected] Transaction: 5095


skipped. If this block evaluates as True, then the value of the led variable will be
switched to its opposite:

If led is currently equal to False, then led = not led will set led to True

If led is currently equal to True, then led = not led will set led to False

The time.sleep(.2) was added to slow down this loop. Without the delay, the contact
bounce within the switch can cause additional button presses to be registered, toggling
the LED unexpectedly. Slowing down the toggling of the led state by adding the delay
fixes that problem.

The GPIO.output statement will switch GPIO26 high or low based on the value of the
led variable:

If led is high, then this statement will run as GPIO.output(26, True) which is
equivalent to GPIO.output(26, GPIO.HIGH).

If led is low, then this statement will run as GPIO.output(26, False) which is
equivalent to GPIO.output(26, GPIO.LOW).

The last line of code will set old_switch equal to the current value of new_switch. This
line is crucial for the program's ability to remember the last state of the switch. This
enables the if block that changes the led state to run only when the button is first
pressed. Holding the pushbutton down will not affect the LED status, as both
new_switch and old_switch will be equal to False during that time. Once the button is
released, new_switch will be True again, and old_switch will be True as well, once
this last line of code sets them equal.

One thing to note about the GPIO.output commands above is that multiple arguments
can be used to turn a GPIO pin on and off. GPIO.HIGH, True, or 1 will all make a GPIO
pin go high:

GPIO.OUTPUT(26, GPIO.HIGH)
GPIO.OUTPUT(26, True)
GPIO.OUTPUT(26, 1)

Lesson 6 – Logical Operators Page 148

Prepared exclusively for [email protected] Transaction: 5095


These are all ways of making GPIO26 go high, and they will all work equally well.
GPIO.LOW, False, and 0 can all be used to pull a GPIO pin low.

GPIO.OUTPUT(26, GPIO.LOW)
GPIO.OUTPUT(26, False)
GPIO.OUTPUT(26, 0)

As these lessons progress, you will start to switch to using 0 and 1 to push GPIO pins
high and low. This will be one way that more advanced programs can be slightly
simplified, by using one character instead of 8 for these arguments.

Lesson 6 – Logical Operators Page 149

Prepared exclusively for [email protected] Transaction: 5095


USING A SWITCH TO END A PROGRAM
The last program worked well, but there was no way to end the program gracefully.
Pushing the stop button in Thonny will work to stop the program, but you will see errors
the next time you try to use GPIO pins, since they weren't cleaned up properly when the
last program stopped.

To use the example from the last section, it might be nice to have one switch that turns
the LED on and off, and another switch that ends the program gracefully. Since GPIO13
was being used to toggle the LED, you could use GPIO22 for this theoretical end switch
in the example below.

The main change will be to the while True: loop. That loop will run forever with no
way to exit. Instead you can create a variable called end above the loop and set it to
False:

end = False

Now the while True: statement will be replaced with one that relies on end staying
equal to False:

end = False

while end == False:

This while loop will only continue to run as long as end equals False. Setting end to
True anywhere in the loop will cause the loop to exit, and the rest of the program after
the loop to run. The variable you choose to control the loop does not have to be named
end, but whatever name you choose must be set initially above the loop and referenced
in the while loop condition.

Lesson 6 – Logical Operators Page 150

Prepared exclusively for [email protected] Transaction: 5095


The next step is to add a statement inside the loop to see if GPIO22 has been activated.
GPIO22 in this example has been assigned as an input with a pull up in the section
above this area of the program. GPIO22 will be pulled low by pressing the button:

end = False

while end == False:

if GPIO.input(22) == False:

Since GPIO22 is activated, it will evaluate as False and any code inside the if statement
will run. Go ahead and add another line of code to change the value of end to True:

end = False

while end == False:

if GPIO.input(22) == False:

end = True

As long as GPIO22 continues to evaluate as True, this block of code will be skipped,
and the rest of the while loop will run over and over. Adding the GPIO.cleanup code to
the very end of this program will complete the end button functionality.

end = False

while end == False:

if GPIO.input(22) == False:

end = True

GPIO.cleanup()

Lesson 6 – Logical Operators Page 151

Prepared exclusively for [email protected] Transaction: 5095


Adding this end button code to the LED code from the previous section would look like
this:

led = False

old_switch = True

end = False

while end == False:

if GPIO.input(22) == False:

end = True

new_switch = GPIO.input(13)

if new_switch == False and old_switch == True:

led = not led

time.sleep(.2)

GPIO.output(26, led)

old_switch = new_switch

GPIO.cleanup()

The button attached to GPIO13 will switch the LED state, and the button attached to
GPIO22 will cause the while loop to end, the cleanup command to run, before exiting
the program.

Lesson 6 – Logical Operators Page 152

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will practice using the logical operators you learned in this
lesson. You will use them to write a program that uses a pushbutton switch to toggle an
LED on and off, write another program that changes LED behavior based on two
switches, and then modify that program to end when specific criteria are met.

The programs above will all use the breadboard circuit that you built in Lesson B-5.

ACTIVITY #1 – USE A SWITCH AS A TOGGLE


In this activity you will create a program that will use the pushbutton switch to toggle an
LED on and off. Normally, the pushbutton switches in your kit can only activate things
while they are pressed. By adding some additional logic to this program, you can use
each press of the pushbutton to switch the LED state. If the LED is off then pressing the
button will turn the LED on, and if it's already on, pressing the button will turn the LED
off.

STEP #1
The first step will be to create a new program in Thonny. Create a new program called
logic_pb and save it to the Desktop.

STEP #2
This program will use GPIO pins and the time.sleep command, so those modules will
need to be imported. Import them in the first line of the program:

import RPi.GPIO as GPIO, time

Lesson 6 – Logical Operators Page 153

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The switch connected to GPIO13 as well as the LED connected to GPIO 26 will be used
in this program. Set the GPIO pin mode to BCM, GPIO13 as an input with a pull-up, and
GPIO26 as an output:

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)

GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(26, GPIO.OUT)

STEP #4
The pushbutton switch and LED are now set up to be used by the program. Next, you
will need a couple of variables to hold the current state of the LED and the last value of
the switch. By using the not logical operator you can toggle the state of an LED
regardless of its current state. Create a variable named led that's equal to False and
old_switch that's equal to False:

GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(26, GPIO.OUT)

led = False

old_switch = False

These values will be modified later in the program to control the state of the LED as well
as "remember" the last state of the software toggle switch you are creating for the
pushbutton.

Lesson 6 – Logical Operators Page 154

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
You can keep this program exiting smoothly by adding a try/except conditions that will
catch keyboard interrupts and cause a GPIO.cleanup to occur. Add the following
try/except block to your program:

led = False

old_switch = False

try:

except KeyboardInterrupt:

GPIO.cleanup()

STEP #6
You want the program to continually monitor the pushbutton on GPIO13 for presses so
add a while True: loop inside the try: block so it will run over and over:

try:

while True:

except KeyboardInterrupt:

GPIO.cleanup()

Lesson 6 – Logical Operators Page 155

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
The first step to the main program will be to check the current state of GPIO13 and set a
variable named new_switch equal to GPIO13's current state. If GPIO13 is high
(pushbutton not pressed) then new_switch will equal True. If GPIO13 is low (pushbutton
pressed) then new_switch will equal False:

try:

while True:

new_switch = GPIO.input(13)

except KeyboardInterrupt:

GPIO.cleanup()

STEP #8
Now that new_switch is storing the current value of GPIO13, you can compare it to the
value of old_switch. If they are both False, then you need to switch the state of led
using the not logical operator, as well as add a time.sleep of 0.2 seconds to make
sure this loop only triggers once each time the pushbutton is pressed:

try:

while True:

new_switch = GPIO.input(13)

if new_switch == False and old_switch == False:

led = not led

time.sleep(0.2)

except KeyboardInterrupt:

Lesson 6 – Logical Operators Page 156

Prepared exclusively for [email protected] Transaction: 5095


STEP #9
The new LED state has been set, but GPIO26 has not been updated with this new LED
state. You also need to set old_switch equal to new_switch so the program can
"remember" the last state of the software toggle switch that you are creating. You will
also add a time.sleep of 0.05 seconds to the main loop to slow it down slightly, freeing
up resources on the Raspberry Pi:

try:

while True:

new_switch = GPIO.input(13)

if new_switch == False and old_switch == False:

led = not led

time.sleep(0.2)

GPIO.output(26, led)

old_switch = new_switch

time.sleep(0.05)

except KeyboardInterrupt:

Make sure these additions are indented for the main program loop and not part of the
if block. The if block should only be updated the state of led and inserting a 0.2 second
delay.

Lesson 6 – Logical Operators Page 157

Prepared exclusively for [email protected] Transaction: 5095


STEP #10
Run the program. Each press of the pushbutton switch will cause the LED to change
state. If your program does not run as expected, check it against the program below.
This program will be used as a starting point for Activity #2, so do not continue until this
program is working properly.

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)
GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(26, GPIO.OUT)

led = False
old_switch = False

try:
while True:
new_switch = GPIO.input(13)
if new_switch == False and old_switch == False:
led = not led
time.sleep(0.2)
GPIO.output(26, led)
old_switch = new_switch
time.sleep(0.05)

except KeyboardInterrupt:
GPIO.cleanup()

Lesson 6 – Logical Operators Page 158

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – USE AND/OR TO CONTROL DIFFERENT EVENTS
In this activity, you will modify the program from Activity #1 to incorporate the slide
switch attached to GPIO22. If the slide switch is not grounding GPIO22 and the
pushbutton is not pressed then the LED will stay off. If either the slide switch is
grounding GPIO22 or the pushbutton is pressed, then the LED will blink on and off. If
both the slide switch is grounding GPIO22 and the pushbutton is pressed, then the LED
will stay on.

STEP #1
The first step will be to create a copy of the program from Activity #1 that can be
modified to add these new features. With logic_pb.py open in Thonny, click File and
then Save As. Use a new file name of logic_and_or and save the new file to your
Desktop.

STEP #2
Since this program will make use of the slide switch connected to GPIO22, GPIO22 will
need to be configured as an input with a pull-up. Add the line of code below to the
GPIO.setup lines:

GPIO.setmode(GPIO.BCM)

GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(26, GPIO.OUT)

Lesson 6 – Logical Operators Page 159

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Youwill no longer need the lines of code for led, old_switch, or the contents of the
while True: block. Remove these lines of code and your program will now look like this:

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)

GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(26, GPIO.OUT)

try:

while True:

except KeyboardInterrupt:

GPIO.cleanup()

Lesson 6 – Logical Operators Page 160

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Now it's time to program the switch and LED behavior in the main program block. First,
you will add an if condition that checks to see if both GPIO22 and GPIO13 are low, and
if so, make the LED connected to GPIO26 go high:

try:

while True:

if GPIO.input(22) == False and GPIO.input(13) == False:

GPIO.output(26, GPIO.HIGH)

except KeyboardInterrupt:

Lesson 6 – Logical Operators Page 161

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
Next, you will add an elif condition that checks to see if either GPIO22 or GPIO13 are
low, and if so, make the LED connected to GPIO26 blink. The code is this block will turn
the LED on, sleep for 0.2 seconds, turn the LED off, and then sleep for another 0.2
seconds. When this condition is triggered over, and over, it will give the appearance of
the LED blinking:

try:

while True:

if GPIO.input(22) == False and GPIO.input(13) == False:

GPIO.output(26, GPIO.HIGH)

elif GPIO.input(22) == False or GPIO.input(13) == False:

GPIO.output(26, GPIO.HIGH)

time.sleep(0.2)

GPIO.output(26, GPIO.LOW)

time.sleep(0.2)

except KeyboardInterrupt:

Lesson 6 – Logical Operators Page 162

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
The last condition to add to the code will be an else: that will be triggered if neither
GPIO22 or GPIO13 are False. This else: block will turn the LED off each time it's
triggered:

try:

while True:

if GPIO.input(22) == False and GPIO.input(13) == False:

GPIO.output(26, GPIO.HIGH)

elif GPIO.input(22) == False or GPIO.input(13) == False:

GPIO.output(26, GPIO.HIGH)

time.sleep(0.2)

GPIO.output(26, GPIO.LOW)

time.sleep(0.2)

else:

GPIO.output(26, GPIO.LOW)

except KeyboardInterrupt:

Lesson 6 – Logical Operators Page 163

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
The last code to add to the main loop will be a time.sleep command of 0.05 seconds
to the main loop. This will slow down the main loop slightly, but will greatly decrease the
resources that this program will consume when running:

else:

GPIO.output(26, GPIO.LOW)

time.sleep(0.05)

except KeyboardInterrupt:

GPIO.cleanup()

Lesson 6 – Logical Operators Page 164

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
Run the program and manipulate both switches. With both switches deactivated the
LED will be off. With one switch or the other activated the LED will blink, and with both
switches activated, the LED will stay on. Below is the code for the whole program. This
program will be used as a starting point for Activity #3 so do not proceed until this
program is working as expected.

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)
GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(26, GPIO.OUT)

try:
while True:
if GPIO.input(22) == False and GPIO.input(13) == False:
GPIO.output(26, GPIO.HIGH)
elif GPIO.input(22) == False or GPIO.input(13) == False:
GPIO.output(26, GPIO.HIGH)
time.sleep(0.2)
GPIO.output(26, GPIO.LOW)
time.sleep(0.2)
else:
GPIO.output(26, GPIO.LOW)
time.sleep(0.05)

except KeyboardInterrupt:
GPIO.cleanup()

Lesson 6 – Logical Operators Page 165

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – ADD STOP BUTTON FUNCTIONALITY
Using the program from Activity #2 as a starting point, you will now add the ability to end
the program to the code using the switches connected to GPIO22 and GPIO13. Instead
of making the LED stay on when both GPIO22 and GPIO13 are False, you will replace
this condition with code to end the program and print a message letting the user know
the program is ending.

STEP #1
The first step will be to create a copy of the program from Activity #2 that can be
modified to add these new features. With logic_and_or.py open in Thonny, click File
and then Save As. Use a new file name of logic_end and save the new file to your
Desktop.

STEP #2
You will need to use a variable to hold a value that will determine whether the main loop
continues to run. Let use end as the variable and set it equal to False. Add this line of
code between the GPIO.setup lines and the try: block:

GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(26, GPIO.OUT)

end = False

try:

Lesson 6 – Logical Operators Page 166

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The main block of the program is currently running with a while True: loop which will
run forever, regardless of the value of end. This line of code needs to be modified to be
dependent on the value of end. If end continues to be False then the loop will run
forever, but if end ever becomes True, the main loop will no longer run, and the
program will exit.

Replace while True: with while end == False: in the main loop:

try:

while end == False:

if GPIO.input(22) == False and GPIO.input(13) == False:

STEP #4
The last change to the program will be the block of code that gets executed when both
GPIO22 and GPIO13 are False. When this condition is satisfied, you want to set end
equal to True, print Program ending, and run a GPIO.cleanup(). Replace the line of
LED code in the if block with the lines below:

try:

while end == False:

if GPIO.input(22) == False and GPIO.input(13) == False:

end = True

print('Program ending')

GPIO.cleanup()

elif GPIO.input(22) == False or GPIO.input(13) == False:

You may be wondering why you need another GPIO.cleanup since there is already one
at the end of the program inside the except block. That GPIO.cleanup will only run
when the program is stopped by an exception, which is how you have been stopping the
program, until now.

Lesson 6 – Logical Operators Page 167

Prepared exclusively for [email protected] Transaction: 5095


Since you are now ending the program by using a variable to control whether the main
loop continues running, the except block will no longer be triggered, so the additional
GPIO.cleanup is required elsewhere in the program.

STEP #5
Run the program. It will behave the same as the program in Activity #2 with the
exception that activating both switches will now end the program instead of turning on
the LED. Here is the entire program for your reference:

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)
GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(26, GPIO.OUT)

end = False

try:
while end == False:
if GPIO.input(22) == False and GPIO.input(13) == False:
end = True
print('Program ending')
GPIO.cleanup()
elif GPIO.input(22) == False or GPIO.input(13) == False:
GPIO.output(26, GPIO.HIGH)
time.sleep(0.2)
GPIO.output(26, GPIO.LOW)
time.sleep(0.2)
else:
GPIO.output(26, GPIO.LOW)
time.sleep(0.05)

except KeyboardInterrupt:
GPIO.cleanup()

Leave the circuit assembled for use in Lesson B-7.

Lesson 6 – Logical Operators Page 168

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Is this truth table for an and or an or operator?

Condition 1 Condition 2 Result


True True True
True False True
False True True
False False False

2. Does the variable used to end a program have to be named end, or can you
use variable names like continue or keep_loop_running?

3. Which logical operator can be used to get the opposite of a value that's True or
False?

Answers can be found on the next page.

Lesson 6 – Logical Operators Page 169

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Is this truth table for an and or an or operator?

Condition 1 Condition 2 Result


True True True
True False True
False True True
False False False

ANSWER: This table is for an or operator. If it was a table for an and operator,
only the first line with two true conditions would produce a true result. Since
lines two and three containing a single true statement are also true, this is an or
operator table.

2. Does the variable used to end a program have to be named end, or can you
use variable names like continue or keep_loop_running?

ANSWER: You can use any variable you like, as long as the names match in
both the initial assignment, as well as wherever it gets modified later in the
program:

continue = 0

while continue == 0:

if GPIO.input(21) == True:

continue = 1

Lesson 6 – Logical Operators Page 170

Prepared exclusively for [email protected] Transaction: 5095


3. Which logical operator can be used to get the opposite of a value that's True or
False?

ANSWER: The not operator can switch a value to its opposite. In the example
of var = not var, the not operator will be applied to the current True/False
value of var, switching it to the opposite of the current value, and saving it as
the new value of var.

Lesson 6 – Logical Operators Page 171

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to use logical operators to control program flow and cause
multiple programmed actions to occur based on input from switches.

In the next lesson, you will learn to work with a 3x4 matrix keypad which provides a
more efficient wiring system for connecting multiple switches.

Lesson 6 – Logical Operators Page 172

Prepared exclusively for [email protected] Transaction: 5095


LESSON 7

WORKING WITH A
3X4 MATRIX KEYPAD

OBJECTIVE
In this lesson, you will learn to work with multiple keypad switches wired in a matrix style
configuration. You will also learn to work with global and local variables.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Assembled Circuit from Lesson B-6
• 1 x Matrix Style Keypad plus Male-to-Male Pin Adapter
• 4 x Short Jumper Wires

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Switches (Lessons A-5, B-5)


• Reading Electrical Schematics (Lesson A-8)
• Lists (Lesson A-13)
• Strings (Lesson B-3)

Lesson 7 – Working with a 3x4 Matrix Keypad Page 173

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will learn to work with multiple keypad switches wired in a matrix style
configuration. This configuration will allow you to use less inputs than standard switches
connected directly to GPIO pins. In addition, you will work with global and local
variables.

MATRIX STYLE INPUT PANEL

Until now, all the circuits and programs


you have constructed have required a
single GPIO pin devoted to monitoring a
single switch. This works well as long as
you don’t have more switches than you
have GPIO pins. This keypad, for
example, would need 61 GPIO pins to
monitor all of these input switches. Most
devices, especially inexpensive devices
like the Raspberry Pi, don’t have nearly
that many GPIO pins available.

There are many methods for reducing the number of microcontroller pins required to
interact with a large number of switches, and one very popular method is called matrix
wiring. Matrix wiring means switches are wired together in such a way that each row
and column has its own unique output
wire. Matrix wiring can also be used with
LEDs, which is how they are often wired in
LED cube projects.

The pushbutton switches in this diagram


have been drawn at an angle to maximize
clarity. Remember when reading electrical
schematics, just because lines on the
drawing cross, it does not mean they're
connected. The rows and columns are
only connected by switches, and not to
each other.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 174

Prepared exclusively for [email protected] Transaction: 5095


Each row of switches shares a connection and each column of switches shares a
connection. Just like regular pushbutton switches, these switches are normally open,
meaning they only allow current to flow when they are pressed.

At first glance, it may be difficult to visualize how you will determine which specific
switch is pressed, but software is crucial to how this matrix of switches can be read and
used in a program.

Using the 3x4 matrix of switches from


above, imagine you’ve pressed the switch
labeled 5 and work on detecting it through
the row/column wiring.

The switch labeled 5 is now in the pressed


position, so its contacts are connecting
Row B and Column 1.

By applying 3.3V or high to rows A, B, C,


and D, while monitoring columns 0, 1, and
2 as inputs, only column 1 will be high, as
no switches in columns 0 or 2 are allowing
current to flow into those columns.

The rows and columns where 3.3V is


present are illustrated by red in this image.
3.3V will flow into one leg of all the
switches, but only being pressed will allow
it to flow to the other leg, using that switch
as a path from one row to one column.

You now know that the pressed switch is


in column 1. The only problem is that it
could be switch 2, 5, 8 or 0. Removing
3.3V from all rows will cause column 1 to
go low again. Monitoring column 1 while
applying 3.3V to each row, one at a time,
will allow you to pinpoint the row of the
pressed switch. In this case it would be
row B, column 1, or the button labeled 5.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 175

Prepared exclusively for [email protected] Transaction: 5095


PROGRAM FLOW FOR CHECKING A MATRIX
Checking a matrix may seem like a very tedious process, but software can automate
this process to make it extremely fast. The actual software flow would look something
like this:

Make row A high

Check columns 1, 2, and 3

If any are high, then the row/column has been located

If all are low, then no buttons are pressed so move to the next row

Make row A low

Repeat for rows B, C, and D, then start over again with row A

The only thing missing is to assign values to each row/column that match the buttons
that will be pressed. This will allow the software to give you the exact value of the button
that was pressed. This value assignment can be done with lists:

row_A = ['1', '2', '3']

row_B = ['4', '5', '6']

row_C = ['7', '8', '9']

row_D = ['*', '0', '#']

These lists are the reason the columns were numbered 0, 1, and 2, instead of the more
logical 1, 2, 3. The values 0, 1, and 2 correspond directly with the index values in these
rows, therefore column 0 (index 0) for row a will map to the button labeled 1, and so on.
This way the column number can be used to access the exact position in each of these
lists. Back in Lesson B-3 you learned how to return the value from a specific location of
a list, using its index value:

row_A[1] will return the string '2'

row_B[2] will return the string '9'

row_D[0] will return the string '*'

Lesson 7 – Working with a 3x4 Matrix Keypad Page 176

Prepared exclusively for [email protected] Transaction: 5095


STORING VALUES AS STRINGS VERSUS INTEGERS
Row D is actually the reason that quotation marks are required around the values in all
rows. Unlike the values 0 through 9 that can be stored as integers or strings, the
asterisk and octothorp (technical term for the pound sign) are not numeric values so
they must be stored as strings.

Technically, the numeric values in rows A through D could be stored as integers by


omitting the quotation marks, but this could cause errors if the key press values are
printed:

key = row_A[2]

print(key)

This code will respond equally well whether the value of key is set equal to a string or
an integer value. The value of key will be printed in either case.

The problem occurs if you intend to have a more informative print statement that
includes the value of key:

key = row_A[2]
print('The key pressed was ' + key)

This code would work well for any values in the row lists that are stored as strings, but
integer values would cause errors. This is due to the fact that strings and integers are
two different types of objects and cannot be merged into the same print statement.

Storing all values as strings using quotation marks will ensure that either type of print
statement will run properly.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 177

Prepared exclusively for [email protected] Transaction: 5095


MEMBRANE TYPE SWITCHES
The momentary pushbutton switches you worked with in Level A were standard
pushbutton switches, that contain metal contact plates, held apart by a spring. When the
switch is pressed, the contacts touch, and current is able to flow between specific legs
on the bottom of the switch. This is a very common design however it does cause the
switch to be taller than some users might want due to all of the internal components.

Another type of pushbutton switch called a membrane switch has been developed to
mimic the function of a pushbutton switch, but they are able to be made very thin due to
their construction.

A membrane switch is made up of an adhesive backing layer, a circuit layer with


conductive traces printed on its face, a layer of non-conductive spacer, a layer of metal
domes or conductive material, and a layer of thin plastic on which is printed the graphic
for the buttons.

When the switch is pressed, the metal dome or conductive sheet, will come in contact
with the circuit layer, completing a connection between the two sides of the switch. This
will allow current to flow until the switch is no longer pressed, causing the metal dome to
spring back away from the circuit layer, causing the switch to open.

Membrane switches can be found everywhere from the front of your microwave oven to
the keyboard on your computer. Since keyboards usually contain over 100 switches,
manufacturers often opt for membrane-style switches due to lower cost and how much
they simplify the manufacturing process. Some higher end keyboards will separate
pushbutton switches for every key to increase key-press performance for gaming and
other applications.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 178

Prepared exclusively for [email protected] Transaction: 5095


MATRIX KEYPAD WIRING
The matrix keypad supplied in your kit has 7 wires exiting the keypad that connect to the
rows and columns. This keypad is called a 3x4 matrix since it has three columns and
four rows. Each row and column has its own connection, as seen in the photo below:

The connections above will only be correct with the keypad facing up. If the keypad is
flipped over, or face down, then the connections listed above will be reversed.

Each row and column keypad connection must be properly identified in your program. If
the program is checking column 2 on GPIO21, then GPIO21 must be physically
connected to column 2, or the keypad values the program has stored for column 2 will
not match the keys being pressed.

The keypad is also slightly different than the rest


of your parts, in that it uses female sockets for the
connector. Your breadboard also uses female
sockets, so the two devices cannot connect
directly to each other. The keypad comes with a 7-
pin male to male adapter that can be inserted into
the keypad connector, after which the header can
then be inserted into the breadboard.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 179

Prepared exclusively for [email protected] Transaction: 5095


VARIABLES AND SCOPE
The scope of a variable refers to the area of a program that can see or interact with that
variable. The areas of scope are the main program, which is referred to as global, and
the area inside of functions, referred to as local.

This variable var below is being assigned in the main program and is considered
global. It can interact with objects in both the main program and inside functions:

var = 42

def test():

print(var)

The print statement will have no problem printing the value of the global variable var.

The opposite of this is a local variable. The variable var below is being assigned from
within the function called test.

def test():
var = 42

This variable can be used, printed, and modified in any way you want, as long as it
stays inside this function. Trying to use var in the main program will result in errors that
var has not been defined. This is because the main program is looking for variables with
global scope, and in this case, var has been defined local to the test function.

There is a way to allow local variables to be available at the global level, by using the
global command inside of a function to denote that this variable must be available
globally. Just insert the word global followed by the variable name into your function:

def test():

global var

var = 42

Lesson 7 – Working with a 3x4 Matrix Keypad Page 180

Prepared exclusively for [email protected] Transaction: 5095


This will make the value of the variable var available to the main program as well as
any other functions. This method works well for simple programs, but it is frowned upon
for use in more complex programs. The ability for the main program and all functions to
interact with a variable can lead to problems when debugging very complex code. There
are more complex methods of variable management that can be employed in these
cases, but for the purposes of the keypad program, the global option will work well.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 181

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will connect the switch matrix keypad to your breadboard
and create two programs that utilize the keypad.

ACTIVITY #1 – ADDING THE SWITCH MATRIX


In this activity you will add the switch matrix keypad to the circuit you built in Lesson B-
6. Adding the keypad to the circuit will allow you to add keypad functionality to the
existing LED, piezo buzzer, and pushbutton switch.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 182

Prepared exclusively for [email protected] Transaction: 5095


STEP #1
The keypad will require 7 connections to GPIO. Since the Raspberry Pi does not have 7
GPIO pins grouped together in a row, connecting the keypad will require the use of
some jumper wires. GPIO16, GPIO20, and GPIO21 can be used to minimize the
amount of jumper wires required, but four of the pins will have to be jumper wired to
other GPIO pins.

Since space is tight around the wedge, it's best to insert the jumper wires before
inserting the keypad. Insert four jumper wires between the following locations:

Jumper wire between I16 and I24

Jumper wire between I11 and I23

Jumper wire between I9 and I22

Jumper wire between I8 and I21

Lesson 7 – Working with a 3x4 Matrix Keypad Page 183

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The next step is to make the keypad connector
compatible with the breadboard sockets.

Insert the male-to-male header adapter into the


keypad connector. This will allow the keypad to be
connected directly to the breadboard sockets.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 184

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Now that the keypad has male pins available it can be connected directly to the
breadboard.

Connect the keypad to J18 through J24, making sure that the columns of the keypad
(no black marks) are connected to J18 through J20. This will leave J21 through J24
connected to the row pins.

Please note that to align the three column connections with GPIO16, GPIO20, and
GPIO21, the face of the keypad must be facing away from the wedge. The image above
shows the white label adhesive side of the keypad.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 185

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – CREATING A PROGRAM TO DISPLAY KEYPAD PRESSES
In this activity you will create a program that will display any buttons that are pressed on
the keypad. This will be a fairly long program, but if you follow the steps closely,
everything should work well. If your program does not work at the last step, verify the
keypad connection using Activity #1, and then double-check each step in Activity #2 and
verify each block of code is correct.

STEP #1
Open a new program in Thonny. This program will use the RPi.GPIO and time modules
and will use BCM pin numbering. Import these modules separated by a comma, and set
the GPIO pin mode to BCM:

import RPi.GPIO as GPIO, time


GPIO.setmode(GPIO.BCM)

STEP #2
This program will use 7 GPIO pins to communicate with the 3x4 matrix. Each of these
would need their own line of code for setup, but there is another way. The inputs (rows)
and outputs (columns) can be grouped together as lists to keep the pins more organized
and save a few lines of code.

Create a list named inputs that contains 12, 25, 24, and 23. Create a list named outputs
that contains 16, 20, and 21.

inputs = [12,25,24,23]

outputs = [16,20,21]

Lesson 7 – Working with a 3x4 Matrix Keypad Page 186

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The lists you created in the last step will allow you to set up all inputs and outputs using
only two more lines of code.

Using the lists from Step #2, set up all input pins as internally pulled down (active high),
and set up all output pins as outputs. Remember to replace the pin numbers in the
setup commands with the appropriate list name.

GPIO.setup(inputs, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)


GPIO.setup(outputs, GPIO.OUT)

The setup lines will perform the setup repeatedly for each value present in the lists.

STEP #4
The program will need to know what button value should be assigned to each location in
the matrix. You can do this by creating lists with for each row that contain the button
values.

Using r1, r2, r3, and r4 for variable names, create a list for each row that contains the
buttons in that row, starting with column 0 on the left and moving to column 2 on the
right:

r1 = ['1', '2', '3']

r2 = ['4', '5', '6']

r3 = ['7', '8', '9']

r4 = ['*', '0', '#']

Lesson 7 – Working with a 3x4 Matrix Keypad Page 187

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
The next step will be to create a function named check() that will be called any time
you want to check all rows for a high signal. A variable named row will be set to 0 to set
an initial state. If this is not done, the non-zero check for row at the end of the function
will fail as the program does not yet know the value of row. Each row will be checked for
a high, and if that GPIO pin is high, row will be set equal to the row value, r1 through r4.

Create this function:

def check():
row = 0

if GPIO.input(12) == True:

row = r1

elif GPIO.input(25) == True:

row = r2

elif GPIO.input(24) == True:

row = r3

elif GPIO.input(23) == True:

row = r4

Use elif statements for the last three to show that these are connected to the first if
statement.

In this case, the four if statements will function exactly the same as using if/elif, but
it's good to get into the habit of connecting related conditions together properly. You
might make a modification to this program later that could result in the statements not
evaluating properly if multiple if statements are used instead of if/elif.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 188

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
When this function is called later in the program, the value of row will now be equal to
either 0 or r1 through r4, based on whether or not a key was pressed. The next step will
be to add an if statement that will verify that a button was pressed and print the name of
the button. This can be done by checking that the value of row is not 0. If the value of
row is still 0 then no button was pressed, and no further action is needed.

Add an if statement to the bottom of the function that checks to see if row is non-zero. If
so, create a variable named selection that's equal to the row value, using an index
position of [col]. The col variable will be created lower in the program when each
column is made high. Also, inside this if statement, add a print statement that will print
'The key pressed was ' and the value of selection. This must be part of the non-zero
if statement, otherwise the function will try to print selection before a selection has
been made, resulting in errors. Add a time.sleep value of 0.3 at the end of the if block
to keep the same key press from being registered multiple times:

elif GPIO.input(23) == True:

row = r4

if row != 0:

selection = row[col]

print('The key pressed was ' + selection)

time.sleep(0.3)

Make sure that your non-zero check is properly indented so it will run as part of the
check() function. Also, ensure that you use if and not elif for this block. Using elif
will cause this block to be connected to the row selection block. Once a row is selected,
no other elif statements will be evaluated, and the selection / printing block will be
skipped entirely.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 189

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Now that the check() function has been completed, it's time to create the main loop for
the program. This loop will push a column high, set the col variable equal to the column
number, run the row check function, and the return the column low. This column high
loop will need to run once for each column.

Create a while True: loop that contains code to push the following GPIO pins high,
set the column value, run the check() function, and return the GPIO pin low:

GPIO21 will represent column 0

GPIO20 will represent column 1

GPIO16 will represent column 2

while True:

GPIO.output(21,GPIO.HIGH)

col = 0

check()

GPIO.output(21,GPIO.LOW)

GPIO.output(20,GPIO.HIGH)

col = 1

check()

GPIO.output(20,GPIO.LOW)

GPIO.output(16,GPIO.HIGH)

col = 2

check()

GPIO.output(16,GPIO.LOW)

Lesson 7 – Working with a 3x4 Matrix Keypad Page 190

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
The program is now ready to run.

Run the program and press some of the keypad buttons. The buttons should be printed
in the console. Due to the use of the while True: loop, there is no way to gracefully
exit the program, so use the stop button in Thonny when you want to end the program.

If the program is not working as expected, double check the keypad connection and
each step in the program. Do not proceed to Activity #3 until the program is working
properly.

The complete program is listed on the next page for your reference.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 191

Prepared exclusively for [email protected] Transaction: 5095


import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)

inputs = [12,25,24,23]
outputs = [16,20,21]

GPIO.setup(inputs,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(outputs,GPIO.OUT)

r1 = ['1', '2', '3']


r2 = ['4', '5', '6']
r3 = ['7', '8', '9']
r4 = ['*', '0', '#']

def check():
row = 0
if GPIO.input(12) == True:
row = r1
elif GPIO.input(25) == True:
row = r2
elif GPIO.input(24) == True:
row = r3
elif GPIO.input(23) == True:
row = r4
if row != 0:
selection = row[col]
print('The key pressed was ' + selection)
time.sleep(0.3)

while True:
GPIO.output(21,GPIO.HIGH)
col = 0
check()
GPIO.output(21,GPIO.LOW)
GPIO.output(20,GPIO.HIGH)
col = 1
check()
GPIO.output(20,GPIO.LOW)
GPIO.output(16,GPIO.HIGH)
col = 2
check()
GPIO.output(16,GPIO.LOW)

Lesson 7 – Working with a 3x4 Matrix Keypad Page 192

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – ADDING EXIT FUNCTIONALITY TO KEYPAD PROGRAM
In this activity you will add exit functionality to the keypad program you created in
Activity #2. This will allow you to exit the keypad program without errors being
generated the next time you try to use the GPIO pins in a program.

This will be accomplished by using the octothorpe (pound sign) to modify the value of a
variable named end. The modified end value will cause the main while loop to stop so a
GPIO.cleanup can be executed prior to exiting the program.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 193

Prepared exclusively for [email protected] Transaction: 5095


STEP #1
The first step will be to add a variable named end to the program and make the while
loop dependent on that variable.

Using the program from Activity #2 as a starting point, add a variable named end and
set it equal to 0. Add this line just above the while loop. Also change the while loop
from while True: to be dependent on end being equal to 0:

if row != 0:

selection = row[col]

print('The key pressed was ' + selection)


time.sleep(0.3)

end = 0

while end == 0:

GPIO.output(21,GPIO.HIGH)

col = 0

check()

GPIO.output(21,GPIO.LOW)

This will allow the while loop to run only when the value of end is 0. If end is changed to
anything but 0, the while loop will no longer run, and any code below it in the program
will run.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 194

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The end check code should only run if a key has been pressed, so the most logical
location is below the row non-zero check that's watching for key presses.

Add an if statement to the row non-zero block that checks to see if selection is equal to
'#'. If so, assign the variable end to be global, and set end equal to 1:

if row != 0:

selection = row[col]

print('The key pressed was ' + selection)

time.sleep(0.3)
if selection == '#':

global end

end = 1

The global command will allow the variable named end to be accessed by the entire
program. The new end value of 1 will cause the while loop to stop running and run any
code after it in the program.

STEP #3
The last addition will be a GPIO.cleanup line at the very end of the program that will run
when the while end == 0: loop is no longer True. Add GPIO.cleanup to the end of the
program:

GPIO.cleanup()

Lesson 7 – Working with a 3x4 Matrix Keypad Page 195

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Run the program.

The program should run just like it did in Activity #2, but now the program will exit if # is
pressed. If the program is not working as expected, double check each step in this
activity.

The complete program is listed on the next page for your reference.

Leave the circuit assembled for use in Lesson B-9.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 196

Prepared exclusively for [email protected] Transaction: 5095


import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)

inputs = [12,25,24,23]
outputs = [16,20,21]

GPIO.setup(inputs,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(outputs,GPIO.OUT)

r1 = ['1', '2', '3']


r2 = ['4', '5', '6']
r3 = ['7', '8', '9']
r4 = ['*', '0', '#']

def check():
row = 0
if GPIO.input(12) == True:
row = r1
elif GPIO.input(25) == True:
row = r2
elif GPIO.input(24) == True:
row = r3
elif GPIO.input(23) == True:
row = r4
if row != 0:
selection = row[col]
print('The key pressed was ' + selection)
time.sleep(0.3)
if selection == '#':
global end
end = 1

end = 0

while end == 0:
GPIO.output(21,GPIO.HIGH)
col = 0
check()
GPIO.output(21,GPIO.LOW)
GPIO.output(20,GPIO.HIGH)
col = 1
check()

Lesson 7 – Working with a 3x4 Matrix Keypad Page 197

Prepared exclusively for [email protected] Transaction: 5095


GPIO.output(20,GPIO.LOW)
GPIO.output(16,GPIO.HIGH)
col = 2
check()
GPIO.output(16,GPIO.LOW)

GPIO.cleanup()

Lesson 7 – Working with a 3x4 Matrix Keypad Page 198

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Can variables assigned within a function be accessed by the main program? If
not, what must be done to make them accessible to the main program?

2. Which option uses more GPIO pins, separate switches or matrix style switches
with shared column and row connections?

3. Where are some places that membrane type switches might be seen in your
daily life?

Answers can be found on the next page.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 199

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Can variables assigned within a function be accessed by the main program? If
not, what must be done to make them accessible to the main program?

ANSWER: No, variables assigned within a function are only accessible within
the function. For a variable to be used outside the function you must add the
global command to the variable inside the function.

2. Which option uses more GPIO pins, separate switches or matrix style switches
with shared column and row connections?

ANSWER: Separate switches use more GPIO pins. You can use less pins by
using a matrix style switch with shared column and row connections.

3. Where are some places that membrane type switches might be seen in your
daily life?

ANSWER: The microwave and most computer keyboards.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 200

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to work with matrix style keypads which allow you to use
GPIO pins more efficiently. You also learned to change the scope of variables used
within a function.

In the next lesson, you will learn to use GitHub to access library files and advanced
techniques for working with lists. You will also learn about the different versions of
Python and the issues that can arise from attempting to run a program in the wrong
version.

Lesson 7 – Working with a 3x4 Matrix Keypad Page 201

Prepared exclusively for [email protected] Transaction: 5095


LESSON 8

GITHUB AND
PYTHON 2 VS PYTHON 3

OBJECTIVE
In this lesson you will learn to use GitHub to access library files. You will also learn
about the different versions of Python and factors you will need to be aware of if you
attempt a project written in a different version than you are currently working with.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, mouse, and the internet

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Connecting the Raspberry Pi to the Internet, Updating the Raspberry Pi Software


(Lesson A-9)
• Importing Modules (Lesson A-13)

Lesson 8 – GitHub and Python 2 vs Python 3 Page 202

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will learn to use GitHub to access library files and some interesting
factors to consider when working in Python 2 vs. Python 3.

LIBRARY FILES
Different companies sell many devices that interface with the Raspberry Pi, such as
displays, cameras, and all sorts of sensors. Due to the complexity of these devices, it's
not always possible to write a quick Python program that will talk to the device. Some of
these devices require complex methods of communication, but the good news is that
you don't have to worry about that part of the program.

Devices like these have software called libraries (or modules) that have been written to
act as an interpreter for your program. For example, if you want to print this page out
using a printer, you would just click a single print button and a page would come out of
the printer. Behind the scenes, that button press triggered some very complex
communication between your computer and the printer to print that page.

It's much the same with library files. Your program might issue a command like
TEMP_SENSOR.check() which could return a value in Fahrenheit. What you don't see is
the file called TEMP_SENSOR.py that might be hundreds of lines of code built
specifically to talk to the temperature sensor. One line of your program asks
TEMP_SENSOR.py for the temperature and it does all the hard work for you, so your
program can use the temperature value for whatever you like.

These library files need to be available to everyone that might purchase a device, so
most of these library files will be available on a website called GitHub. Some hardware
vendors may host the library files themselves, with links in the product description, but
most libraries are hosted on GitHub.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 203

Prepared exclusively for [email protected] Transaction: 5095


WHAT IS GITHUB?
GitHub.com is a website that's used for storing and distributing software packages.
GitHub also has many other features for developers that allow many people to
collaborate on software projects, as well as track all changes that are submitted to a
project.

GitHub is organized into many folders, known as repositories, which you may see called
repos for short. Each top-level repository is named after the user or organization that
created that repository. Most hardware vendors that sell Raspberry Pi components will
have a repository that contains the code to run their products. GitHub organization
pages will be located at https://github.com/ followed by the name of the company. Here
are a few notable GitHub pages:

https://github.com/42electronics

https://github.com/adafruit

https://github.com/sparkfun

These top-level organization folders will contain folders for any hardware that the
organization has made available. The search bar at the top of the page will allow you to
search for the specific name of different hardware devices, while limiting results to only
that organization, or allow results from all of GitHub.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 204

Prepared exclusively for [email protected] Transaction: 5095


CLONING A LIBRARY FROM GITHUB
The most common interaction you will have with GitHub is to clone a repository. Cloning
means that you make a copy of a specific folder on GitHub, so that you now have
access to those same files locally on your Raspberry Pi. This way your Raspberry Pi will
have the files needed to communicate with more complicated devices you may want to
connect to the Pi.

The first step to cloning a library is to connect your Raspberry Pi to the internet. After
that, make sure it's fully up to date by opening a Terminal window and running:

sudo apt-get update

The Pi maintains a list of all installed software packages and their version numbers. The
apt-get update command causes the Pi to go check the internet for the latest version
numbers of all available packages. This lets the Pi know if any of its installed packages
are not the latest version, and if any new packages are to be installed, which version is
the latest available. No packages are modified by the update command, only the list of
currently available package versions.

The library that you need to clone might only be part of the software that you need to
communicate with the new device. Additional software packages could be required, and
if so, these will be mentioned in the tutorial for your specific device.

The next step will be to install any of these additional packages. In the example below
these packages are python-imaging and python-smbus:

sudo apt-get install python-imaging python-smbus

The install command will let you know if any of the requested packages are already
installed on your Pi, and whether or not they are the newest version. If a package is not
found on your Pi, you will be told how much space the new package will consume after
installation and asked for confirmation to proceed with the installation.

The next step will be to install the git package. This package is used by the Pi to
communicate with GitHub. It could have been installed with the previous install
command, but some tutorials will call this out as an extra step:

sudo apt-get install git

Lesson 8 – GitHub and Python 2 vs Python 3 Page 205

Prepared exclusively for [email protected] Transaction: 5095


This will install the git package, if needed, or let you know that your previously installed
git package is already up to date.

None of these commands so far have been dependent on your location in the directory
structure of the Pi. The update and install commands will function the same from any
directory. The upcoming clone command works differently. It will create a copy of the
remote repository in your current working directory.

If you were previously working in /home/pi/Desktop/projects/2018, and you


execute the clone command in that folder, then that's where the cloned folder will
reside. This might not be very convenient when trying to find it later. For this reason, it's
recommended that you clone remote git repositories into your home directory located at
/home/pi.

Opening Terminal will automatically drop you into /home/pi as your default directory. If
you have moved into another directory during the course of your work, you can get back
to /home/pi using a couple of different commands:

cd /home/pi

or

cd ~

Either of these commands will drop you into /home/pi, regardless of your current
location.

Now that you're in the home directory, you can clone the remote repository. The next
command will use the git package to clone the repository that you need:

git clone https://github.com/organization_name/name_of_repository.git

The name of the organization or user would replace organization_name and the
actual name of the repository would replace name_of_repository.git. If the
organization is using additional folders to organize their repositories there could be
additional /folder_name/ entries in the path as well.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 206

Prepared exclusively for [email protected] Transaction: 5095


WHEN TO CLONE A REMOTE LIBRARY
The cloning process will need to be completed for each new device you wish to add,
using its own specific library. If you need the library for an Adafruit SSD1306 OLED
screen, you would clone the specific Adafruit Python SSD1306 folder from GitHub, and
go through whatever additional installation steps that are required by that vendor, for
that specific library.

Also, keep in mind that GitHub is used for many platforms other than the Raspberry Pi
and Python. Just because you find a folder for your specific hardware device does not
mean it's compatible with Raspberry Pi and Python. There are many other hardware
platforms and languages available on GitHub, so prior to installing anything, always
confirm the library is for the correct platform and language that you plan to use.

The best way to ensure library compatibility is to obtain GitHub library links directly from
setup tutorials provided by the hardware vendor. They will often have links on the
website's product page that contain walkthroughs on how to get that device running on
different platforms.

While GitHub is currently the largest host for repositories, you may also find files hosted
on GitLab, BitBucket, SourceForge, or other legitimate repository hosting sites.

Avoid downloading libraries from random sites found using a


Google search. Libraries, much like other types of files that can
be downloaded from the internet, can contain code that may
harm your Raspberry Pi file system, or even worse, other
systems on your home network. Reputable vendors will make
their code available on sites like GitHub, where many users can
review the code and take the appropriate action if the software is
found to contain malicious code.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 207

Prepared exclusively for [email protected] Transaction: 5095


COMPLETING THE LIBRARY INSTALLATION
Some libraries simply need to be present on the Pi to be used by your programs.
Accessing the library can be accomplished by importing the libraries modules into your
program.

Other, more complicated libraries require an additional installation step be completed


prior to using the library. This installation step will compile the library modules based on
information gathered from your system. This information could consist of criteria like
which version of a package you have installed. Something like, if packageA is 6.5.3 or
below then use module653 during the installation.

The vendor's tutorial will inform you if this additional installation step is required. These
installations are usually simple and only involve moving into the cloned directory and
executing a file named setup.py that can be found inside:

cd cloned_directory

sudo python setup.py install

This will fully install the library and any of its files can now be accessed by your
programs.

While already inside the new directory you might want to check to see if it contains any
example programs. These can often be found in a subfolder named examples. These
can be a good way to check out what the modules can do with your new hardware.
Most tutorials will have you run these example programs as a way to quickly get familiar
with the modules and their capabilities.

In online tutorials you may see mention of pip or easy_install. These are alternate
installation methods that some packages support. If one of these installation methods is
required, then the tutorial will walk you through the exact commands to be used to
complete the installation.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 208

Prepared exclusively for [email protected] Transaction: 5095


VIEWING CODE INSIDE FILES ON GITHUB
Another great feature of repository sites like GitHub is the ability to look at a file's code,
without even downloading the file. This can be done right inside the web browser, so
you can try to figure out how a program works, or if it is applicable to the project that
you're working on, before cloning the whole repository.

Since GitHub is organized in a logical file format, navigation is about the same as
navigating folders on your PC or the Raspberry Pi. The top-level organization or user
folder will be located at:

https://github.com/organization_name

If you aren't sure of the specific URL of the organization you're looking for on GitHub,
you can always use the search available at the top of https://github.com. This will
search GitHub for that term and return all results for any users (organizations),
repositories, code, and much more.

The list of result categories can be clicked on to narrow the displayed results. The
search for "42 Electronics" yields a large amount of results but clicking on the "Users"
result will refine the displayed results to 42 Electronics, as well as a few individual users
whose user profiles fit the search criteria.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 209

Prepared exclusively for [email protected] Transaction: 5095


Clicking on the user profile for 42 Electronics will take you to the 42 Electronics page.
Here you will find any repositories we have made available. The Overview tab will have
the most popular content, and the Repositories page will list all available repositories.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 210

Prepared exclusively for [email protected] Transaction: 5095


Clicking on a repository name will take you inside that repository, and you will see that
reflected in the path provided at the top of the page. In the image below, we are located
in the level_a repository of the organization named 42electronics:

Clicking on a folder name in this area will take you inside that folder. Clicking on any
supported type of image, text, or code file will open a viewer within the browser,
allowing you to quickly review these types of files.

Code files are the most interesting thing you will view in this area. The code will open in
a window that will display the entire program with line numbers, colorized markup to aid
in clarity, and the ability to highlight and copy code directly from within this window.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 211

Prepared exclusively for [email protected] Transaction: 5095


When done viewing the file you can click the links at the top of the page to return to the
last directory, the whole repository, or all the way back to the organization page.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 212

Prepared exclusively for [email protected] Transaction: 5095


PYTHON 2 AND PYTHON 3
Back in Lesson A-11, we talked briefly about differences in the print command between
Python 2 and Python 3. The Raspberry Pi has both versions installed but it's important
to understand some key points about running programs in one or the other.

Python installations live in what's known as an environment. This environment includes


all the files and settings that Python needs to run programs. The Raspbian OS on your
Raspberry Pi has both Python 2 and Python 3 available for use.

The command line (Terminal) has both versions of Python available using different
commands. Programs can be run in Python 3 by using the command python3 before
the name of the program you want to run. The command line name for Python 2 is
slightly less logical as the command python is used before the name of the program
you want to run. This happened because Python 2 was, at one point, the one and only
Python, so this command made perfect sense.

When Python 3 was released, there were already a lot of programs running version 2
that were using the python command. Python 3 needed a new command to
differentiate it from 2, so python3 was used. To summarize, when on the command
line:

python – runs program in Python 2

python3 – runs program in Python 3

This command information can be applied to the library install from the previous section.
The command sudo python setup.py install was used to install the library. The
use of python in this command means this library was only installed in the Python 2
environment. Attempting to run programs in Python 3 that require files from this library
will result in errors that the library is not installed.

Some libraries are written to support installation in either Python 2, Python 3 or both. If
the library is compatible it can be installed in the Python 3 environment by using the
python3 command:

sudo python3 setup.py install

This will install the library in the Python 3 environment, making it available for any
Python 3 programs that need to access files contained in the library.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 213

Prepared exclusively for [email protected] Transaction: 5095


THONNY AND PYTHON
Thonny has the ability in its settings to switch between Python environments, but it only
supports version of Python above 3.4. It will not support running programs in Python 2.
This normally won't be a problem since Python 3 is the newer language, and many of
the more recent tutorials and Raspberry Pi projects are written in Python 3.

You need to be aware of the difference in environments when working in Thonny on


programs that require library installations. Most tutorials will have you open Terminal,
clone a repository, and install the library. If the command used is sudo python
setup.py install, then the library will only be installed in Python 2 and will not be
available to the program if run through Thonny.

Thonny is a great tool for editing programs, but sometimes the best solution is a mix of
Terminal and Thonny. Having both windows open at once, each with their own purpose.
Terminal can be used to install libraries, and quickly execute Python 2 programs using
the python command. Even with Thonny running its Python 3 environment, you can still
open, modify and save Python 2 programs without any problems. As long as you don't
execute the Python 2 program in Thonny, everything will run well. Here is what this
workflow might look like:

1. Terminal – clone git repository and install library in Python 2


2. Terminal – cd into library example directory and run example program using
python
3. Thonny – Navigate to library example folder and open program
4. Thonny – makes changes to program and save as a new file name to avoid
overwriting the original
5. Terminal – run your new program in Python 2 using sudo python new_program

Leaving these two windows open will allow you the freedom to quickly execute Python 2
programs using Terminal while using the advanced editing features available in Thonny.

Attempting to make Python 2 programs run in Python 3, and vice-versa, is known as


porting. This can sometimes create very complex issues that are best avoided by
running programs in the environment in which they were written. Using Terminal to
execute Python 2 programs and Thonny to execute Python 3 programs will allow you a
place to run each type of program, without modifying any Python settings in Thonny.

Don’t worry if this sounds a bit confusing. You’ll be practicing running Thonny and
Terminal side by side in the activities for this lesson.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 214

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will clone and install a GitHub repository, explore that
repository, and practice running Terminal and Thonny side by side.

ACTIVITY #1 – CLONING AND INSTALLING A GITHUB REPOSITORY


In an upcoming lesson you will be working with an IC (integrated circuit) called an
MCP3008. Interfacing the Raspberry Pi with this IC requires a library be installed that
can be used for communication. In this lesson you will clone the GitHub repository
where this library is located and complete the installation of this library on your Pi.

STEP #1
The first step is to ensure your Pi knows the latest version numbers for packages that
are currently available.

Ensure your Pi is connected to the internet and open a Terminal window. Once the
Terminal window is open, type the command sudo apt-get update and press enter:

sudo apt-get update

Your Pi will check the internet and update its internal list with the latest available
package versions.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 215

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The next step is to install a few packages required for the proper operation of the
MCP3008 library. You will also be installing git during this step. Any packages already
present and up-to-date on your Pi will be skipped.

Install the packages named build-essential, python-dev, python-smbus, and git by


entering the command below:

sudo apt-get install build-essential python-dev python-smbus git

If any of these packages need to be installed or updated, you will be presented with the
amount of storage space required to make the changes.

Type y followed by enter to confirm the changes. Wait until the installation is fully
complete and you are presented with a command prompt before proceeding to the next
step.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 216

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The required packages, along with git, have now been installed and you're almost ready
to clone the remote repository. Make sure this clone is going to end up in the proper
place by ensuring you're located in the home directory.

Type cd ~ followed by enter to change to the home directory:

cd ~

NOTE: This step is not needed in this specific instance since you have been
located the home directory since Step #1. This step is good to remember,
however if you happened to start this process in a previous Terminal session that
may not have been located in /home/pi. Performing this step in either instance
will always ensure that your cloned folders end up located in /home/pi.

NOTE: If you didn't enter the ~ (tilde) correctly, you might have accidently
entered the ` (backtick) that shares the same key. If this happened then you are
likely looking at the interactive > prompt, meaning the command line is waiting for
more information. CTRL-C (holding the control key while typing c) will end the
interactive prompt and return you to the main command prompt, so you can try
again.

STEP #4
It's now time to clone the remote repository for the MCP3008. Adafruit maintains a
repository for this IC in their main GitHub repository.

Type the following command to clone the remote MCP3008 repository into your local
/home/pi directory:

git clone https://github.com/adafruit/Adafruit_Python_MCP3008.git

If you receive any errors, verify every aspect of the command above is correct.
Capitalization is critical along with every _ and / mark. If the command is correct you will
see status activity related to the download.

Wait until this activity is complete before proceeding to the next step.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 217

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
The library has been cloned and you now have your own copy on the Raspberry Pi. The
setup file that you need to execute is located inside the new directory named
Adafruit_Python_MCP3008. Change into this directory by using the following command:

cd Adafruit_Python_MCP3008

Again, accuracy is critical. If you receive any messages about a directory not being
found, verify every character in your command is correct. You must be located in this
new directory to execute the setup command in the next step.

STEP #6
This specific library requires the additional install step talked about earlier in this lesson.

Now that you're located inside the new folder containing the library files, you can run the
setup file by running the following command:

sudo python setup.py install

This will compile the library elements based on your installed packages and make the
library elements available in the Python 2 environment. This library is also compatible
with Python 3.

Install the library into the Python 3 environment by running the following command:

sudo python3 setup.py install

Both Python 2 and Python 3 will now have access to the files contained in this library.
Leave the Terminal window open as you will use it in the next activity.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 218

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – EXPLORING THE ADAFRUIT MCP3008 REPOSITORY
Now that you have installed the MCP3008 library, take a look at the files contained
inside.

STEP #1
For this activity you will use the Terminal window from Activity #1 as a starting point. In
this window you will already be located in the /home/pi/Adafruit_Python_MCP3008
directory.

Type pwd to ensure you are in the correct directory:

pwd

The console will report that you are located in the /home/pi/Adafruit_Python_MCP3008
directory. Type ls to list the contents of the current directory:

ls

All files and folders located in the current directory will be listed:

Terminal will color code directories in blue and Python, setup, and text files in gray.
Some other file types may show up in different colors, but none of those file types are
present in this directory.

From the directory listing you can see various setup files, a readme file, and a few
directories.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 219

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Take a look at the README.md file. This file contains information about the library and
any installation instructions or things to know about this library. These same instructions
are often included in vendor tutorials, but it's good to know how to access them if you
need.

The Nano command line editor can be used to open the README.md file. Open the file
by running the following command:

sudo nano README.md

This will open the README file in the Nano text editor. Scroll through the file using the
up and down arrows. The installation instructions will look familiar as they are the ones
you just ran to get the library cloned and installed.

Press CTRL-x to exit the Nano editor and you will be brought back to the command line.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 220

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Arguably, the most import part of this library, besides the code that communicates with
the MCP3008, is the folder of example code. This folder contains files that have been
pre-built by the vendor to get your connection with the MCP3008 tested, without you
having to write code from scratch. Reviewing this example code can help you
understand how the library is intended to be used and its capabilities.

Change directories into the examples directory by running the following command:

cd examples

Once inside the examples directory, list the directory contents using the ls command:

ls

You will see two files named differential.py and simpletest.py.


differential.py is a program designed to display the difference between channels 0
and 1 on the MCP3008. simpletest.py will poll channels 0 through 7 of the MCP3008
every .5 seconds and display the results in a table format. Check out simpltest.py in
the next step.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 221

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
To check out the code in simpletest.py, you will need to open it in Nano.

Open simpletest.py in Nano by running the following command:

sudo nano simpletest.py

The file will open in Nano, so you can view the code. Some of the most important lines
are 13 through 16, where the GPIO pins are configured. These might have to be edited
based on how the IC gets wired to the Raspberry Pi. If, for example, another part of
your project is already using pins 18, 23, 24, or 25, the values on lines 13 through 16 on
this program would need to be modified according to use the new GPIO pin locations.

Don’t be concerned with that for the moment though. You will learn more about this in
the lesson when you wire up the MCP3008 to the Pi and use simpletest.py as a
template for other programs.

Press CTRL-x to exit the Nano editor and close the Terminal window.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 222

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – VIEWING CODE ON GITHUB
In this activity you will use the web browser on your Raspberry Pi to view Python code
on github.com. You will find a repository and navigate down to the Python files to view
the code.

STEP #1
The first step to reviewing code on GitHub is to open a web browser window. Ensure
your Pi is connected to the internet and open a web browser window. Using the
browser, go to https://github.com.

STEP #2
Now that you are on GitHub, you can look for a repository.

Type 42 Electronics into the search bar and press enter. Click on Users, then click
on the user named 42 Electronics.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 223

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
You will now be at the organization page for 42 Electronics.

Click on the Repositories tab, then click on the repository named level_a:

STEP #4
Inside level_a you will be presented with the list of folders contained in that repository.

Click on the folder named lesson 17 and you will see the contents of that folder. Click on
the file named Activity3_Four_LEDs_10_Times.py and it will open in your browser:

Lesson 8 – GitHub and Python 2 vs Python 3 Page 224

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
You are now viewing the code that makes up the Python file.

Scroll down below the license information to view the Python code. You can now see
the lines of code from Lesson 17 of Level A that made four GPIO pins blink 10 times:

This same process can be used to the view the code inside any repository that's publicly
available on GitHub.

Close out the web browser window when you are finished.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 225

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. In which Python environment will a library be installed when using the command
sudo python setup.py install from inside that library?

A. Python 2 only
B. Python 3 only
C. Both Python 2 and Python 3
D. Neither Python 2 or Python 3

2. Will installing one library cover all hardware that might be connected to a
Raspberry Pi, or will you need to install a library for each piece of hardware you
intend to connect to the Pi?

3. On github.com, can you view the contents of a Python file in your web browser
or do you have to clone the repository locally before being able to view the
contents?

Answers can be found on the next page.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 226

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. In which Python environment will a library be installed when using the command
sudo python setup.py install from inside that library?

A. Python 2 only
B. Python 3 only
C. Both Python 2 and Python 3
D. Neither Python 2 or Python 3

ANSWER: The library will be installed in the Python 2 environment.

2. Will installing one library cover all hardware that might be connected to a
Raspberry Pi, or will you need to install a library for each piece of hardware you
intend to connect to the Pi?

ANSWER: Each piece of hardware may have its own library that will need to be
downloaded and installed.

3. On github.com, can you view the contents of a Python file in your web browser or
do you have to clone the repository locally before being able to view the
contents?

ANSWER: You can view the contents in your web browser.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 227

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to work with library files including downloading files from
GitHub and installing the files locally on your Raspberry Pi. You also learned how to
work with files in Python 2 vs. Python 3.

In the next lesson, you will learn about analog signal processing with the Raspberry Pi.

Lesson 8 – GitHub and Python 2 vs Python 3 Page 228

Prepared exclusively for [email protected] Transaction: 5095


LESSON 9

ANALOG SIGNAL PROCESSING


WITH THE RASPBERRY PI

OBJECTIVE
In this lesson, you will learn how to interface the Raspberry Pi with analog signals, work
with integrated circuits, and add a few additional electronics concepts to your
knowledge base.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Assembled Circuit from Lesson B-7
• 1 x MCP3008 (Analog to Digital Convertor Integrated Circuit)
• 4 x Long Jumper Wires
• 6 x Short Jumper Wires

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Series vs Parallel Circuits and Ohms Law (Lesson A-3)


• Pulse Width Modulation (Lesson B-4)

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 229

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will learn how to interface the Raspberry Pi with analog signals. This
will be accomplished by learning more about ICs (integrated circuits) as well as some
other new electronics concepts.

VOLTAGE DIVIDERS
Back in Lesson A-3 you learned about how voltage is dropped across different
components. Voltage drop is the amount of voltage that a component in the circuit
consumes. The total of the voltage dropped across components will be equal to the
supply voltage. If 3.3V is the supply voltage, then 3.3V will be dropped across all
components in the circuit.

A voltage divider is a method of using two or more resistors to


make a new voltage level available. This voltage can be used to
power another device, shift a signal's level from 5V to 3.3V, or
many other uses.

A voltage divider is made up of at least two resistors connected


in series between supply voltage and ground:

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 230

Prepared exclusively for [email protected] Transaction: 5095


In a voltage divider, the ratio of voltage dropped in each resistor is equal to the ratio of
its resistance divided by the total resistance:

The single 1K ohm resistor makes up the entire resistance between supply and ground,
so it's dropping 100% of 3.3V.

The total resistance of two 1K ohm resistance in series is 2K ohms. Each resistor
makes up one half of the total resistance, so they each drop half of 3.3V, or 1.65V.

The total resistance of three 1K ohm resistors in series is 3K ohms. Each resistor
makes up one third of the total resistance, so they each drop one third of 3.3V, or 1.1V.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 231

Prepared exclusively for [email protected] Transaction: 5095


Measuring between any of these points and ground will result in different voltage levels
based on the amount of voltage dropped prior to that point:

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 232

Prepared exclusively for [email protected] Transaction: 5095


The resistors in a voltage divider do not have to be equal values. Resistors of differing
values can be used to fine tune the voltage output as needed, however the position of
the resistors in the voltage divider will affect the voltage, as seen below:

The voltage available at the center point can vary drastically by swapping the positions
of the resistors above.

In Activity #3, you will use a voltage divider to create new voltage levels. These voltage
levels will not be 3.3V or ground so an analog input will need to be added to the
Raspberry Pi in order to measure these signal levels.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 233

Prepared exclusively for [email protected] Transaction: 5095


ANALOG INPUT ON THE RASPBERRY PI
As you learned previously when working with PWM signals in Lesson B-4, the
Raspberry Pi does not have the native ability to handle analog signals. The GPIO pins
can input and output digital signals, made entirely of highs and lows, but they aren’t able
to understand anything in between, like the varying voltage levels of an analog signal.

This capability works well for most projects, but some will require the ability to monitor
an analog signal. Some sensors do not have digital capability and will only output an
analog signal of varying voltage level based on the conditions that sensor is
experiencing, whether it's varying light levels, varying input levels on a microphone, or
whatever else your sensor is meant to sense.

While digital signals are limited to only high and low, an analog signal can travel
anywhere between its supply voltage and ground. This means that an analog device
running on a supply voltage of 3.3V can output 3.1V, 2.3V, .5V, or any other voltage
level between 3.3V and ground. Rapid fluctuations in these analog signal levels can
even be used to create sound waves by driving a speaker.

One way to visualize digital signals versus analog is to think about a driving video
game. Most game controllers have analog inputs that allow for very fine steering and
throttle/brake application. Imagine if all of these switches were digital, only on or off.
Staying on the track would be a lot more difficult if your only options were hard left turn,
go straight, or hard right turn. The same would go for only having throttle and brake
options of 100% acceleration, coast, or 100% braking. Instead of these on/off type
inputs we have nice, smooth control sticks and triggers that allow for varying levels of
steering or throttle/brake based on your input.

Analog signals have many uses, but you may see them used to represent varying
sensor outputs. For example, a light sensor may represent the light value it's sensing by
varying an analog output. 0V might represent complete darkness and 3.3V might
represent full sunlight. Voltage levels in between will present the amount of light present
at the sensor, so a voltage level of .5V would represent a fairly dark light level, and 2.5V
would represent that a fairly bright light is present. Your program could be coded to
respond to these varying voltage levels and complete different tasks based on the
sensor voltage level.

To get analog signals converted into something the Raspberry Pi understands, you
must use another piece of hardware called an Analog to Digital Converter, also known
as an A/D Converter. An A/D Converter is an integrated circuit that will take in analog
signals and convert them into a digital format the Raspberry Pi can process.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 234

Prepared exclusively for [email protected] Transaction: 5095


INTEGRATED CIRCUITS
An integrated circuit or IC, commonly referred to as a "chip", is an electronic component
that contains an entire circuit in a compact, convenient package. ICs allow you to create
fairly complex circuits with only a few components, as most of the components making
up the circuit are contained within the IC.

ICs can contain large amounts of circuitry and some, like A/D converters, do not require
any additional components to operate. They only need to be supplied with power and
ground and they are ready to start converting analog signals into digital. All the complex
circuitry to complete this task is already inside the IC.

ICs will need to be connected to the rest of the circuit to perform the intended task. For
an A/D converter, the analog inputs would be connected to your analog sensor input,
and the digital output would connect to your Raspberry Pi.

IC DATASHEETS
The IC's datasheet will contain operating specifications like temperature and voltage
requirements, as well as the signal pinout for connecting the IC into a circuit.

The function of each pin will vary based on different models of ICs, so it's important to
make sure you're using a datasheet that matches your specific model. If the datasheet
is not linked in the project tutorial you're using, you can always find it by searching the
internet for the IC's model number followed by the word "datasheet". For example, "555
datasheet" or "MCP3008 datasheet" will show results that include PDF versions of the
datasheets for those ICs.

The model number of the IC will be printed across the top:

There may be additional information printed on top of the chip like the manufacturer's
logo, along with other information the manufacturer might use to identify the production
run. If you're searching for a datasheet and not finding any results, try the other
numbers from the top of the IC. As shown in the previous image, different
manufacturers print IC information differently, but the model number will be there
somewhere.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 235

Prepared exclusively for [email protected] Transaction: 5095


IC PIN NUMBERING
Before learning about what signals will be on each pin, it's important to learn the
numbering scheme used for all ICs. Learning IC numbering will allow you to properly
connect ICs in a circuit.

The most important aspect of IC pin numbering is locating pin one. Pin one is used as
the starting point for numbering pins on the IC. Pin one will be to the left of the notch at
one end of the IC. Some ICs also have a dot to indicate the location of pin one.

Once pin one is located, the pins are numbered in a somewhat counter-clockwise
direction. With pin one at the top, the pins will increase in value down the left side of the
IC, resuming at the bottom of the chip, and increasing up the right side of the chip:

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 236

Prepared exclusively for [email protected] Transaction: 5095


It's critical to properly identify the orientation of an IC before
using it in your circuit. Each pin on the IC has a very specific
job. If the IC was accidently rotated 180 degrees in the circuit,
the signals on each pin would differ from what the IC is
expecting, and the IC would likely be damaged beyond use. It
only takes a second to irreparably damage an IC or other
components in your circuit, so always verify pin one's location
before you attempt to use an IC in a circuit.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 237

Prepared exclusively for [email protected] Transaction: 5095


IC PINOUTS
How exactly the IC gets connected in your circuit will be determined by the ICs pinout,
or the map of what function each pin is used for on the IC. The pinout information will
show you exactly how each pin must be connected to function properly in the circuit.

Searching for the IC's model number followed by the word "pinout" is a good way to
quickly find images of an IC's pinout. The images will have very short names for the pin
descriptions, so you will likely need to refer to the site supplying the image or the IC's
datasheet to fully understand what the shortened names mean. For example, here is
the pinout for an MCP3008 A/D converter:

These short pin descriptions are a little


difficult to decode without some context:

Pins 1 through 8 – analog input channels 0


through 7

Pin 9 – Digital ground.

Pin 10 – Chip select or shutdown

Pin 11 – Digital communication in

Pin 12 – Digital communication out

Pin 13 – Clock signal for digital


communication

Pin 14 – Analog ground

Pin 15 – Voltage for reference

Pin 16 – Supply voltage

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 238

Prepared exclusively for [email protected] Transaction: 5095


This may look a little complicated, but the voltage
pins and grounds are actually tied together when
using this IC with a Raspberry Pi. That leaves four
pins for digital communication of the values to the
Pi, and the input to each channel. Here is a
diagram of the MCP3008 wired in a circuit
according to the pinout:

Most tutorials will walk you through connecting an


IC into your circuit, but it's also good to
understand how they came up with the
information in the tutorial.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 239

Prepared exclusively for [email protected] Transaction: 5095


ESD AND IC HANDLING PRECAUTIONS
Having all this compact circuity packed inside an IC is very convenient, but it comes at a
price. The components housed inside the IC are very tiny, and some parts of the circuit
can be easily damaged by static electricity. In electronics this is referred to as ESD or
Electro-Static Discharge.

When two materials are in contact with each other, electrons can flow from one material
into another. Separating the two materials can leave excess positive or negative charge
in the materials that is looking for a place to equalize by discharging. When this
discharge happens, it's known as static electricity.

You have probably been shocked by touching a door handle at some point in your life.
This is because you may have built up excess charge by walking around on carpet. This
excess charge was looking for somewhere to go and the metal of a door handle is a
good conductor. The intensity of the spark generated can vary based on how much
contact you had with the carpet and the humidity of the room, but often the spark can be
felt, or sometimes even seen.

To see or feel a spark from static electricity means that thousands of volts of charge
were present on your finger. This charge isn’t dangerous to humans as all of that
voltage is only accompanied by a minute amount of current.

While it takes thousands of volts to generate a spark that we can feel or see, ICs can be
damaged by much lower voltage levels. This is because the connections inside the IC
are extremely small and are not generally built to handle thousands of volts flowing
through them. Some ICs are much more static sensitive than others, but it's good to
take precautions as if the IC you are handling might be damaged by ESD.

ICs are less likely to be damaged once they are connected to your circuit and have a
ground path available. ICs are most likely to be damaged when they are not yet
connected in your circuit, so the special handling considerations below should be
followed.

1. If possible, do your IC work on an ESD anti-static mat. An ESD anti-static mat,


unlike most workbench surfaces, is made of a special material that does not
generate static electricity when most materials rub across it. ESD mats will also
come with wiring connections that will allow for grounding of the mat, and some
will also come with a wrist strap to wear that will ensure you never build up a
damaging level of excess electrons.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 240

Prepared exclusively for [email protected] Transaction: 5095


2. If an anti-static mat is not available, always touch something metal before
handling an IC to help lower the amount of excess electrons that you might pass
into the IC. Anything metal will work, but the best options are metal that is
grounded, like a metal computer chassis. This will allow the maximum amount of
excess electrons to flow into the grounded chassis, meaning there are even
fewer that might accidently flow into the IC.

3. Only handle an IC by the ends,


where there are no pins. By not
touching the pins you reduce the risk
of accidently imparting excess
charge into some part of the IC that
could be damaged.

4. When not in use, the IC should be stored in a static-shielded bag. Standard


plastic bags can actually generate static electricity during the process of opening
and closing, so they should not be used for storage. Some types of pink plastic
bags are made from special materials that allow them to store ICs safely. Its
generally a good practice to retain any IC storage bag that you get with a part, so
it can be used later for safe storage.

As stated previously, not all ESDs are extremely static sensitive. An ICs sensitivity to
ESD damage will increase as the density of the circuitry inside the IC is increased.
Devices like computer CPUs are extremely sensitive due to the large number of
transistors packed inside, and additional precautions like an ESD wrist strap should be
used when handling devices like CPUs. For hobby-level ICs, like the MCP3008 in this
kit, the four steps listed above will help you avoid damaging any ICs that you plan to use
with Raspberry Pi projects.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 241

Prepared exclusively for [email protected] Transaction: 5095


DIGITAL COMMUNICATION
The digital communication that allows the MCP3008 to talk to the Raspberry Pi is
accomplished by using an interface called SPI, or Serial Peripheral Interface. The
Raspberry Pi has two options for communicating with SPI devices, one is done using
hardware and the other is done in software.

Using hardware SPI means that a small part of the BCM2835 processor on the
Raspberry Pi is devoted to handling SPI communication. This method requires you to
enable a setting in the Raspbian OS to "turn on" the hardware SPI portion of the
BCM2835, then hardware SPI must be done via a few specific GPIO pins after the
setting is enabled. Hardware SPI can yield better performance in programs where large
amounts of data are being transferred using SPI, but these increases will not be seen
when creating simple programs like the ones in this level.

Software SPI means that your program uses GPIO pins going high and low very rapidly
to emulate hardware SPI. When software is used to emulate hardware it's often referred
to as "bit banging". The benefits of this method are that no setting changes are required
on the Pi, and any of the configurable GPIO pins can be used for SPI communication. A
library must be installed for your program to understand how to communicate with other
devices using software SPI.

Software SPI communication requires four connections:

CLK – Serial clock used to synchronize communication between devices

CE – Chip enable allows multiple devices to share CLK, MISO, and MOSI connections

MISO – Master Input Slave Output – communication from the device to the Pi

MOSI – Master Output Slave Input – communication from the Pi to the device

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 242

Prepared exclusively for [email protected] Transaction: 5095


THE MCP3008 A/D CONVERTER
Throughout this lesson, you have already covered a lot of information pertaining to the
MCP3008. This section will cover a few more points about the MCP3008 before you get
to the activities.

The MCP3008 Analog to Digital Converter is used to gather information from 8 analog
input channels and send it to another device using digital communication. This
converter has a resolution of 10 bits meaning 10 units of ones and zeros will be
available to express the analog voltage level it sees.

On a 3.3V based input voltage, 10 bits of resolution means 1024 steps of voltage
change can be measured between 0V (ground), and 3.3V. Breaking that voltage range
into 1024 steps means that the MCP3008 can resolve voltage changes as small as
0.0032 volts, or 3.2 millivolts. That should be more than accurate enough for any of your
projects.

The MCP3008 can be powered by a supply anywhere between 2.7V and 5.5V, making
it an ideal part for running from the 3.3V the Raspberry Pi's can supply. Another
attractive feature of this part is that it does not require any additional components to
operate, just connections to power, ground, SPI communication, and some analog
voltage levels for inputs.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 243

Prepared exclusively for [email protected] Transaction: 5095


The Adafruit library that you installed in the last lesson contained some preselected pin
numbers for software SPI. Instead of complicating things by modifying the program, we
will use these preselected pins when wiring up the circuit. The example programs
contained these pin numbers for software SPI communication with the MCP3008:

CLK – GPIO18

MISO – GPIO23

MOSI – GPIO24

CS – GPIO25

Applying these connections to the


full MCP3008 pinout, you get the
diagram to the right:

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 244

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will add the MCP3008 Analog to Digital IC to your circuit,
create a small program to read the values, and then create a voltage divider circuit to
display various analog voltage levels using your program.

ACTIVITY #1 – CONNECTING THE MCP3008


In Lesson B-8 you installed the library required for communicating with the MCP3008. In
this activity you will remove a few components from the circuit in Lesson B-7, and add
the MCP3008 IC.

STEP #1
In order to make room for the MCP3008, a few components will need to be removed
from the circuit you built in Lesson B-7.

Shut down the Pi and disconnect power before proceeding.

Remove the keypad, slide switch, pushbutton switch, and the jumper wires connecting
to those parts. When completed, your circuit should look like this:

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 245

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The MCP3008 will need some 3.3V power. Make this power available on power rail P1.
Connect a short jumper wire from A1 to P1-3:

STEP #3
Now that power and ground are available near the bottom of the board, the next step
will be to carefully insert the MCP3008 into the breadboard.

Insert the MCP3008 into breadboard holes E43-50 and F43-50, with pin 1 located in
E43. The black dot on the IC (pin 1) should be near the LED in row 37:

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 246

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
The MCP3008 now needs power and ground connections so it can operate. 3.3V is
needed on IC pins 15 and 16 and ground is needed on pins 9 and 14.

Insert two short jumper wires from the P1 power rail over to G43 and G44. Insert two
short jumper wires from the ground rail N2 over to J45 and J50:

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 247

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
The last step is connecting the SPI lines that the MCP3008 and the Pi will use to
communicate with each other.

Install four long jumper wires between:

CLK – J6 to G46 MOSI – J9 to G48


MISO – J8 to G47 CS – J11 to G49

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 248

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Now you just need an analog voltage to measure. We will get to more interesting analog
sources later, but for now, let's connect the channel 0 input to 3.3V.

Using a short jumper wire, connect D43 and P1-43:

STEP #7
Your circuit is now complete.

Ensure all connections match the image in Step #6 and power up your Raspberry Pi.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 249

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – READING A VALUE FROM THE MCP3008
The MCP3008 library is installed and you have wired up the IC. The only thing left is to
read some analog values. In this activity you will create a program that will read the
value of channel 0 and print it to the screen.

STEP #1
The first step to creating your
program will be to open up
Thonny and create a new
program.

Open Thonny and press the


New button to create a new
program. Click File, Save as,
and save the new file as
read3008.py:

STEP #2
This program will need a couple of libraries to run. It will need the time library to add
some pauses to the program, and it will need the Adafruit_MCP3008 library to
communicate with the MCP3008.

Import the libraries time and Adafruit_MCP3008:

import time, Adafruit_MCP3008

Remember the spelling, symbols, and capitalization are important. Every character of
the library name must exactly match the line above, of your program will fail to find the
library.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 250

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The next thing your program needs is a list of pins that you plan to use for software SPI.
These short pin names will be used later in the program when your program attempts to
read values from the MCP3008.

Create the following list of pin names and numbers in your program:

import time, Adafruit_MCP3008

CLK = 18

MISO = 23

MOSI = 24

CS = 25

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 251

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
The Adafruit_MCP3008 library needs a long name and all the pin numbers sent every
time you want to communicate with the IC. The Adafruit engineers have simplified this
by using the variable mcp to represent all this information. This variable could be named
anything, but in the interest of consistency we will stick with what Adafruit uses in their
example programs.

Add the line below setting the variable mcp equal to the values the Adafruit library needs
to complete a read of the MCP3008:

import time, Adafruit_MCP3008

CLK = 18

MISO = 23

MOSI = 24

CS = 25

mcp = Adafruit_MCP3008.MCP3008(clk=CLK, cs=CS, miso=MISO, mosi=MOSI)

The variable mcp can now be used to greatly simplify the command needed to read
values from the IC.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 252

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
Now it's time to build a loop that will read the Channel 0 of the MCP3008 every .25
seconds and print the result to the console. You want this loop to run continually so use
while True: to keep it looping forever.

Create a while True: loop that sets a variable named ch0 equal to mcp.read_adc(0),
prints the value, then pauses for .25 seconds and repeats:

import time, Adafruit_MCP3008

CLK = 18

MISO = 23

MOSI = 24

CS = 25

mcp = Adafruit_MCP3008.MCP3008(clk=CLK, cs=CS, miso=MISO, mosi=MOSI)

while True:

ch0 = mcp.read_adc(0)

print(ch0)

time.sleep(.25)

The mcp.read_adc command will return the current MCP3008 value for the channel
number specified between parentheses, in this case channel 0.

You will add more intelligence to gracefully exit this loop in future lessons, but for now
the Stop button in Thonny will be used to end the program.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 253

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Run the program in Thonny and watch for values around 1023 to be printed to the
console:

The value will not vary much from 1023. Remember that the MCP3008 uses 0 to
represent ground and 1023 to represent 3.3. Since channel 0 is tied directly to 3.3V, the
reported value should be right around 1023.

If anything does not run as expected, confirm all of your code matches the last step, and
confirm your circuit is wired properly per Activity #1, Step #6.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 254

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – ADDING A VOLTAGE DIVIDER
The MCP3008 is good at measuring analog voltages, but the only voltage you've had
available to measure is 3.3V. In this activity you will add a voltage divider to the circuit
that will create different analog voltage levels that you can measure with the MCP3008.

STEP #1
The first step will be to power down the Pi to make it safe to add circuitry to the
breadboard. Shut down the Pi and disconnect power before proceeding.

STEP #2
Next, you will add a voltage divider network of three 1K-ohm resistors. As well as
ground and 3.3V, this will make two more analog voltage levels available for
measurement.

Insert 1K-ohm resistors into the following locations:

P1-57 to A57

D57 to D61

E61 to G61

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 255

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Your voltage divider is connected to 3.3V but it still lacks a path over to ground.

Install a jumper wire from J61 to N2-61 to complete the path between power and
ground, through these three resistors:

STEP #4
Double-check your voltage divider placement and connections and power up the
Raspberry Pi. Channel 0 is still connected to 3.3V at this time.

Run the read3008.py program to ensure it is still reporting values of around 1023,
representing 3.3V.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 256

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
Next you will move the wire connected to Channel 0 to a new location on the voltage
divider, so the voltage level can be measured

With the program still running and reporting values, carefully relocate the input jumper
wire from P1-43 to B57. Your program will start reporting new values around 682.

The voltage divider is cutting the voltage into thirds since it's using three equally sized
resistors. Only one resistor is between our test point and 3.3V. 1.1V is being dropped by
this resistor, so you will measure 2.2V at this point. This will be shown as 2/3 of 1023
counts or around 682.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 257

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Now to measure the voltage present in row A61 through E61 using the MCP3008. Be
very careful doing this as you will be relocating a jumper wire to a new location while the
Raspberry Pi is up and running!

With the program still running and reporting values, carefully relocate the input jumper
wire from P1-43 to B61. Your program will start reporting new values around 341.

Only 1.1V will be present at this point in the voltage divider, which will be represented
by 1/3 of 1023 counts or around 341.

Leave this circuit on your breadboard for use in upcoming lessons.

You will add devices that can create variable analog voltage levels that will be
measured using the MCP3008.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 258

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. What voltage level will be present at the center point of a voltage divider made
up of two 1K-ohm resistors in series between a 5V supply and ground?

2. Does the Raspberry Pi have the capability to measure analog voltage levels
using its onboard components?

3. When using hardware or software SPI, which is more flexible with respect to
which GPIO pins must be used for communication?

Answers can be found on the next page.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 259

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. What voltage level will be present at the center point of a voltage divider made up
of two 1K-ohm resistors in series between a 5V supply and ground?

ANSWER: A voltage divider made up of two equal value resistors will each drop
½ of the supply voltage. Since the supply voltage is 5V then each resistor will
drop 2.5V. 2.5V be available at the center- point between the two resistors.

2. Does the Raspberry Pi have the capability to measure analog voltage levels
using its onboard components?

ANSWER: No, the Raspberry Pi is only capable of measuring digital voltage


levels using its onboard components. Measuring analog voltage levels requires
an analog-to-digital convertor whose digital output is then fed into the Raspberry
Pi.

3. When using hardware or software SPI, which is more flexible with respect to
which GPIO pins must be used for communication?

ANSWER: Software SPI is more flexible as it can be done using many of the
GPIO pins. Hardware SPI is less flexible because it must be done using specific
GPIO pins which may already be in use in your project.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 260

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson, you learned how to interface the Raspberry Pi with analog signals, work
with integrated circuits, and learned about digital communication.

In the next lesson, you will learn to work with potentiometers (variable resistors) and
photoresistors (light sensors). You will also learn advanced commands for lists which
will increase the functionality of programs you code.

Lesson 9 – Analog Signal Processing with the Raspberry Pi Page 261

Prepared exclusively for [email protected] Transaction: 5095


LESSON 10

POTENTIOMETERS,
PHOTOTRANSISTORS, AND
ADVANCED LIST COMMANDS

OBJECTIVE
In this lesson you will learn to use potentiometers (variable resistors), phototransistors
(light sensors), and work with advanced list commands.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Assembled Circuit from Lesson B-9
• 1 x Potentiometer
• 1 x Phototransistor
• 1 x 10k-ohm Resistor
• 5 x Short Jumper Wires

REVIEW CONCEPTS

If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Working with Resistors and LEDs (Lesson A-2)


• Lists (Lesson A-13)

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 262

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will learn about variable resistors known as potentiometers. You will
also learn to use phototransistors which are a light sensor. Finally, you will learn to use
advanced list commands and create programs that can perform different actions based
on sensor input.

VARIABLE RESISTORS

A variable resistor is a resistor that can change value based on user input. These
devices are also called potentiometers or "pots" for short.

Variable resistors have a rotary knob or slider that changes their internal resistance
when a user moves the knob or slider. They are commonly used in audio equipment for
volume control, but they can show up in other places like lighting dimmer switches and
mechanical joysticks. They can be used any time user input needs to be measured so it
can be processed by a circuit or program.

You can also find potentiometers on the circuit boards inside most electronic equipment.
If an exact voltage or resistance level is needed for a circuit to function properly, a
potentiometer can be added so the circuit can be tuned to operate as intended, without
removing and reinstalling resistors of different values.

Variable resistors have many variations, but the most


common models have three leads. Two leads are
connected to the ends of a resistive track and one
lead is connected to a wiper.

The wiper moves up and down the resistive track,


changing the resistance between the wiper and each
end point. In rotary knob potentiometers the resistive
track is wrapped around a circle, but the output is the
same. As the wiper moves closer to one end of the resistive track, the resistance
between the wiper and that end decreases. Inversely, as the wiper moves further from
the other end, the resistance between the wiper and that end increases.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 263

Prepared exclusively for [email protected] Transaction: 5095


Using the same principles from voltage dividers in the last lesson, these increasing and
decreasing resistance values can be used to create variable voltages. By applying a
voltage like 3.3V across the resistive track, the voltage seen at the wiper will track up
and down along with the adjustment of the potentiometer. This is just like having two
resistors in series creating a voltage divider, but their values can be adjusted
effortlessly.

In this example, the potentiometer is adjusted about 20% away from one end. Since the
total resistance of the resistive track is 10K-ohms, this leaves 2K-ohms from the wiper
to one end, and 8K-ohms from the wiper to the other end. Using the voltage divider
formula you will find that the 2K-ohm side is dropping .2 or 20% of the 10K-ohm total
resistance. This means that .2 of 3.3V or 0.66V is being dropped by the resistive track
before the wiper. Subtracting this 0.66V from the initial supply of 3.3V results in 2.64V
being available at the wiper. These resistance values only apply to the potentiometer in
this exact position. If the knob is adjusted, these resistance and voltage values would
have to be recalculated.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 264

Prepared exclusively for [email protected] Transaction: 5095


Each different style of potentiometer will have its resistive track and wiper pins in
different locations, so refer to the datasheet from the vendor if you're unsure of the
pinout. Here is an image of the pinout of the potentiometer included in your kit:

Pins 1 and 3 are the ends of the resistive track, and pin 2 is connected to the wiper that
moves along the resistive track.

The voltage output from the wiper will only be useful if


it can be measured as an analog input. Since the
Raspberry Pi does not have the ability to measure
analog voltages, a device like the MCP3008 must be
used. If the analog voltage is fed into the MCP3008,
the Raspberry Pi will be able to display the analog
voltage level in counts of 0 to 1023 or ground to 3.3V.

Your programs can perform all different sorts of


functions based on the value of the potentiometer. For example, you could have 8 GPIO
pins driving LEDs used to indicate the value of a potentiometer. As the value of counts
increases from the MCP3008, LEDs could be lit from left to right, until they are all lit,
indicating maximum value.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 265

Prepared exclusively for [email protected] Transaction: 5095


LIGHT SENSORS
A light sensor will change its resistance or conductance based on the amount of light it's
receiving.

One type of light sensing device is called a Photo-conductive cell.


There are other types of light sensors as well, but Photo-
conductive are by far the most common. They are the least
expensive and most likely to be found in light-sensing projects you
may find online. You may also see these devices referred to as a
Light Dependent Resistor or LDR.

Photo-conductive cells will change their resistance based on the amount of incoming
light. In full darkness the resistance will be highest, and full sunlight their resistance will
be lowest. This resistance change can be used as part of a voltage divider to create an
analog voltage based on the light level the sensor is reading.

Another type of light sensing device is called a Photo-junction device or phototransistor.


A phototransistor uses the incoming light to change the conductance of a junction inside
the phototransistor, allowing current to flow through the device. Due to this junction and
a special clear lens designed to maximize light transmission into the junction,
phototransistors offer increased light measurement sensitivity over LDRs.

Phototransistors can be used to create analog voltage levels to


indicate the amount of light the device is sensing. Due to their
sensitivity, these measurements can be very accurate, allowing your
circuit or program to respond to various light levels with different
behavior.

Even though a phototransistor looks very similar to an LED, they are


very different internally. An LED is a diode that has an anode and cathode, but a
phototransistor contains a tiny transistor. Transistors have a base, emitter, and
collector.

The legs of a phototransistor are the emitter and collector, but there is no external
connection for the base. The base is essentially made of photoconductive material and
is fully contained inside the phototransistor. As more light is received, the transistor will
allow more current to flow through the device.

Photoresistors do not have any polarity so they can be installed into a circuit in either
direction. This is not the case for phototransistors. Since phototransistors operate using
a polarized junction, it is important to have the device properly polarized in your circuit
to allow for proper light measurement.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 266

Prepared exclusively for [email protected] Transaction: 5095


Here is an image of a phototransistor with its legs
labeled to indicate direction:

The internal components at the top of the


phototransistor have been outlined in gray to help you
identify which direction the internal plates are pointing.
The larger plate is the collector and the smaller plate is
the emitter. The body of the phototransistor will contain
identifiers like a flat spot in the body and different leg
lengths, but don’t rely on these characteristics for
identification of the emitter or collector. The flat spot and
leg lengths can vary between manufacturers, so it's best
to identify the emitter and collector based on the shape
of the plates inside the device.

The emitter of the phototransistor must be connected to the ground side of the circuit,
even if it's through one or more resistors. If not, the component will be reversed in the
circuit, and current will not be able to flow through it when light is detected.

In the good examples above, the phototransistor is properly polarized with the collector
toward 3.3V and the emitter toward ground. In the bad examples, the phototransistor is
reversed and will not allow current to flow through the device when light is detected.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 267

Prepared exclusively for [email protected] Transaction: 5095


ADVANCED LIST COMMANDS
You have used lists in previous lessons to store multiple values, but there are many
more ways that lists can be used in your programs. These commands can be used to
display and even modify the contents of a list.

CREATING A LIST USING THE SPLIT COMMAND


You can create a list from a string, as long as your string has identifiable separation
points between each value. For example, the string below uses spaces as separators:

data = '12 23 34 45 56'

The split command can break this string into new values in a list:

data = '12 23 34 45 56'

new_list = data.split()

In this command data represents the name of the string that will be split, and () will
cause default separators of spaces to be used. The resulting contents of new_list will
now be:

new_list = ['12', '23', '34', '45', '56']

The separator does not need to be spaces. It can be anything as long as it is specified
inside the parentheses. In this example the incoming data is separated by commas:

data = '12,23,34,45,56'

new_list = data.split(',')

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 268

Prepared exclusively for [email protected] Transaction: 5095


The split command will use commas as the separator, and the resulting new_list will
once again be:

new_list = ['12', '23', '34', '45', '56']

What happens if you specify a separator that's not contained in the list? No errors will
occur, but the string won't be properly split up because Python doesn't know where the
splits should occur:

data = '12?23?34?45?56'

new_list = data.split('/')

In this case, the split command is looking for forward slashes in the string to indicate
separation point. Since none are found, the entire contents of the string are moved into
the list, as the first entry in the list:

new_list = ['12?23?34?45?56']

This list isn't very useful, as it isn't split up into different parts as intended. The separator
must always match the one specified after the split command for the split to work
properly.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 269

Prepared exclusively for [email protected] Transaction: 5095


ADDING ITEMS TO A LIST
If you have an existing list and want to add items to it, this can be done using the
append command. The append command will add the string you specify in parentheses
to the end of the list:

my_list = ['12', '23', '34', '45', '56']

my_list.append('67')

This will result in the string '67' being added to the end of the list. The new value of
my_list will be:

my_list = ['12', '23', '34', '45', '56', '67']

FINDING ITEMS IN A LIST


Adding items to an existing list is nice but you might have trouble finding them later. The
index command can be used to locate a string within a list and return its index position:

my_list = ['12', '23', '34', '45', '56']

my_list.index('34')

This index locator will look for the string '34' in the list named my_list and return the
index position if found. If the string is not found in the list, Python will generate an error
stating that information.

Running the code above would return an index value of 2, but that value isn't being
used for anything. You can use this value in your program however you like. You might
set a variable called position_34 equal to the index value that's returned from this
check:

my_list = ['12', '23', '34', '45', '56']

position_34 = my_list.index('34')

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 270

Prepared exclusively for [email protected] Transaction: 5095


The position_34 variable can then be used to do other things in your program. You
might just want to print the value that's returned. You could either print the current value
of position_34, or you could save a line of code by nesting the index command inside
of a print command:

my_list = ['12', '23', '34', '45', '56']


print(my_list.index('34'))

Running this code will print the value 2 to the console since this is the index position of
'34' in my_list. Remember to include two parentheses at the end of a nested
statement like this, one to close out the print statement, and one to close out the string
value for the index command.

One thing to note about the index command is how it behaves when the same value is
present more than once in the list. It starts looking through the list from the left, or index
position 0. As soon as it finds your string it will return that value, even if there are more
of that value further down the list:

my_list = ['12', '23', '34', '34', '34', '34, '45', '56']

position_34 = my_list.index('34')

There are multiple instances of the string '34' in my_list. The index command will
start at index position 0 and will run across the first occurrence of '34' in index position
2, so the value of position_34 will now be 2. The rest of the occurrences of '34' will
be ignored.

This won't be a problem as long as your list does not contain duplicate data, but this
behavior is definitely something to keep in mind if it does.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 271

Prepared exclusively for [email protected] Transaction: 5095


DETERMINING IF AN ITEM IS PART OF A LIST
At some point you might want to determine if an item is part of a list. You could request
that item’s index position, and if it's in the list you will have its index. Unfortunately, if it's
not in the list you will get an ugly Python error. A cleaner way to do this is by using the
in operator with an if statement:

my_list = ['12', '23', '34', '45', '56']

if '45' in my_list:

print('Found 45')

This can be used to determine if the string '45' is part of my_list. If found, then Found
45 will be printed to the console. If '45' is not found in my_list, then the print
statement will be skipped, and the rest of the program will execute.

Another way to use this might be to add an index check if the string is found:

my_list = ['12', '23', '34', '45', '56']

if '45' in my_list:

position_45 = my_list.index('45')

If found, position_45 will be set equal to 3 which is the index position of '45' in
my_list. The value of position_45 can then be used throughout the rest of your
program. If '45' is not found in my_list, then this if statement and its contents will be
skipped, without creating any errors.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 272

Prepared exclusively for [email protected] Transaction: 5095


FINDING THE LENGTH OF A LIST
If you keep adding items to a list, at some point you might want to know how many
items are now in that list. The len() command can be used to return the current length of
a list:

my_list = ['12', '23', '34', '45', '56']

print(len(my_list))

This will print the current length of my_list, which is 5, to the console. This command
can also be used to set a variable equal to the length:

my_list = ['12', '23', '34', '45', '56']

my_list_length = len(mylist)

The variable named my_list_length can then be used throughout the rest of your
program to represent the current length of my_list. The value of my_list_length will
only be accurate if no items are added to or removed from my_list. This was a
snapshot of the length when this line of code ran. The value of my_list_length will
need to be updated with the new length if items are added to or removed from my_list.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 273

Prepared exclusively for [email protected] Transaction: 5095


SORTING A LIST
The option also exists to sort lists to reorder them in alphabetical or numerical order. For
this command to work your list must consist of all strings or all integers. A list containing
a mix of both will cause errors when attempting to sort.

There are two command that can be used to sort a list. The first command is sort and it
will reorder the contents of your original list. The second command is sorted and it will
only return a resorted list, but will not modify the contents of your original list.

In both commands, the default behavior is to sort alphabetically or by increasing


numeric value. Here are some examples:

my_list = ['Brad', 'Kelly', 'Annie', 'Charlie', 'Dan']


my_list.sort()

The second line of code runs a sort on my_list and reorders the contents of the
original list. After running the second line of code, the items in my_list will now be in
alphabetical order. Printing the contents of my_list will now print:

['Annie', 'Brad', 'Charlie', 'Dan', 'Kelly']

This is great, but you might not want to modify the contents of the original list. In that
case, you can use the sorted command to return the resorted contents, without
modifying the original list:

my_list = ['Brad', 'Kelly', 'Annie', 'Charlie', 'Dan']

print(sorted(my_list))

This will print the contents of my_list in alphabetical order, without actually modifying
the contents of my_list. You can also use the sorted command to create a second
alphabetized list, while leaving the original list unmodified:

my_list = ['Brad', 'Kelly', 'Annie', 'Charlie', 'Dan']

my_list_alpha = sorted(my_list)

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 274

Prepared exclusively for [email protected] Transaction: 5095


You now have two copies of the list to use, the original order in my_list, and the
alphabetized version in my_list_alpha.

These commands can also take some parameters, one of which is reverse. The
reverse parameter will let sort or sorted know that you want the results in reverse, or
descending order:

my_list = ['Brad', 'Kelly', 'Annie', 'Charlie', 'Dan']

my_list.sort(reverse=True)

The result of this command will be the reordering of my_list into reverse alphabetical
order. Printing my_list to the console now will result in:

['Kelly', 'Dan', 'Charlie', 'Brad', 'Annie']

Adding the reverse command to sorted requires a comma after the list name:

my_list = ['Brad', 'Kelly', 'Annie', 'Charlie', 'Dan']

my_list_alpha = sorted(my_list, reverse=True)

This will result in my_list_alpha being created in reverse alphabetical order, while the
contents of my_list remain unmodified.

One interesting behavior of both commands is the way numeric strings are ordered. You
might think these values are already in order, but since they are strings, they are not:

my_list = ['4', '8', '34', '45', '71']

Reordering this list with sort or sorted will give us:

['34', '4', '45', '71', '8']

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 275

Prepared exclusively for [email protected] Transaction: 5095


This happens for the same reason that '10' cannot be divided by '5'. Python does not
see these values as integers, so they cannot be ordered or divided in the same way that
integers would. When sorting a list like this the numeric values are treated like letters.
Just like sorting a list of names, the names starting with a will all be up front. This is why
4 ends up between 34 and 45. The sort looks for everything beginning with 3 until those
are gone, and then moves on to 4. If you had the value '4999' in the list, it would still end
up before the 71 when sorted:

['34', '4', '45', '4999', '71', '8']

This same list, if converted to integers, will sort as you would expect:

[4, 8, 34, 45, 71, 4999]

The process for sorting numeric strings in the order you expect is called Natural Sorting
or Natural Sort Order. In the event that your program needs this functionality, while
outside the scope of this lesson, there are many examples of Python code available
online showing various ways to accomplish this task.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 276

Prepared exclusively for [email protected] Transaction: 5095


PRINTING ALL ITEMS IN A LIST ALONG WITH THEIR INDEX VALUES
You can print all items in a list with their index values by using a for loop that runs
through and prints each item in the list. The loop can also print how many times the loop
has run, which will correspond to that item's index value.

The first step is to only run the loop as many times as there are items in the list, which is
equal to the length of my_list or len(my_list). Normally you would specify the stop
value as an integer, but you can substitute len(my_list) to specify that value as the
amount of times the loop should run:

my_list = ['Brad', 'Kelly', 'Annie', 'Charlie', 'Dan']

for i in range(len(my_list)):

Now this for loop will once for each item in my_list and then stop. A print statement
can now be added to the for loop that will print the current value of i along with the
value of my_list at index position i:

my_list = ['Brad', 'Kelly', 'Annie', 'Charlie', 'Dan']

for i in range(len(my_list)):

print(i, my_list[i])

The first time this loop runs, i will equal 0. The print statement will print 0 along with the
value of my_list at index position 0 which is 'Brad'. i will be incremented by 1 and
will now equal 1. This new value of i will be printed along with the value at my_list[1]
or 'Kelly'. The printing will look like this in the console:

0 Brad
1 Kelly

2 Annie

3 Charlie

4 Dan

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 277

Prepared exclusively for [email protected] Transaction: 5095


This will continue printing and looping until i equals len(my_list), which is 5 in the
case of my_list. The for loop will then exit and the rest of the program will run.

REMOVING ITEMS FROM A LIST


Items can be removed from a list by using the pop() command. The pop() command
will remove the last items in the list by default, or you can specify the index position of
the item you want removed:

my_list = ['Brad', 'Kelly', 'Annie', 'Charlie', 'Dan']

my_list.pop()

If the contents of my_list were printed at this point they would no longer include 'Dan'
as the pop() command removed the last item from the list. You can remove other items
by specifying their index position inside the parentheses:

my_list = ['Brad', 'Kelly', 'Annie', 'Charlie', 'Dan']

my_list.pop(0)

Printing my_list at this point would show that index position 0 or 'Brad' has been
removed from the list.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 278

Prepared exclusively for [email protected] Transaction: 5095


CLEARING ALL ITEMS FROM A LIST
Here are a couple of methods that can be used for removing all the strings contained in
a list. One option is to specify all values of the list by using the [:] argument and
setting the list equal to an empty set or []:

my_list[:] = []

This will remove all values from the list and return it back to a completely empty state.
Another method available in Python 3.3 or higher is to use the clear() command:

my_list.clear()

When running programs in Thonny using the default Python environment of 3.5, either
of these commands will work well for clearing the contents of a list.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 279

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will add a potentiometer and phototransistor to your analog
measurement circuit from Lesson B-9. You will also create a new program to practice
the advanced list commands from this lesson.

ACTIVITY #1 – ADDING THE POTENTIOMETER AND PHOTOTRANSISTOR


In Lesson B-9 you built a circuit and program to measure analog voltages using the
Raspberry Pi. In this activity you will modify that circuit to add a potentiometer and
phototransistor capable of generating variable analog voltages. You will also modify the
program you created in Lesson B-9 to measure these new voltages.

STEP #1
To make room for the new components, the voltage divider circuit will need to be
removed from your breadboard. You will use circuit from Lesson B-9 as a starting point
for this activity.

Shut down the Pi and disconnect power before proceeding.

Next, remove the voltage divider, and any associated jumper wires from your
breadboard. When completed the circuit should look like this:

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 280

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Now that you have some room for new components, add the potentiometer to channel 1
of the MCP3008. Remember that channel 1 is actually the second pin from the end
since the inputs start with channel 0.

The resistive track on the potentiometer will be connected between 3.3V and ground,
and the wiper will be connected to channel 1 on the MCP3008. Make the following
additions to your breadboard:

Connect potentiometer to pins E61, E62, and E63. Align the body of the potentiometer
across the center of the breadboard so that columns A through D are clear for additional
connections.

Connect a jumper wire from B44 to A62.

Connect a jumper wire from N2-59 to A61.

Connect a jumper wire from P1-61 to A63.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 281

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Next, add the phototransistor. The phototransistor will be connected in series with a
10K-ohm resistor between 3.3V and ground. The connection between the
phototransistor and resistor will be connected to channel 0 of the MCP3008 to allow for
analog measurement of the voltage as it varies with light intensity.

Connect the phototransistor between P1-55 to A55. The collector should be connected
to P1-55 and the emitter should be connected to A55. This will properly polarize the
sensor.

Connect a 10K-ohm resistor between E55 and G55.

Connect a jumper wire between J55 and N2-55.

Connect a jumper wire between D43 and D55.

STEP #4
The circuit is now complete.

Double-check all the connections and power up the Raspberry Pi.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 282

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
Create a copy of the program you created in Lesson B-9 so you can add the
measurement and display of channel 1.

Locate the file named read3008.py you saved in Lesson B-9 and double-click to open it
in Thonny. Click File, and then Save As to save a new copy of the file named
read3008_2ch.py to your Desktop.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 283

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
This program is already able to read and display channel 0 that is now connected to the
phototransistor. It's time to add the ability to read and display the potentiometer
connected to channel 1.

Just below the line reading channel 0, add a line to read channel 1 using the variable
ch1. Make sure to point the mcp.read_adc command at channel 1 by using (1). The
new line is highlighted below:

while True:

ch0 = mcp.read_adc(0)

ch1 = mcp.read_adc(1)

print(ch0)

time.sleep(.25)

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 284

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
This program is now able to read channel 1, but it will only print the value of channel 0.
Now you will tell the program to display both channel 0 and channel 1.

Add ch1 to the existing print statement by using a comma after ch0:

while True:

ch0 = mcp.read_adc(0)

ch1 = mcp.read_adc(1)

print(ch0, ch1)

time.sleep(.25)

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 285

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
All that's left is to run the program. When the program runs two analog values will be
printed, first the value of the phototransistor, and second will be the potentiometer.

Run the program. With the program running, use a flashlight to vary the intensity of light
on the phototransistor and watch its reported value increase and decrease. Turn the
knob on the potentiometer and its reported value will change as well.

NOTE: Be careful when adjusting the potentiometer. The knob will stop when
you get to the end of adjustment range and the legs of the potentiometer can be
damaged if you continue to attempt to rotate the knob past the end of travel.

Press the stop button in Thonny to exit the program when you are finished.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 286

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – INCORPORATING THE LED AS AN INDICATOR
Printing the analog value of a sensor to the console is handy, but it's also useful to be
able to use that analog reading to trigger some behavior in your program. In this activity
you will add the LED on GPIO26 as an indicator when levels go above a pre-determined
amount. In this program, a value of 500 from either the phototransistor or potentiometer
will turn on the LED.

STEP #1
Create a new copy of your program that you can use as a base for your new program.

In Thonny, go to File, then Save As and save the new file as read3008_led.py. This
will leave your original read3008_2ch.py unmodified in case you want to go back and
run it again, or you want to create another copy to use as a base for another program.

STEP #2
Since this program will be using a GPIO pin, the GPIO module must be imported.

Import the GPIO module just below the existing import line for the time and MCP3008
modules:

import time, Adafruit_MCP3008

import RPi.GPIO as GPIO

CLK = 18

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 287

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The GPIO.BCM pin mode will need to be set and GPIO26 needs to be configured as an
output.

Just below the import section, add two lines of code to set the pin mode to BCM and
configure GPIO26 as an output:

import time, Adafruit_MCP3008

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

GPIO.setup(26, GPIO.OUT)

CLK = 18

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 288

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
GPIO26 is now ready to use in the program. The last step will be to add an if statement
to the while True: loop that will turn the LED on for phototransistor or potentiometer
values above 500.

Add an if/else block between the print and sleep statements that checks both ch0 and
ch1 for values above 500. If the value is above 500, turn on GPIO26. If not, turn off
GPIO26. The if statement will need to use the or condition to allow either the
phototransistor or the potentiometer to turn on the LED. The additional code is
highlighted in gray below:

while True:
ch0 = mcp.read_adc(0)

ch1 = mcp.read_adc(1)

print(ch0, ch1)

if ch0 > 500 or ch1 > 500:

GPIO.output(26, GPIO.HIGH)

else:

GPIO.output(26, GPIO.LOW)

time.sleep(.25)

STEP #5
Run the program.

Your sensor values will still be printed to the console, but now the LED will turn on if
either sensors value goes above 500.

Leave this circuit built as it will be used as a starting point in Lesson B-11.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 289

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – WORKING WITH LISTS
In this activity, you will practice some of the list operations covered in this lesson. These
will include splitting a string up into a list, printing that list with its index values, using the
main list to create a new alphabetized list, and printing the alphabetized list.

STEP #1
The first step is to create a new program.

In Thonny, click the green plus button to create a new program. Go to File, then Save
As, and save the file as lists.py:

STEP #2
You will need to create a string to use as the data that will be manipulated by the rest of
the program.

Create a variable named data that contains a single string of the names Tom, Annie,
Zach, Kelly, and Bill, with colons (:) between each name:

data = 'Tom:Annie:Zach:Kelly:Bill'

STEP #3
Unfortunately, this data being in a single string isn't very useful. Break up the string into
a list by using the split() command.

Create a variable called name_list that will contain the names from data, split up
using (:) as a separator:

data = 'Tom:Annie:Zach:Kelly:Bill'
name_list = data.split(':')

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 290

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
You now have a list of names stored in name_list. Print the list to see what it looks
like.

Add a print command to print the contents of name_list and run the program. The list
of names will be printed to the console:

data = 'Tom:Annie:Zach:Kelly:Bill'

name_list = data.split(':')

print(name_list)

STEP #5
The horizontal name list is nice, but print the list vertically along with the index value for
each name. You can do this by using the for i in range loop covered earlier in this
lesson.

Add a for loop that will loop through the list, printing both the current value of i, and the
value of that index position in name_list. After adding the for loop, run the program and
both the horizontal list and vertical list with index values will be printed to the console.

data = 'Tom:Annie:Zach:Kelly:Bill'
name_list = data.split(':')

print(name_list)

for i in range(len(name_list)):

print(i, name_list[i])

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 291

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Create a new alphabetized list from name_list.

Create a variable called abc_list and use the sorted command to store an
alphabetized copy of name_list, without modifying the contents of name_list:

data = 'Tom:Annie:Zach:Kelly:Bill'

name_list = data.split(':')

print(name_list)

for i in range(len(name_list)):

print(i, name_list[i])

abc_list = sorted(name_list)

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 292

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
The alphabetized list has been stored, and the only thing left to do is print that list.

Add a print command that will print the contents of abc_list, and run the program:

data = 'Tom:Annie:Zach:Kelly:Bill'

name_list = data.split(':')

print(name_list)

for i in range(len(name_list)):

print(i, name_list[i])

abc_list = sorted(name_list)

print(abc_list)

The following output will be printed to the console. First, the original list in the order they
were pulled from the string, and then the vertical list with along with index numbers, and
then the alphabetized version of the list.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 293

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Which line of code would you change in the program from Activity #2 to increase
the threshold value required to turn on the LED from 500 to 900? What would
the new line of code look like?

2. Which are more sensitive to light level changes, photoresistors or


phototransistors?

3. Between sort and sorted, which command modifies the original list, and which
does not?

Answers can be found on the next page.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 294

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Which line of code would you change in the program from Activity #2 to increase
the threshold value required to turn on the LED from 500 to 900? What would
the new line of code look like?

ANSWER: The code would look like this:

if ch0 > 900 or ch1 > 900:


GPIO.output(26, GPIO.HIGH)

2. Which are more sensitive to light level changes, photoresistors or


phototransistors?

ANSWER: Phototransistors are more sensitive to light than photoresistors.

3. Between sort and sorted, which command modifies the original list, and which
does not?

ANSWER: The command sort will modify the original list. The command
sorted will not modify the original list.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 295

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to work with both potentiometers and phototransistors. You
also learned a number of new commands to manipulate lists, making lists far more
useful in future projects.

In the next two lessons, you will learn to work with Radio Frequency Identification
(RFID) technology.

Lesson 10 – Potentiometers, Phototransistors, and List Commands Page 296

Prepared exclusively for [email protected] Transaction: 5095


LESSON 11

RFID SYSTEMS

OBJECTIVE
In this lesson you will learn to work with radio frequency identification (RFID) systems
including how to both read and write RFID tags.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, mouse, and internet access


• Assembled Circuit from Lesson B-10
• 1 x RFID Reader
• 1 x RFID Tag
• 1 x RFID Card

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Administrative File Management (Lesson B-1)


• Using Github to clone libraries (Lesson B-8)

Lesson 11 – RFID Systems Page 297

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will learn about RFID systems, their components, and some of their
applications. You will connect an RFID reader that will write and read RFID cards, as
well as create a program that will read an RFID card and perform an action based on
the value that's read from the card.

RFID TECHNOLOGY

RFID stands for radio-frequency identification. This system was originally designed for
powering a device using radio waves. The RFID receiver was powered by a transmitter
sending radio waves at a very specific frequency. The receiver did not contain its own
power source, instead it contained special circuitry to convert the incoming radio waves
into power that could be used to power up additional circuits in the receiver.

Around 1970, new ideas and patents started to emerge from this existing RFID
technology. Proposed use cases involved automated toll collection systems, banking,
security, and medical applications, as well as many others.

RFID technology can now be seen everywhere. Automated toll collection is installed on
many of highways. Most pets have RFID implants that allow for identification of the
animal without any external markings like a collar or tags. Many businesses rely on
RFID cards to manage employee access control, instead of handing out physical keys
to the building.

HOW RFID WORKS


Modern RFID systems operate using readers and tags. The reader contains a radio
transmitter and receiver, commonly referred to as a transceiver, as well as other
circuitry to decode signals received from scanned tags. The reader is always
broadcasting radio signals and waiting to hear back from a tag.

Tags, or cards, contain an antenna, circuitry to convert radio waves to DC power, a


radio transceiver, as well as a tiny amount of storage that can be used to hold data
specific to that card. When the card receives a radio signal in a specific frequency, the
antenna will harvest energy from that signal, and power up the storage chip and the
transceiver.

Lesson 11 – RFID Systems Page 298

Prepared exclusively for [email protected] Transaction: 5095


The card's transceiver will then send the contents of its tag back to the reader, which
can happen over different distances based of the type of tag. Passive tags do not
contain a power source and their distance is generally limited to anywhere between a
few millimeters to a couple of inches, based on the design of the tag.

Active tags contain a battery that can be used to boost the tag's transmission distance.
Some of these tags can be read from hundreds of meters away, but the tags are much
larger than passive tags due to containing a battery, larger antenna, and more radio
amplification circuitry.

Since passive tags don't contain a power source or much circuitry, they can be much
smaller. Some versions are flat stickers that can be stuck to products in a store, and
some are almost as small as a grain of rice and can be implanted under an animal’s
skin.

RFID can be used in many applications, but the underlying technology is the same. The
reader sends out radio waves, and the tag responds with values stored within the tag.

RFID FOR ACCESS CONTROL


When used for building access control, a central computer or controller is used to
monitor one or more card readers. This controller will also have the ability to unlock
doors electronically. This means that the controller is the heart of the access control
system.

The controller is where access control permissions are managed. It stores all tag or
keycard information, along with what areas or doors that keycard is permitted to access.

The software inside the controller operates much like some of the if/else programs you
have created throughout this course. When a keycard is scanned, it's unique value is
used to check a list of permissions stored inside the controller. For example:

User A is issued card number 123. Card 123 is allowed to access door 1 and door 2.

User B is issued card number 456. Card 456 is only allowed to access door 2.

If card 123 is scanned at door 1 or 2 then access will be granted. If card 456 is scanned
at door 2 then access will be granted, but when scanned at door 1, access will be
denied. This is nice because access to areas can be granted or revoked just be
changing the permissions in the main controller, instead of handing out individual metal
keys, or changing locks.

Lesson 11 – RFID Systems Page 299

Prepared exclusively for [email protected] Transaction: 5095


SECURITY CONCERNS OF RFID
While more convenient, RFID does have some inherent security risks associated with
the technology. With a metal key in your pocket there is little to no risk of anyone
duplicating a key while it's in your pocket. The same cannot be said for RFID cards.

Although encryption is used to secure data storage and communication on some higher
end cards, most cards are not very secure. These less secure cards will respond to any
reader that requests its data, given that reader is operating in the right frequency range.
The tags in your kit operate in the 13.56MHz range, so they will start transmitting their
stored data anytime they receive a 13.56MHz signal.

This is where the problem occurs. Your card doesn't know the difference between the
RFID reader in your kit and any other 13.56MHz reader it sees. Imagine that you wired
your front door to be conveniently controlled by an RFID lock that only opened for a tag
that sent out the string Open Sesame. Everything is working great and you no longer
have to carry keys, using only the tag to unlock your front door.

Since your tag will respond to any 13.56MHz reader, it's possible that your tag might get
scanned by someone hoping to duplicate your tag in order to access your house.
Someone close enough to your tag in line at a coffee shop could send a 13.56MHz
signal your way, and your tag would respond to them with Open Sesame. They can now
duplicate or clone your card and unlock your front door with their copy of your card.

Your tag won't know that it was scanned and the reader on your front door won't know
the difference, since it's only looking for the string Open Sesame to unlock the door. This
attack is fairly uncommon, but it is made possible by the convenience of RFID. There
are RFID shielding devices available that make this type of attack impossible, but this
security does come at the cost of convenience.

These RFID shielding devices completely enclose your card in a material that radio
waves cannot penetrate. This shielding can be built directly into wallets, purses, or
smaller pouches and can hold a single card or tag. While inside this shielding, your card
cannot be read by an attacker, but it also cannot be read by your front door reader
either. You would need to remove your card from this shielded pouch in order for the
radio waves emitted by the reader to interact with the card, and for the card to send its
data back to the reader.

As with everything, there are trade-offs between security, convenience, and cost. The
benefits of deploying an RFID system would need to be weighed against the possible
cloning and misuse of a card. More secure RFID systems can lessen the risk, if not
eliminate, the possibility of these attacks, but this will require a higher-level system with
encryption that will come at a higher financial cost.

Lesson 11 – RFID Systems Page 300

Prepared exclusively for [email protected] Transaction: 5095


MFRC522 TAG READER
The RFID reader included in your kit runs on the MFRC522 chipset which can read and
write 13.56MHz tags. The Raspberry Pi requires installation of a special library to
communicate with the MFRC522, but once installed, communication with the device is
very simple over the SPI bus.

These libraries do not support software SPI, so hardware SPI will be used to
communicate with the reader. Using hardware SPI means that specific GPIO pins will
be used for this communication, and that a setting in the Raspberry Pi will need to be
changed in order to enable hardware SPI. In the activities for this lesson, you will install
this library and change the SPI setting once the reader is wired up.

Here is a pinout of the MFRC522 board, as well as signal descriptions:

CE0 – Chip Enable

SCLK – Serial Clock

MOSI – Master Out, Slave In

MISO – Master In, Slave Out

IRQ – Interrupt, not used in Raspberry Pi with hardware SPI

Ground – Connect to Ground

Reset – Will trigger a reset of the MFRC522

3.3V – Connect to 3.3V supply

Lesson 11 – RFID Systems Page 301

Prepared exclusively for [email protected] Transaction: 5095


There are many RFID cards on the market, but not all are compatible with this reader. If
you plan to purchase additional cards for other projects, ensure they belong to one of
the families listed below:

MIFARE1 S50

MIFARE1 S70

MIFARE Ultralight

MIFARE Pro

MIFARE DESFire

Cards may also list compatibility with ISO14443A, which means they will also work with
this reader. If you plan to use a card not listed above, please do your research to ensure
it will be compatible with the MFRC522 reader before purchasing.

RFID TAGS
Tags can come in many shapes and sizes and can store different amounts of
information. The tags in your kit will hold 1KB (one kilobyte) of information. This is not a
ton of room, but it is enough to store enough information to identify the card with a
reader. Your program can then respond however you would like to the presence of the
card.

The tags and library you will be using in this lesson support the reading of two values:

The card's UID value, or Unique ID, is a unique 12 to 13-digit value that is assigned at
the time the card is manufactured. This value can be read but cannot be modified.

The card's text value is a 48-character field that can store any data you like. This could
be anything from a single letter, number, or character, up to a complex string of all these
combined. This value can be read and written by the MFRC522 reader.

Lesson 11 – RFID Systems Page 302

Prepared exclusively for [email protected] Transaction: 5095


READING TEXT FROM THE TAG
One thing to note about the text value is that it is 48 characters long, and shorter strings
written to the card will be formatted to use all 48 of these characters. If you intend to
write the value card to your tag, only four of the character slots will be used, leaving 44
unused.

These unused values cannot be left empty, so spaces will be used to fill up the rest of
the characters available. This means that when trying to write a short value like 'card',
the actual value written to the card will be:

'card '

This wont normally be an issue unless you want your program to act on this value when
read back from the card.

This wouldn't normally be a problem unless you want to make an action happen in your
program based on this text value:

'card' does not equal 'card '

If 'card ' is read from the text


field of the tag, and your program is looking for the string 'card', then the strings will
not match, and the program will not operate as expected.

Leading spaces refer to spaces that occur before the information in your string:

' card'
Trailing spaces refer to spaces that occur after the information in your string:

'card '
Python has a built-in function to get rid of these extra spaces, and you will learn more
about it in the next section.

Lesson 11 – RFID Systems Page 303

Prepared exclusively for [email protected] Transaction: 5095


REMOVING TRAILING SPACES IN PYTHON
As you found in the last section, sometimes it is necessary to remove leading and
trailing spaces, also known as whitespace, from a string. Fortunately, Python makes this
easy with the strip() command:

short_var = long_var.strip()

In this example, the string long_var will be stripped of all leading and trailing spaces,
and saved as short_var.

long_var = ' 42 Electronics '


short_var = long_var.strip()

print(short_var)

The strip() command will only strip leading and trailing spaces from the string, and
not the one between the words. After stripping and being saved as short_var, the print
command will print 42 Electronics to the console, instead of the longer version with
extra spaces.

There are also some other variations of the strip() command:

lstrip() Removes only leading whitespace from a string

rstrip() Removes only trailing whitespace from a string

You may have noticed that the rstrip() command could also be used to eliminate
trailing whitespace from the text value read from a card. This is true, but strip() will
ensure that any extra spaces, whether before or after the value in the string, are
removed.

Lesson 11 – RFID Systems Page 304

Prepared exclusively for [email protected] Transaction: 5095


DETERMINING PROGRAM TYPE: SHEBANG OR #!
You haven’t seen them in programs yet, but a shebang line can be used at the
beginning of a program to help some systems identify what type of program is below.
The line will be the very first line of a program, and it will start with the characters #!
which are referred to as a shebang. The line of code might look something like this:

#!/usr/bin/env python

or

#!/usr/bin/env python3

In Unix-like operating systems, this line can be used to determine the path to locate the
program that should be used to execute the program, and which program should be
used to run the program. Both examples above specify the path for finding Python as
/usr/bin/env, but they specify different versions of Python. The first example specifies
Python, but not which version. Without modifications to your system, Raspbian will use
Python 2 for a file containing this type of shebang. The second example specifies that
the program must run in Python 3.

These lines of code are only used when the Python version is not specified when
running the program on the command-line. Starting the program using sudo python
program.py or sudo python3 program.py will override this line, and it will only be
treated as any other comment in the program. This is the same behavior we see in
Thonny. The environment setting in Thonny will determine which version of Python is
used to run a file, not the shebang line.

This won't normally be an issue for you since programs so far have been run in either
Thonny using Python 3, or in the command-line using Python 2. This information can be
helpful if you're trying to build a project you found online, and odd errors seem to be
popping up.

A shebang including of something like #!/usr/bin/env python3 or #!/usr/bin/env


python2.6 means that program was intended to specifically run in that version of
Python. Attempting to run a program intended for Python 2.6 using Thonny, where the
default is Python 3, will likely result in program errors.

Lesson 11 – RFID Systems Page 305

Prepared exclusively for [email protected] Transaction: 5095


READING AND WRITING TAGS
In order to read and write tags, you will install a couple of libraries in Activity #1 that will
enable your Pi to communicate with the MFRC522. One of the libraries is SPI-Py which
is takes care of the communication framework needed for the SPI bus. The second
library is MFRC522-python which is used to greatly simplify sending data to and from
the MFRC522,

Once these libraries are installed, and the SimpleMFRC522 module is imported into
your program, working with the reader becomes very easy:

reader = SimpleMFRC522.SimpleMFRC522()

This line of code will allow you to refer to the reader as reader in your program, instead
of the much longer name above.

id, text = reader.read()

This command will read the id number and text value stored on the tag and set them
equal to id and text. These variables can then be printed or used in other ways
throughout your program.

reader.write('card')

The command above can be used to modify the text value stored on the tag. In this
example, card will be written to the tags text value. The id number field is fixed so there
is no command available for changing that value.

That is the extent of the commands that we need to interact with the reader. Using
these two commands you can change the text value of a tag, read the id and text value
of a tag, and then have your program take any actions you would like, based on the tag
data that is presented.

Lesson 11 – RFID Systems Page 306

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will connect the RFID reader to your Raspberry Pi, install
two libraries that will be used to communicate with the reader, and write a couple of
programs that will interact with the reader.

ACTIVITY #1 – ADDING THE RFID READER AND SOFTWARE


In this activity you will connect the RFID reader and install software that will allow you to
read and write tags. The circuit from Activity #10, Lesson #2 will be used as the starting
point for this lesson.

STEP #1
Shut down the Pi and disconnect power before proceeding.

To make room for the new components, the phototransistor, potentiometer, and any
associated components will need to be removed from the breadboard.

Using the circuit from Lesson #10, Activity #2 as a starting point, remove the
phototransistor, potentiometer, resistor, and any associated jumper wires. The circuit
should now look like this:

Lesson 11 – RFID Systems Page 307

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The MCP3008 will also need to be removed from the breadboard. Exercise a lot of
caution when removing the IC from the breadboard. If the IC comes out of the board
unevenly it can cause the pins to bend, and they may break when straightened.

Gently inserting a small, flat screwdriver under alternating sides of the IC is the safest
way to remove the IC.

Remove the IC and associated jumper wires from the breadboard. If you're at all unsure
on the best way to do this, watch the short video on the Level B Resource Page before
attempting to remove the IC. The breadboard should now look like this:

Lesson 11 – RFID Systems Page 308

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Now that you have room on the breadboard, it's time to install and connect the reader.

Install the 8-pin connector of the reader into H49 through H56. The reader should be
oriented such that the main body of the reader is above covering columns A through H.

Lesson 11 – RFID Systems Page 309

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
The reader is now ready to be connected to the wedge. Make the following connections
between the reader and the wedge:

3.3V – J49 to P1-41 MISO – J53 to C11


Reset – J50 to J11 MOSI – J54 to C10
Ground – J51 to N2-51 SCLK – J55 to C12
No connection – J52 SDA – J56 to J12

Double-check all connections with this photo before proceeding to the next step.

Lesson 11 – RFID Systems Page 310

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
Now that the reader is connected it's time to power on the Pi and enable hardware SPI.

Power on your Raspberry Pi.

Once it's up and running click on the raspberry in the top-left corner, select Preferences,
and then select Raspberry Pi configuration from the bottom of the list.

Lesson 11 – RFID Systems Page 311

Prepared exclusively for [email protected] Transaction: 5095


Once inside the configuration utility, select the Interfaces tab, and select the Enabled
radio button next to SPI. This will turn on hardware SPI in the Raspberry Pi.

Reboot your Raspberry Pi to allow the SPI setting change to take effect.

Lesson 11 – RFID Systems Page 312

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Before installing the required libraries, make sure your Pi is fully updated so the libraries
will have access to the latest versions of the Raspberry pi software packages.

Open a Terminal window by clicking the terminal button in the top menu bar. Once
open, use the command sudo apt-get update to ensure your Pi knows the latest
version numbers of all packages. Once that completes, run sudo apt-get upgrade to
upgrade any required packages to the latest version. Answer y to any questions about
free disk space that will be consumed by the upgrades.

Lesson 11 – RFID Systems Page 313

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Now that your Pi is fully updated, it's time to clone the required libraries from GitHub.

In your existing Terminal window, type the following commands, pressing enter after
each command:

First, ensure you are still located in the /home/pi directory:

cd ~

Next, clone SPI-Py from the 42 Electronics GitHub repository:

git clone https://github.com/42electronics/SPI-Py.git

Now change directories into SPI-Py:

cd SPI-Py

The last step is to execute the setup.py install script for python3:

sudo python3 setup.py install

SPI-Py will now be installed for use in the Python 3 environment, which you can run
from within Thonny. If you encounter any errors during this process, start over from the
beginning of this step.

Lesson 11 – RFID Systems Page 314

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
The last library to clone will be MFRC522-python. This library does not require the
install step, just cloning from GitHub.

In your existing Terminal window, type the following commands, pressing enter after
each command:

First, move yourself back to the /home/pi directory:

cd ~

Next, clone MFRC522-python from the 42 Electronics GitHub repository:

git clone https://github.com/42electronics/MFRC522-python.git

If you encounter any errors during this process, start over from the beginning of this
step.

Lesson 11 – RFID Systems Page 315

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – READING AND WRITING TAGS
In this activity you will connect the read information from and write information to the
tags included in your kit.

STEP #1
You now have a local copy of the MFRC522-
python repository.

Open File Manager by clicking on the folder icon


in the top menu bar. File manager will open in the
/home/pi directory. Double-click on the MFRC522-
python directory to view its contents.

Some example programs called read.py and


write.py have been included to allow you to
quickly begin reading and writing tags.

Double click the file named read.py and it will


open in Thonny:

Run read.py by clicking the run button in Thonny. Place the card near the reader and
its id number and text value will be displayed in the console output.

The text field will be represented


by 48 squares because this field
is completely empty from the
factory.

Scan the blue tag to ensure it


scans properly, and that it
displays a different id value than
the card.

Lesson 11 – RFID Systems Page 316

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The cards can be read but you need to fix the empty text value fields by writing new
data to the tags.

Navigate back to the File Manager and double-click on write.py. Write.py will open as a
new tab in Thonny.

Make sure there are no tags near the reader when you run write.py. The program will
write any tag within range and you want to make sure the tags get programmed
correctly, so you can write a program in Activity #3 that will recognize these values.

You will now write the text value of card to the white card.

With no tags near the reader, run write.py in


Thonny. In the Shell window of Thonny, enter
card as the value to be written to the tag, and
press enter. Place the white card near the
reader when prompted, the tag will be written,
and Tag written will be printed to the Shell for
confirmation.

If any errors occur, or the program does not run as expected, stop write.py in Thonny
and run it again. Do not proceed to the next step until the white card has successfully
been written with the text value card.

STEP #3
Now that the card has been written you can work on the blue tag. Make sure to hold the
metal keyrings on the tag to keep them from coming into contact with any metal
connections on the circuit board of the reader.

Using the same process in the last step, write a text value of tag to the blue tag.

Lesson 11 – RFID Systems Page 317

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Confirm the tags were properly written by reading the
values back.

Switch tabs in Thonny back to read.py and run that


program. Scan the white card to ensure it reports its ID
number and the text string card when scanned.

Run the program again and scan the blue tag to


ensure it reports its ID number and the text string tag
when scanned.

You now know that the text values loaded correctly and now you can create a program
that can make decisions based on those values.

NOTE: The RFID reader's proper operation relies on good connections to power,
ground, and data lines on the Raspberry Pi. Poor or intermittent connections can
cause the reader not to operate properly. If your reader stops reading or writing
for no reason, carefully remove and reinstall each of the jumper wire connections
in J49 through J56. If the problem with your reader is due to a bad jumper wire
connection, this should fix the problem.

The reader will read a tag very quickly, but if you try to swipe a tag by the reader
extremely fast it is possible to pull the tag away from the reader before it's
finished reading. This will result in a card read error that might look something
like this:

If this happens, just scan the tag again, more slowly, and everything should work
as expected.

Lesson 11 – RFID Systems Page 318

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – CREATING AN ACCESS CONTROL PROGRAM
In this activity you will create a program that reads the text values from scanned tags,
and prints a message letting the user know if access is granted, or not.

STEP #1
Use the read.py program as a
starting point, as it already contains
everything needed to read tags.

With read.py open in Thonny, select


File, and then select Save as from
the dropdown menu. Enter a file
name of access_control and click
the Save button:

NOTE: Programs that need to read RFID tags must be located inside the MFRC-
Python directory, as they will need direct access to the SimpleMFRC522.py and
MFRC522.py files used to communicate with the reader. Attempting to run
programs that require access to these communication files from anywhere else,
will result in Python errors.

Lesson 11 – RFID Systems Page 319

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
You now have a copy of read.py saved as access_control.py that can be modified
without affecting the original file. The program is currently running a try: loop until it
sees a tag, prints the id and text values, and runs a GPIO.cleanup() before exiting.
Only one tag can be read before the program automatically exits. Let's add a while
True: loop inside the try: loop to keep the program reading tags until you exit the
program.

Add a while True: loop just below the try: loop. The addition is highlighted below:

reader = SimpleMFRC522.SimpleMFRC522()

try:
while True:
id, text = reader.read()
print(id)
print(text)
time.sleep(.3)

finally:
GPIO.cleanup()

Run the program and read the card and tag.

You will notice the program is now reading without exiting after each tag, but it's reading
one tag multiple times because this program can loop very quickly, and the tag might be
near the reader during more than one loop.

Another problem with the program is that there is no longer away to exit the program
gracefully. Press the stop button in Thonny or CTRL-C to stop the program. This will
result in errors because the program was busy communicating with the reader when the
program was ended. You will fix the looping and exit problems in the next step:

Lesson 11 – RFID Systems Page 320

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
It's time to fix the duplicate read and exit issues that we created in the last step. You will
add a sleep command to slow down the loop and modify the finally: command to
catch keyboard exceptions.

Import the time module at the beginning of the program and add a time.sleep(.3) to
the end of the loop. Also, change finally: to except KeyboardInterrupt: which will
allow manually ending the program to trigger the GPIO.cleanup(). Additions and
changes are highlighted below:

#!/usr/bin/python3

import RPi.GPIO as GPIO


import SimpleMFRC522
import time

reader = SimpleMFRC522.SimpleMFRC522()

try:
while True:
id, text = reader.read()
print(id)
print(text)
time.sleep(.3)

except KeyboardInterrupt:
GPIO.cleanup()

Run the program.

It can now scan tags reliably without duplicating reads and pressing stop in Thonny or
CTRL-C will not generate errors, as GPIO.cleanup() is being triggered before the
program exits.

Lesson 11 – RFID Systems Page 321

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Now that the program is reading cards without exiting, it needs to strip the trailing
whitespace from the text value that's being read from the card. To do this you will run
the strip() command on the value of text that's read from the card.

Replace the two existing print statements with a strip() command that will strip trailing
whitespace from text and save it as the new value of text. Changes highlighted
below:

try:
while True:
id, text = reader.read()
text = text.strip()
time.sleep(.3)

The id number and text value will be read from the card and the text value will be
stripped down to either 'card' or 'tag' based on the tag that was scanned.

Lesson 11 – RFID Systems Page 322

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
You can now add some if statements to check which tag is being read and print whether
the tag is granted or denied access. Go ahead and grant access to the card, but deny
access to the tag, by adding two if statements.

Add two if statements directly under the strip() command that will check the value of
text and print ACCESS GRANTED or ACCESS DENIED based on which tag is read:

try:
while True:
id, text = reader.read()
text = text.strip()
if text == 'card':
print('ACCESS GRANTED')
if text == 'tag':
print('ACCESS DENIED')

time.sleep(.3)

Run the program and read both tags. The access messages will be printed each time a
card is scanned to indicate ACCESS GRANTED or ACCESS DENIED.

This code is being kept very simple to illustrate the concept, but the print sections of the
if statements in this program could be replaced with anything you like. By including
GPIO pin setups at the beginning of your program you could light an LED when access
is granted, play a noise through the piezo speaker when access is denied, or any
combination of print statements and GPIO events that you might want.

In Lesson B-12, you will add more advanced program functionality, and circuitry that will
allow for additional notification options.

• Leave this circuit built as it will be used as a starting point in Lesson B-12.
• Save the program to use as a starting point in Lesson B-12.

Lesson 11 – RFID Systems Page 323

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Is hardware SPI turned on by default on the Raspberry Pi, or does it require a
menu change and reboot to become enabled?

2. What is the Python command that removes leading and trailing whitespace from
a string?

3. Do all RFID tags work with all RFID readers?

Answers can be found on the next page.

Lesson 11 – RFID Systems Page 324

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Is hardware SPI turned on by default on the Raspberry Pi, or does it require a
menu change and reboot to become enabled?

ANSWER: Hardware SPI requires a menu change and a reboot to be enabled.

2. What is the Python command that removes leading and trailing whitespace from
a string?

ANSWER: To remove both leading and trailing spaces, use the strip()
command.

3. Do all RFID tags work with all RFID readers?

ANSWER: No, not all readers and tags are compatible. It is important to check
for compatibility between RFID readers and RFID tags.

Lesson 11 – RFID Systems Page 325

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to work with RFID readers, including how to read and write
tags, and remove trailing spaces.

In the next lesson you will continue to work with RFID readers but add more advanced
Python programming techniques to create more complicated program functionality.

Lesson 11 – RFID Systems Page 326

Prepared exclusively for [email protected] Transaction: 5095


LESSON 12

USING INPUT FILES AND


MULTITHREADED OPERATIONS

OBJECTIVE
In this lesson, you will learn to use advanced Python programming techniques that will
enable you to create more complicated programs.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, mouse, and the internet


• Assembled Circuit from Lesson B-11

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Importing Modules (Lesson A-13)


• Loops (Lesson A-17)
• Functions (Lesson B-2)
• Lists (Lesson B-10)
• RFID (Lesson B-11)

Lesson 12 – Using Input Files and Multithreaded Operations Page 327

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson, you will learn about some advanced Python programming techniques that
will enable you to create more complicated programs. Some of these techniques include
reading data from an external file, formatting the current date/time, and running multiple
operations at once in your program.

USING A FILE FOR INPUT

Your program does not always need to contain every bit of information needed to
operate. Using the access control system you created in an activity in Lesson B-11 as
an example, you might want to create a text file list containing users and access levels,
that your program can access to determine which cards should be allowed and denied
access. Keeping a separate access list means that you do not have to modify your
program every time a user needs to be granted or denied access.

INPUT FILE TYPES AND FORMATTING


The first requirement for reading a file is making sure that you understand the format of
the file containing the data. One type of very common data file is a .txt or text file that
contains your data values. The values must be stored inside the file in a very specific
way, so your program can interact with the values, and understand what is to be done
with them.

A very common data format is called .csv which stands for Comma Separated Values.
In this format, each piece of data is separated by a comma. This might be something as
simple as a list of people and their favorite colors:

Annie,blue
Bobby,green

Cindy,yellow

David,red

Lesson 12 – Using Input Files and Multithreaded Operations Page 328

Prepared exclusively for [email protected] Transaction: 5095


Due to the formatting of this file, a program can read the information contained in this
file, and it will now know each person's favorite color. You can use this information to
answer questions like:

What is David's favorite color?

Who likes the color blue?

The program can look up the value for each question and determine which person or
color should be provided as the correct answer.

You may also see data files that include header information that labels each column in
the first line:

Person,Color
Annie,blue

Bobby,green

Cindy,yellow

David,red

This may seem little silly with only two columns, but .csv files can get much more
complicated, and hard to read, as they get larger. The column names can help keep
track of what each value represents,

Person,Color,Age,State,Lesson,Joined,LastLogin

Annie,blue,12,CA,A,2017,27JUN2018

Bobby,green,14,WA,B,2016,2JUL2018

Cindy,yellow,13,NC,A,2018,22JUL2018

David,red,12,FL,B,2018,12FEB2018

Lesson 12 – Using Input Files and Multithreaded Operations Page 329

Prepared exclusively for [email protected] Transaction: 5095


This format is a little hard for humans to read, but when imported by a program, all the
values will be well organized, and each of the values can be used to answer a question.
Spaces after the commas might make the values easier to read, but that would create a
problem. Every character between the commas becomes part the data, including the
spaces. Consider the following data:

Annie, blue, 12
Bobby, green, 14

Cindy, yellow, 13

David, red, 12

If you ask the program to find the person that likes 'green', it won't find anyone when
using this data. This is due to the space that made Bobby's favorite color ' green'
instead of 'green'. Since ' green' does not equal 'green' no match will be found,
and the program will report that none of the people listed likes the color 'green'.

This is why it's very important to understand the format of your data, before attempting
to use it in a program. If another program is creating the .csv file you may not have the
option of removing spaces or other problems you might find. In that case, your program
may need to perform some additional tasks on the data, such as stripping leading
spaces, prior to using the data in your program.

Lesson 12 – Using Input Files and Multithreaded Operations Page 330

Prepared exclusively for [email protected] Transaction: 5095


OPENING A FILE IN YOUR PROGRAM
Once you have determined the input file type and formatting that you will be using, you
can open the file in Python by using the following command:

with open ('file_to_open.txt', 'r') as data:

This will open the file named 'file_to_open.txt' in read-only mode due to the 'r' argument
enclosed with the filename. In this example, the data at the end of the command
specifies that the information pulled in from the file will be referred to as data. This data
value can then be used in your program to refer to the input file.

One thing to note about the filename specified in the command above is that there is no
path specified for the input file, such as /home/pi/file_to_open.txt or
/home/pi/file_to_open.txt. The filename without a path will only work if the file is
located in the same directory as your program. Adding this open command
to:/home/pi/MFRC522-python/access_control.py means that the program will be looking
for file_to_open.txt in the /home/pi/MFRC522-python directory. If the file is not
located in that folder, then Python will generate an error, and you will either need to
move your input file inside /home/pi/MFRC522-python, or specify the correct path to
your input file in the open command.

Lesson 12 – Using Input Files and Multithreaded Operations Page 331

Prepared exclusively for [email protected] Transaction: 5095


READING VALUES FROM A FILE
Python includes a module called csv that can read .csv files and create a list from the
contents of the file. The csv module must be imported at the beginning of the program if
you plan to use its commands:

import csv

Now that the csv module has been imported, you can use the csv.reader() command
to read the information contained in data. The parenthesis must contain the name of
the file to be read, in this case, data. The list() command is wrapped around the
csv.reader() command so a list can be created by the output, and saved as a new
variable called values:

with open ('file_to_open.txt', 'r') as data:

values = list(csv.reader(data))

This will read the csv values from the file named data and save them as a list of strings
named values. One thing to note about input files that contain multiple lines is that
each line will be stored as its own list. When there are multiple input lines involved, you
end up with a list of lists, also known as a matrix. Here are some examples of a single
list versus a matrix:

Single-line input file: Annie,blue,12

values will contain: ['Annie','blue','12']

Multi-line input file: Annie,blue,12


Bobby,green,14
Cindy,yellow,13
David,red,12

values will contain: [['Annie','blue','12'],['Bobby','green','14'],


['Cindy','yellow','13'],['David','red','12']]

Lesson 12 – Using Input Files and Multithreaded Operations Page 332

Prepared exclusively for [email protected] Transaction: 5095


In the multi-line input file, each line is its own small list, and each new line is added to
the end of a larger list. This is good because the data is still separated into logical
blocks that still represent the values from each line of the input file. The actual value of
the list named values will look like this:

[['Annie','blue','12'],['Bobby','green','14'],['Cindy','yellow','13'],
['David','red','12']]

By stacking each small list on top of each other, you can see the familiar pattern of rows
and columns from the input file:

[['Annie','blue','12'],

['Bobby','green','14'],

['Cindy','yellow','13'],

['David','red','12']]

The information in a matrix can be accessed by using the index of the row and then the
column of the data you want. Just like before when accessing positions of a string, row
index values start at 0 and move to the right. Column index values begin at 0 with the
top line and work their way down:

print(values[0][1]) will print the value in row 0, column 1 or blue

print(values[2][2]) will print the value in row 2, column 2 or 13

This way, the program can be used to locate specific values that were pulled in from the
input file.

Lesson 12 – Using Input Files and Multithreaded Operations Page 333

Prepared exclusively for [email protected] Transaction: 5095


MULTITHREADED OPERATION
Computer programs, as you have already learned, run sequentially from top to bottom,
making detours on the way based on if/else statements or other decision points in the
program. This works well, but only one program operation can happen at a time. If the
program is sleeping in between button checks or LED flashes, nothing else can be
happening in the program.

Consider a program where you want to constantly monitor a button for presses, and
also blink a status light once every 10 seconds:

Check button

if button pressed then do something


Wait 10 seconds

Turn on LED for .5 seconds

Turn off LED

Repeat

The LED will flash once every 10 seconds, but this program has a problem with the
button check. The button will be checked initially, and then the program sleeps for 10
seconds before turning on the LED. During this delay, any button presses will not be
registered. Holding the button down during a check would allow the push to be
registered, but this would not be a very user-friendly circuit or program. The good news
is that Python has a way to handle this using multiple program threads.

Lesson 12 – Using Input Files and Multithreaded Operations Page 334

Prepared exclusively for [email protected] Transaction: 5095


A thread in a program is a sequential set of events that can only do one thing at a time,
like the example above. While the main program thread executes automatically, Python
has the ability to create new threads that will run in parallel with your main program. The
status LED above can run in its own thread so it's delays do not impact the monitoring
of the button. That would look something like this:

Start program
Define function that blinks LED every 10 seconds

Start LED function as a new thread

Check button

if button pressed then do something

The LED can now be slowly flashing in a secondary thread, while the main program
constantly checks the button for presses. To use multithreaded operations, you must
first import the _thread module:

import _thread

Next, a function needs to be created that will define what will happen in the new thread:

define status_led():

while True:

time.sleep(10)

GPIO.output(26, GPIO.HIGH)

time.sleep(.5)

GPIO.output(26, GPIO.LOW)

The function will be running like its own program, so it will need a while True: or some
other type of condition check to keep it running. Otherwise, it will only run through one
time and the thread will stop.

Lesson 12 – Using Input Files and Multithreaded Operations Page 335

Prepared exclusively for [email protected] Transaction: 5095


Now that the function has been defined the last step is to create the new thread using
the command below:

_thread.start_new_thread(status_led, ())

This will send the command start_new_thread to the _thread module. The name of
the thread to start is status_led. The () in this command is similar to calling a function
in a program:

status_led will not call the function and Python will generate an error

status_led() will call the function properly

This trailing () is still included when starting a new thread, it's just separated from the
function name by a comma.

IMPORTANT NOTES ABOUT MULTITHREADED OPERATION


In Python 3, the thread module is named _thread. In Python 2, the thread module is
named thread. This can cause headaches when trying to run programs you might find
online, so it's good to remember this difference in module names between Python 2 and
3.

When running programs using the command line, new threads that are created to loop
continuously, like the status_led example above, terminate as soon as the main
program ends. This is not the case in Integrated Development Environments like
Thonny.

IDEs are convenient to use, but they do not always mimic everything that would happen
when running programs using the command line. This is true when terminating new
threads that were started in your program. Pressing the stop button in Thonny will
terminate the main program, but the secondary thread will continue running in the
background. Pressing the stop button again will terminate this secondary process.

A good way to work around this is to use a conditional loop in your secondary thread.
Instead of creating an endless loop by using while True:, do a variable check like
while end == 0: and modify the value of end in your main program. This way the
value of end will be evaluated each time the new thread runs, and if end is ever
anything but 0, the loop will stop, and the thread will terminate. You could also use this
end value to control your main program loop:

Lesson 12 – Using Input Files and Multithreaded Operations Page 336

Prepared exclusively for [email protected] Transaction: 5095


end = 0

def status_led():
while end == 0:
do LED things

start status_led thread

try:
while end == 0:
do main program things

except KeyboardInterrupt:
end = 1

Both the main program and the status_led thread will loop continuously as long as
end equals 0, which is set at the beginning of the program. Pressing CTRL-C or Stop in
Thonny will generate a KeyboardInterrupt, changing the value of end to 1. The main
program will stop, and the new value of end will cause the loop in status_led to
evaluate as False, terminating the secondary thread.

Using the technique above will help you avoid threads continuing to run after the main
program has terminated, when using Thonny.

Lesson 12 – Using Input Files and Multithreaded Operations Page 337

Prepared exclusively for [email protected] Transaction: 5095


FORMATTING AND DISPLAYING TIME AND DATE
Using the strftime command in your program can allow you to display the current date
and time, in any format that you like. The strftime command is part of the time
module that you have previously used in all programs that require delays using the
time.sleep command.

The strftime command can create a string of the current date and time in whatever
format you like:

time.strftime('%Y-%m-%d %H:%M:%S')

This command will create a string of the current date and time that will be formatted
using the sequence in parentheses. These values are called directives:

%Y = Year %H = Hour
%m = Month %M = Minutes
%d = Day %S = Seconds

Capitalization is very important for these directives, so %Y is not the same as %y. They
both represent the year, but in different formats:

%Y represents the year with century included or 2018

%y represents the year without the century or 18

A full list of al the directives available for the strftime command is available at:

https://docs.python.org/3/library/time.html

Running print(time.strftime('%Y-%m-%d %H:%M:%S')) will result in the following


being printed to the console:

2018-07-02 15:45:09

Lesson 12 – Using Input Files and Multithreaded Operations Page 338

Prepared exclusively for [email protected] Transaction: 5095


Having a string like this available can make the messages printed by something like an
access control system much more meaningful. In the Activities section, you will add this
functionality to the access control program you created in Lesson B-11.

Lesson 12 – Using Input Files and Multithreaded Operations Page 339

Prepared exclusively for [email protected] Transaction: 5095


BREAKING OUT OF A LOOP
Loops using for and while are very useful, but sometimes you need to exit a loop
before the end. This behavior is good for things like when a loop is searching for a value
and it's been found or if a program needs to end. You can use a break command inside
the for or while loop to break out of the loop:

end = 0

while True:
print('running')
if end == 0:
break

print('exiting')

The while True: loop would normally run forever, printing 'running' over and over, but the
if statement inside is evaluated each time the loop runs. The very first time the loop
runs, the if statement evaluates as True, and the while loop is broken, allowing the rest
of the program to run. The console output from this program would look like this:

running
exiting

The break statement causes any higher level for or while loop to exit, no matter how
far nested the break is inside the outer loop:

end = 0

while True:
print('running')
if end == 0:
if end == 0:
if end == 0:
break

print('exiting')

Lesson 12 – Using Input Files and Multithreaded Operations Page 340

Prepared exclusively for [email protected] Transaction: 5095


This program will still have the exact same output. running will only be printed once
since each consecutive if statement evaluates as True, and the final if statement
causes the outer while loop to break, allowing exiting to be printed.

Lesson 12 – Using Input Files and Multithreaded Operations Page 341

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In Lesson B-11 you created an access control system that read an RFID tag and printed
specific messages based on which tag was scanned. In the activities you will add the
following features to this program:

• Create a text file that contains access control user information


• Read the contents of the text file from within Python and allow or deny access
based on the contents of the text file
• Create a new thread that flashes the LED on GPIO26 to indicate that a card has
been read
• Add printing of the employee name along with date/time of the tag reading event

ACTIVITY #1 – READ AN EXTERNAL FILE IN PYTHON


Using the access control program that you created in Lesson B-11 as a starting point,
you will create a text file containing tag names, employee names, and allow/deny
access information, as well as read the file from within Python.

STEP #1
The first step will be to create and populate the text file that will contain the tag
information, employee name, and allow/deny information. In order to simplify the file
path needed in the program, you will be placing this file in the same directory as your
access_control.py program, the MFRC522-python directory.

Lesson 12 – Using Input Files and Multithreaded Operations Page 342

Prepared exclusively for [email protected] Transaction: 5095


With your Raspberry Pi powered on, open File Manager and double-click on the folder
called MFRC522-python.

Once inside that folder, right-click on any open space in that window and select Create
New, and then Empty File. When prompted for a name, type access_list.txt and
click OK.

Lesson 12 – Using Input Files and Multithreaded Operations Page 343

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Now that the file has been created, fill in some data. You will be using the Comma
Separated Values or CSV format for this file. Your white card named 'card' will be
identified as Employee and will be allowed access. The blue key fob named 'tag' will be
identified as visitor and will be denied access.

Double-click the access_list.txt file that you just created, and it will open in a new text
editor window. Type the information below into your text file:

card,Employee,allow
tag,Visitor,deny

After entering the text above, select File from the upper menu, and then select Save to
save your changes. You can now use the X button at the top-right to close the file.

STEP #3
You now have a text file that contains the access information. The reading of this new
file needs to be added to your access_control.py program.

Open access_control.py in Thonny by double-clicking on it in the MFRC522-python


folder. The first step in reading the file will be to import the csv module. Add import csv
below the existing import lines. Addition highlighted below:

import time
import csv

reader = SimpleMFRC522.SimpleMFRC522()

try:

Lesson 12 – Using Input Files and Multithreaded Operations Page 344

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Now open the file and convert its contents into a matrix that your program can use to
determine whether a tag should be granted access or not.

Just above the main try: loop, add a with open statement that opens
access_list.txt in read-only mode, and save the contents of that file as a new
variable called data. Indented on the next line, create a new variable named values
that contains the list output of running csv.reader on the information saved as data.

Additions to the code are highlighted below:

reader = SimpleMFRC522.SimpleMFRC522()

with open('access_list.txt', 'r') as data:


values = list(csv.reader(data))

try:
Your file named access_list.txt will now be opened and converted to a matrix (list of
lists) called values that your program can check against any tags that are scanned.

STEP #5
The text file is now available within your program, but your program is not yet searching
for tag matches in that file. For that you will create a loop that will look for matches
between the scanned tag's text value and any matching entries in the values matrix
data.

Just below the text.strip() command, add a for/in loop that will look through each
line in the matrix called values and temporarily set each line equal to a variable named
search. Add the following line of code just below the text.strip() line.

Additions to the code are highlighted below:

try:
while True:
id, text = reader.read()
text = text.strip()
for search in values:
if text == 'card':

Lesson 12 – Using Input Files and Multithreaded Operations Page 345

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
The program can now search through the values matrix, but there are no instructions
on what it’s looking for, or what to do if a match is found. Next, add some code that will
look in search for a match to the text value from the scanned tag.

If a match is found then the program should grab index values [1] and [2] from the
search list, saving them as user and status respectively. The loop must then break to
stop the search, so your user and status values are not overwritten by the upcoming
else statement.

If no match is found, an else: statement will be used to manually assign a user value
of 'Unknown' and a status of 'deny'. This will keep the program from generating
errors if the tag value scanned is corrupt or not found in the text file.

Indented below the for search in values: line of code, add the following if/else
statements to accomplish the behavior outlined above.

Additions to the code are highlighted below:

for search in values:


if text in search:
user = search[1]
status = search[2]
break
else:
user = 'Unknown'
status = 'deny'
if text == 'card':

The variables user and status are now being set equal to the values in your
access_list.txt file, based on the text value of the tag that is scanned. If the tag
is found, user will store the employee name and status will be equal to either 'allow'
or 'deny' based on your text file. If the tag is not found, user will equal 'Unknown' and
status will equal 'deny'.

Lesson 12 – Using Input Files and Multithreaded Operations Page 346

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
The variables user and status will now reflect who scanned the card and whether they
should be granted access or not. The only problem is that your program is still using the
value of the text string from the scanned card to determine access, bypassing your
new status information. Change the if blocks containing the GRANTED or DENIED
messages to use the value of status instead of text.

Change both if statements to use status instead of text. The status to allow access
must be changed to 'allow', and the status to deny access must be changed to
'deny' so they match the values from the text file.

Updates to the code are highlighted below:

else:
user = 'Unknown')
status = 'deny'
if status == 'allow':
print('ACCESS GRANTED')
if status == 'deny':
print('ACCESS DENIED')
time.sleep(.3)

Lesson 12 – Using Input Files and Multithreaded Operations Page 347

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
Your completed program should now look like this:

#!/usr/bin/python3

import RPi.GPIO as GPIO


import SimpleMFRC522
import time
import csv

reader = SimpleMFRC522.SimpleMFRC522()

with open('access_list.txt', 'r') as data:


values = list(csv.reader(data))

try:
while True:
id, text = reader.read()
text = text.strip()
for search in values:
if text in search:
user = search[1]
status = search[2]
break
else:
user = 'Unknown'
status = 'deny'
if status == 'allow':
print('ACCESS GRANTED')
if status == 'deny':
print('ACCESS DENIED')
time.sleep(.3)

except KeyboardInterrupt:
GPIO.cleanup()

Run your program in Thonny. Scanning the card will result in an ACCESS GRANTED
message and scanning the key fob will result in an ACCESS DENIED message.

Lesson 12 – Using Input Files and Multithreaded Operations Page 348

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – TAG READ INDICATOR
In this activity, you will add code to your program that will flash the LED on GPIO26
when a tag is read. This LED code will be launched as a separate thread, so it does not
interfere with the rest of the program.

STEP #1
The first step is to import the _thread module so it can be used to launch the LED
thread.

Add import _thread to the list of imports at the beginning of the program:

import RPi.GPIO as GPIO


import SimpleMFRC522
import time
import csv
import _thread

STEP #2
Next, the GPIO pin numbering mode must be specified and GPIO26 must be configured
as an output.

Add the GPIO.setmode and GPIO.setup lines just below the import section:

import csv
import _thread

GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.OUT)

reader = SimpleMFRC522.SimpleMFRC522()

Lesson 12 – Using Input Files and Multithreaded Operations Page 349

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Now, it's time to build the function that will flash the LED on GPIO26.

Below the GPIO lines, create a function called scanned() that turns the LED on for .1
seconds, then off for .1, and repeat 5 times:

GPIO.setup(26, GPIO.OUT)

def scanned():
for i in range(0,5):
GPIO.output(26, GPIO.HIGH)
time.sleep(.1)
GPIO.output(26, GPIO.LOW)
time.sleep(.1)

reader = SimpleMFRC522.SimpleMFRC522()

STEP #4
The last step is to modify the if status == statements that currently only print the
messages to the console. In addition to printing the message these blocks will also
trigger the scanned() function that will flash the LED.

Add the following command to both if blocks that print the messages:

else:
user = 'Unknown'
status = 'deny'
if status == 'allow':
print('ACCESS GRANTED')
_thread.start_new_thread(scanned, ())
if status == 'deny':
print('ACCESS DENIED')
_thread.start_new_thread(scanned, ())
time.sleep(.3)

Lesson 12 – Using Input Files and Multithreaded Operations Page 350

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
Your entire program should now look like this:

#!/usr/bin/python3

import RPi.GPIO as GPIO


import SimpleMFRC522
import time
import csv
import _thread

GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.OUT)

def scanned():
for i in range(0,5):
GPIO.output(26, GPIO.HIGH)
time.sleep(.1)
GPIO.output(26, GPIO.LOW)
time.sleep(.1)

reader = SimpleMFRC522.SimpleMFRC522()

with open('access_list.txt', 'r') as data:


values = list(csv.reader(data))

try:
while True:
id, text = reader.read()
text = text.strip()
for search in values:
if text in search:
user = search[1]
status = search[2]
break
else:
user = 'Unknown'
status = 'deny'
if status == 'allow':
print('ACCESS GRANTED')
_thread.start_new_thread(scanned, ())
if status == 'deny':
print('ACCESS DENIED')

Lesson 12 – Using Input Files and Multithreaded Operations Page 351

Prepared exclusively for [email protected] Transaction: 5095


_thread.start_new_thread(scanned, ())
time.sleep(.3)

except KeyboardInterrupt:
GPIO.cleanup()

Run your program and read the tags. Reading a tag will now result in the LED flashing
quickly 5 times whenever a tag is read.

Lesson 12 – Using Input Files and Multithreaded Operations Page 352

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – ADDING DATE/TIME MESSAGES
In this activity you will add the date and time of any tag read that occurs, as well as print
the employee name assigned to the tag that was read.

STEP #1
In the first activity you stored the employee name of the scanned tag as user. You will
now add that variable to the ACCESS GRANTED and ACCESS DENIED print statements.

Add the strings ' for ' and value to the print messages. Make sure to include spaces
before and after for so the messages are spaced properly:

if status == 'allow':
print('ACCESS GRANTED' + ' for ' + user)
_thread.start_new_thread(scanned, ())
if status == 'deny':
print('ACCESS DENIED' + ' for ' + user)
_thread.start_new_thread(scanned, ())

Run the program and confirm that employee names from the text file are now included
with the messages and that they are formatted properly.

STEP #2
Next, add a print statement that will print the date and time of each scan using the
strftime command. You will use the 2018-08-04 12:23:21 formatting from this lesson.
Add the following print statements below the existing print statements:

if status == 'allow':
print('ACCESS GRANTED' + ' for ' + user)
print(time.strftime('%Y-%m-%d %H:%M:%S'))
_thread.start_new_thread(scanned, ())
if status == 'deny':
print('ACCESS DENIED' + ' for ' + user)
print(time.strftime('%Y-%m-%d %H:%M:%S'))
_thread.start_new_thread(scanned, ())

Lesson 12 – Using Input Files and Multithreaded Operations Page 353

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Your completed program should now look like this:

#!/usr/bin/python3

import RPi.GPIO as GPIO


import SimpleMFRC522
import time
import csv
import _thread

GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.OUT)

def scanned():
for i in range(0,5):
GPIO.output(26, GPIO.HIGH)
time.sleep(.1)
GPIO.output(26, GPIO.LOW)
time.sleep(.1)

reader = SimpleMFRC522.SimpleMFRC522()

with open('access_list.txt', 'r') as data:


values = list(csv.reader(data))

try:
while True:
id, text = reader.read()
text = text.strip()
for search in values:
if text in search:
user = search[1]
status = search[2]
break
else:
user = 'Unknown'
status = 'deny'
if status == 'allow':
print('ACCESS GRANTED' + ' for ' + user)
print(time.strftime('%Y-%m-%d %H:%M:%S'))
_thread.start_new_thread(scanned, ())
if status == 'deny':

Lesson 12 – Using Input Files and Multithreaded Operations Page 354

Prepared exclusively for [email protected] Transaction: 5095


print('ACCESS DENIED' + ' for ' + user)
print(time.strftime('%Y-%m-%d %H:%M:%S'))
_thread.start_new_thread(scanned, ())
time.sleep(.3)

except KeyboardInterrupt:
GPIO.cleanup()

Run your program and read the tags. The date and time of the scan will now be printed
just below the DENIED or GRANTED message that includes the employee name.

You may disassemble the circuit as you won’t be needing it for Lesson B-13.

Lesson 12 – Using Input Files and Multithreaded Operations Page 355

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. What is another name for a list of lists?

2. Will a time.sleep in a secondary thread cause the main program to pause as


well, or only the secondary thread?

3. What command would you use to print the current date/time in the following
format? Hour:minutes:seconds day/month/year

Answers can be found on the next page.

Lesson 12 – Using Input Files and Multithreaded Operations Page 356

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. What is another name for a list of lists?

ANSWER: A list of lists is also known as a matrix.

2. Will a time.sleep in a secondary thread cause the main program to pause as


well, or only the secondary thread?

ANSWER: A secondary thread operates independently from the main program


so a time.sleep in the secondary thread will not affect timing in the main
program.

3. What command would you use to print the current date/time in the following
format? Hour:minutes:seconds day/month/year

ANSWER: print(time.strftime('%H:%M:%S %d/%m/%Y'))

Lesson 12 – Using Input Files and Multithreaded Operations Page 357

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to use input files such as text and CSV, as well as how to use
multithreaded operations.

In the next lesson, you will learn about level shifting and have an opportunity to work
with infrared sensors.

Lesson 12 – Using Input Files and Multithreaded Operations Page 358

Prepared exclusively for [email protected] Transaction: 5095


LESSON 13

LEVEL SHIFTING AND


INFRARED SENSORS

OBJECTIVE
In this lesson you will learn how to shift signal voltage levels to make components
compatible with the GPIO pins of the Raspberry Pi. You will also learn to work with
infrared obstacle and line sensors.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse


• 1 x Breadboard
• 1 x Wedge and Ribbon Cable
• 1 x RGB LED
• 3 x 1k-ohm Resistors
• 1 x 10k-ohm Resistor
• 13 x Short Jumper Wires
• 6 x Long Jumper Wires
• 1 x 74LVC245 Level Shifting Integrated Circuit
• 1 x Infrared Obstacle Sensor
• 1 x Infrared Line Sensor

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• RGB LED (Lesson A-6)


• GPIO Pins and High and Low Signals (Lesson A-16)
• Integrated Circuits (Lesson B-9)

Lesson 13 – Level Shifting and Infrared Sensors Page 359

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson you will learn about shifting the voltage level of signals, when it is
necessary, as well as working with infrared obstacle and line sensors.

SIGNAL LEVEL SHIFTING


In previous lessons you learned that digital signals can be either high or low. The
voltage level of a low is always zero volts, but the voltage level of a high signal can
depend on the piece of equipment you are working with.

The GPIO pins on a Raspberry Pi operate in the range of 0 to 3.3 volts, with 3.3V
representing a high signal. Another popular microcontroller board is called the Arduino
and its GPIO pins are based on a system with a range of 0 to 5 volts. In the case of the
Arduino, a high signal will be 5V.

Many devices, such as infrared sensors or RFID readers, are


specifically designed to communicate using either 3.3V or 5V
signals, but you must ensure they are compatible with your device.
An Arduino can accept 5V signals and can generally interface with
3.3V devices as well. The Raspberry Pi, with its lower 3.3V GPIO
voltage, will be damaged if connected to a device that sends out
signals at a 5V level. Therefore it is extremely important to
understand what signal levels a device will be outputting before connecting it to your
Raspberry Pi.

Generally, devices will use their supply voltage to determine the voltage level used for
communication. If an RFID reader is powered by 3.3V, then it will likely communicate
using 3.3V as a high signal. If an infrared obstacle sensor is powered by 5V then it will
likely communicate using 5V as a high signal. These are only general rules, so always
confirm that a device will not communicate using voltages above 3.3V if you plan to
connect it directly to the GPIO pins of your Raspberry Pi.

Lesson 13 – Level Shifting and Infrared Sensors Page 360

Prepared exclusively for [email protected] Transaction: 5095


HARDWARE LEVEL SHIFTING
What if you find a really interesting sensor that was designed to communicate with an
Arduino using 5V, but you want to use it in your Raspberry Pi? For that, you can use
hardware level shifting to convert the signal from 5V down to 3.3V.

A hardware level shifter is an IC or circuit board that converts one voltage level to
another. They are available in many voltage levels, but for these lessons you will be
focusing on 3.3V and 5V. These level shifters can come in a few different varieties:

3.3V to 5V – converts a 3.3V input signal to a 5V output signal

5V to 3.3V – converts a 5V input signal to a 3.3V output signal

3.3V/5V Bidirectional – converts both 3.3V and 5V inputs into the opposite output

Bidirectional level shifters are generally more costly since they can convert between
3.3V and 5V in both directions. These devices are used to enable two-way
communication between two devices, one using 3.3V signals and one using 5V signals.

If two-way communication is not required, then a standard, or unidirectional level shifter


can be used to shift the voltage level of signals coming from a 5V sensor, into signals
that are safe for Raspberry Pi GPIO pins. One model of IC used for this purpose is the
74LVC245.

Lesson 13 – Level Shifting and Infrared Sensors Page 361

Prepared exclusively for [email protected] Transaction: 5095


The 74LVC245 is an 8-channel level shifter that is capable of level shifting 8 signals at
once. By varying its input voltage, this IC has the ability to shift 3.3V inputs to 5V
outputs, or shift 5V inputs to 3.3V outputs. Here is the pinout of the 74LVC245 IC:

The VCC and GND pins will be connected to 3.3V and Ground. A1 through A8 and B1
through B8 are the configurable input or output pins. Applying 3.3V or Ground to the
DIR and OE pins determines whether As or Bs will be used as inputs. We will be using
A as the inputs and B as the outputs. The datasheet for this part states that to enable
communication in this direction, OE must be low or grounded, and DIR must be high or
3.3V. This is the way we will configure the IC in the activities section so that the A side
can be used as 5V inputs, and the B side will be the 3.3V outputs.

A channel is made up of A and B pins sharing the same number. This means that A1
and B1 are one channel, with A being the input, and B being the output. If a 5V high is
seen at input A1 then the IC will make B1 go to 3.3V to represent a high. A2 feeds to
B2, A3 feeds to B3, and so on.

Lesson 13 – Level Shifting and Infrared Sensors Page 362

Prepared exclusively for [email protected] Transaction: 5095


NOTE: Take notice of the location of each input and output channel. THEY DO
NOT LINE UP. On many of these level shifting ICs, the inputs and outputs are
directly opposite, but this is not the case on the 74LVC245. The OE next to VCC
pin forces all of the pins on the B side to shift down by one, causing a slight
misalignment of inputs and outputs. This means that if you're feeding in a 5V
sensor on A6 (pin 7), do not expect the 3.3V output to be present on across from
it on pin 14, as that is actually the output for channel 5. The output for channel 6
is B6 on pin 13. Keeping this small offset in mind when you're wiring inputs and
outputs on this IC will help minimize troubleshooting due to outputs not behaving
as expected.

When shifting signals from 5V down to 3.3V, the IC should be powered by 3.3V. Below
is a diagram of a 74LVC245 being used to connect a 5V sensor to a GPIO pin of the
Raspberry Pi:

The sensor outputs a 5V signal that would damage the Pi if connected directly. The 5V
signal is instead connected to the input of the level shifter. The level shifter takes the 5V
input and converts it to 3.3V. This 3.3V output can then be connected directly to a GPIO
pin of the Raspberry Pi. If the sensor outputs a 5V high, the Pi will see a 3.3V high, and
your program can use this data in any way that you like.

Lesson 13 – Level Shifting and Infrared Sensors Page 363

Prepared exclusively for [email protected] Transaction: 5095


INFRARED OBSTACLE SENSOR
An infrared or IR obstacle sensor is a device that uses infrared waves to determine if an
object or surface is near the sensor. A 5V output line is used to report whether or not an
object is near the sensor. This output line can be level shifted down to 3.3V and
connected to an input on your Raspberry Pi, allowing a program to know if something
has come close to the sensor.

The obstacle sensor included in your kit has an active low output, which means that
when no object is detected the output line will have a high or 5V present. When an
obstacle is detected this output line will go to ground or 0V. Your program must be
coded to understand that high means no object has been detected, and that low
represents that an object is near the sensor.

The sensor contains an infrared emitter and receiver, located next to each other.

The infrared emitter constantly sends out infrared signals, and receiver is constantly
waiting to receive those signals back. If an object is close enough to the sensor, the
emitter signals will bounce off the object and reflect back to the receiver. The sensor
then knows that something is close, and the circuitry on the sensor board will pull the
output pin low, indicating an object is near. If an object is not close enough to reflect the
emitted signals back to the receiver, then the sensor will know that no object is near the
sensor, and the output pin will remain high.

Lesson 13 – Level Shifting and Infrared Sensors Page 364

Prepared exclusively for [email protected] Transaction: 5095


The infrared reflectance of an object is its ability to reflect infrared energy. This
reflectance will determine how far away a sensor will see infrared reflected from that
surface. A wall in your house will have a fairly high level of reflectance, so the sensor
will likely be able to detect it from 4-6 inches away. A furry blanket or pet will have very
low infrared reflectance, which will likely lower the detection distance down to 2 inches
or less.

The detection distance will also be affected by the angle at which the emitted infrared
contacts the surface of the object. An object reflecting perpendicular to the sensor will
offer the highest level of reflectance, allowing for the greatest possible detection
distance. The further an angle is from 90-degrees, the lower the amount of reflectance
that surface will offer, which will lower the detection range:

Lesson 13 – Level Shifting and Infrared Sensors Page 365

Prepared exclusively for [email protected] Transaction: 5095


The type of infrared obstacle sensor in your kit is good for sensing objects that are fairly
close to the sensor. This type of sensor will not tell you how far away an object is, only
that something is near the sensor, making it a good option for keeping a robot from
running into walls or other objects. If measuring the distance to objects or sensing
objects at distances greater than 10 inches are required, there are other, slightly more
complex sensors available for that task.

Lesson 13 – Level Shifting and Infrared Sensors Page 366

Prepared exclusively for [email protected] Transaction: 5095


ALIGNMENT OF OBSTACLE SENSORS
An IR obstacle sensor can be added anywhere that touchless object sensing is needed.
They might be used for obstacle avoidance on a robot, or to sense the presence of your
hand to run part of a program. These sensors will only detect objects in a narrow area in
front of the sensor, so their alignment may need to be adjusted to ensure they cover the
desired area:

Lesson 13 – Level Shifting and Infrared Sensors Page 367

Prepared exclusively for [email protected] Transaction: 5095


In order to get coverage for the tire area, sensors can be mounted at a 45-degree angle.
This will allow for angle detection while still providing some front detection:

A different type of long range distance sensor can then be mounted on the front of the
robot to provide additional obstacle information. This type of sensor will be discussed in
an upcoming lesson.

Lesson 13 – Level Shifting and Infrared Sensors Page 368

Prepared exclusively for [email protected] Transaction: 5095


INFRARED LINE SENSOR
An infrared line sensor can be used to detect the difference between dark colors and
light colors using infrared waves. This can allow a robot to perform an activity referred to
as “line following” where it can follow a black line, often created by using black electrical
tape, on any light-colored surface. The robot can follow the path created by the tape
without any intervention from the user, using only the IR line sensor input and the
program.

The infrared line sensor operates almost exactly like the infrared obstacle sensor. The
only difference is that the emitter/receiver have been relocated from the front to the
bottom of the board. Since the line sensor is always close to the surfaces it’s detecting,
a plastic shroud is installed around and between the emitter and receiver, to limit
emission and detection to just the ends of those components, enhancing accuracy:

The line sensor operates using the infrared properties of different colored objects. Black
objects absorb most of the infrared waves they receive, so they have a low level of
infrared reflectance. White objects reflect almost all the infrared energy they receive, so
they have a high level of infrared reflectance. As colors vary between white and black,
so will their infrared reflectance, lighter colors having higher reflectance, and darker
having lower.

The sensor uses this difference in reflectance to determine if it’s positioned above a
black or white surface. The 5V output pin is then updated to match the color that is
currently being sensed, with low or 0V indicating black and high or 5V indicating white. If
no object is close enough to reflect the infrared, the output will also stay low.

Lesson 13 – Level Shifting and Infrared Sensors Page 369

Prepared exclusively for [email protected] Transaction: 5095


ALIGNMENT OF LINE SENSORS
You could follow a black line using only one IR line sensor. The sensor would need to
be positioned directly above the line, and your robot would have to be programmed to
drive one motor at a time. If white was sensed by the line sensor, the current motor has
driven you off the line, and the other motor needs to drive until you are back on the line.

While this would technically work, your robot


would have to move extremely slowly to avoid
getting away from the line too quickly and
getting lost. Adding a second sensor allows you
to watch both sensors, and if either sees black,
then turn away from the line. This allows you to
sense a larger area, so your robot can follow the
line faster without getting lost:

Using two sensors is the most cost-effective way to make a robot reliably follow a line
but continuing to add sensors can enhance the robot’s ability to follow a line quickly.
Some line sensing packages, like the one pictured below made by SunFounder, have
as many 8 infrared sensors:

This can greatly increase your robot’s ability to know the exact position of the line,
however your program will have to be much more complex to process and act on the
data from all 8 sensors. To keep your programming as simple as possible, you will only
be using two line sensors on the robot in Level D.

Lesson 13 – Level Shifting and Infrared Sensors Page 370

Prepared exclusively for [email protected] Transaction: 5095


NOTE: The pinouts between the IR Obstacle and the IR Line sensors are not the
same. Even though they are both three pins with 5V, GND, and Output, these are
located in a different order on both devices. It's critical to ensure that 5V, GND,
and Output are connected properly prior to applying power to your sensor.
Reversing any of these connections will likely result in damaging the sensor and
possibly your Raspberry Pi.

Lesson 13 – Level Shifting and Infrared Sensors Page 371

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will use the IR Obstacle and IR Line sensors to sense
objects and send that information to the Raspberry Pi using the level shifting IC. You will
also build an RGB LED circuit that will be used to display the status of the sensors,
changing the color of the LED based on input from the sensors.

ACTIVITY #1 – LEVEL SHIFTER AND THE RGB LED

In this activity, you will install and connect the level shifting IC, connect its output to a
GPIO input, and build an RGB LED circuit. You will also build a small program to test
everything out before adding the IR sensors in Activities #2 and #3.

STEP #1
The circuit for this lesson will be made of almost entirely different components from
previous lessons. The first step will be to ensure the Raspberry Pi is powered down so
you can remove any components from previous lessons. Remove all components from
the breadboard except for the wedge and ribbon cable. When completed your
breadboard should look like this:

Lesson 13 – Level Shifting and Infrared Sensors Page 372

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The next step will be to add 3.3V power and ground to the P1 side buses. Connect two
short jumper wires between the points below:

3.3V – connect A1 to P1-3

Ground – connect A5 to N1-5

Lesson 13 – Level Shifting and Infrared Sensors Page 373

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
You will now add the current limiting resistors for the RGB LED. You will be using 1K-
Ohm resistors to reduce the current flowing through the RGB LED, as the LED elements
are already very bright even with the small amount of current allowed by the 1K-Ohm
resistors. Add three 1K-Ohm resistors between these three sets of points:

D24 to F24

D26 to F26

D27 to F27

These resistors must be very close together due to the length of the legs of the RGB
LED. Inspect the legs of each resistor closely to ensure no legs are touching any others
before proceeding to the next step.

Lesson 13 – Level Shifting and Infrared Sensors Page 374

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
You are now ready to add the GPIO output wires that will drive the red, green, and blue
elements of the RGB LED. Connect three short jumper wires between the following
points:

GPIO13 – C17 to C24

GPIO19 – B18 to B26

GPIO26 – A19 to A27

Lesson 13 – Level Shifting and Infrared Sensors Page 375

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
It's now time for the RBG LED and its common cathode ground connection. Remember
that all color elements of your RGB LED share one cathode or ground connection, so it
only takes one ground connection for all three colors of your LED to have a path to
ground. The longest leg of the LED is the common cathode. Insert the common cathode
leg into J25, making sure to rotate the LED so it's anode legs connect to J24, J26, and
J27.

During this step you will also install the ground connection for the RGB LED. Insert a
short jumper wire between breadboard points N1-25 and F25:

Lesson 13 – Level Shifting and Infrared Sensors Page 376

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Next, the 74LVC245 level shifting IC needs to be installed so you can convert 5V
signals down to 3.3V, making them safe for the Raspberry Pi GPIO pins. The level
shifting IC has 10 pins per side so it can be a little difficult to get properly aligned, but
make sure all pins are properly aligned in the breadboard before applying pressure
across the top of the IC to seat all of the pins. The IC's pins are very thin and can easily
be damaged by improper insertion into the breadboard. If you are at all unsure about
this process, please watch the video on the Level B Resource Page prior to attempting
installation.

With pin 1 located at E32, insert the 74LVC245 across the center of the breadboard in
rows 32 through 41.

Install four short jumper wires between the following locations to supply power and
ground to the IC:

3.3V - P1-31 to A32

3.3V – C32 to H32

Ground – N1-41 to A41

Ground – C41 to H33

The IC is now connected to power and ground and it has been configured to accept 5V
inputs on the A side and output 3.3V signals on the B side.

Lesson 13 – Level Shifting and Infrared Sensors Page 377

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Make 5V available on the P2 power rail so it can be used to test the level shifter as well
as power the infrared sensors that will be added in upcoming activities. Connect a short
jumper wire from J1 to P2-3.

Lesson 13 – Level Shifting and Infrared Sensors Page 378

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
You are now ready to connect an input and output channel to the level shifter. The input
of the 74LVC245 will float when not pulled up or down. You will use a 10K-Ohm resistor
between the input and ground to ensure that the 74LVC245 sees a low unless we pull
the input high using a connection to 5V. Make the following connections:

10K-Ohm resistor – N1-33 to B33

Output – long jumper wire from I20 to I34

Input – long jumper wire from C33 to P2-37

The input to the level shifter will now be held high by a direct connection to the 5V
power rail. When disconnected the pull-down resistor will take over and the input will be
grounded.

Lesson 13 – Level Shifting and Infrared Sensors Page 379

Prepared exclusively for [email protected] Transaction: 5095


STEP #9
Double check all of your connections in the last step and power on your Raspberry Pi.
It's now time to write a program that will change the color of the RGB LED based on
whether the level shifter is receiving a high or a low. Open Thonny and create a new
program that consists of the following lines:

import RPi.GPIO as GPIO


import time

rgb = [13,19,26]
red = 13
green = 19
blue = 26

GPIO.setmode(GPIO.BCM)
GPIO.setup(rgb, GPIO.OUT)
GPIO.setup(21, GPIO.IN)

try:
while True:
if GPIO.input(21) == False:
GPIO.output(green, GPIO.LOW)
GPIO.output(red, GPIO.HIGH)
else:
GPIO.output(red, GPIO.LOW)
GPIO.output(green, GPIO.HIGH)
time.sleep(.1)

except KeyboardInterrupt:
GPIO.cleanup()

There are no new concepts above, but here is an overview of everything happening in
the program in case you have any questions:

The RPi.GPIO and time modules are imported. A variable called rgb is created that
contains all output pins so they can be setup using only one line. The variables red,
green, and blue are set equal to the GPIO pin numbers that control each of those
colors in the LED. Color names can then be used throughout the program instead of
using the GPIO pins numbers.

Lesson 13 – Level Shifting and Infrared Sensors Page 380

Prepared exclusively for [email protected] Transaction: 5095


The GPIO pin numbering is set to BCM, each of the pins listed in rgb is designated as
an output, and pin 21 is declared as an input. The try/except format is used to catch
keyboard exceptions and a GPIO.cleanup runs if an exception is encountered.

The try loop uses a while True: loop to keep checking the input over and over. If the
input on GPIO21 is low or False, then the green element will turn off and red will
illuminate. The else condition will trigger if GPIO21 is high or True during a check. In that
case the red element will turn off and the green element will turn on. The time.sleep
command is added to slow the loop checks down to avoid unnecessarily checking of the
GPIO which can lead to unnecessary load, and heat, on the processor of the Raspberry
Pi.

STEP #10
Run the program. The LED will initially be green to indicate that GPIO21 is high or 3.3V
coming from the level shifter IC.

Carefully disconnect the jumper wire at location P2-37 and the input will go low,
indicated by the LED turning red.

Reconnect the jumper wire to P2-37 and the LED will change back to green to indicate
a high is present on the input.

Remove and reinstall the connection at P2-37 a few more times to ensure the program
and circuit are acting as expected. If so, feel free to proceed to the next activity where
you will reconfigure this circuit to get 5V input from an IR Obstacle sensor.

If the circuit or program is not behaving as outlined above, shut down the Pi and
recheck all wiring and the IC's pin 1 orientation. If everything looks good on the circuitry,
then proceed to reboot the Pi and double-check your program. Do not continue to the
next activity until this program and circuit are behaving as expected.

Lesson 13 – Level Shifting and Infrared Sensors Page 381

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – ADDING THE IR OBSTACLE SENSOR
In this activity, you will install the IR Obstacle Sensor and connect its output to the input
of channel 1 on the level shifter. You will then test the existing program using the input
coming from the new sensor.

STEP #1
Save your program and shut down your Raspberry Pi so modifications can be made to
the circuitry on your breadboard.

STEP #2
The circuitry on the IR Obstacle Sensor will be taking care of sending both high and low
signals to the level shifter so the 10K-Ohm pull down resistor on channel 1 is no longer
required. Remove the 10K-Ohm resistor between N1-33 and B33. Relocate one end of
the channel 1 input jumper wire from P2-37 over to B48. This will be used to connect to
the output of the new sensor.

The obstacle sensor will need 5V power and ground connections. For ground, add a
short jumper wire between N1-49 and A49. To supply 5V, connect a long jumper wire
between C50 and P2-43. Your breadboard will now look like this:

Lesson 13 – Level Shifting and Infrared Sensors Page 382

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
You're now ready to install the IR Obstacle Sensor. It's critical that the sensor is
installed the correct direction, as reversing it could cause damage to the sensor
or your Pi.

The pins on the sensor are labeled OUT, GND, and VCC, from left to right. You will
notice that the previous step had you connect rows 48, 49, and 50 so those signals are
available for the sensor

Check the markings directly above the pins on the sensor, and insert the sensor into the
breadboard in column E so that:

OUT is in E48

GND is in E49

VCC is in E50

Lesson 13 – Level Shifting and Infrared Sensors Page 383

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Power up your Pi and run the program you saved during the last activity. Instead of
manually moving a jumper wire to simulate high and low, the IR Obstacle sensor will
now be supplying high and low signals based on whether an obstacle is detected.

• If no obstacle is detected, the sensor output will be low, and the LED will remain
green
• If an obstacle is detected, the sensor output will switch to high, and the LED will
turn red.

Simulate an object by lowering a piece of paper in front of the sensor. At some point
around 3 to 5 cm your object will be detected by the sensor, and the LED will change
state. Your hand may work to trigger the sensor, but the human body is not a very good
reflector of infrared waves, so your hand may need to be even closer to trigger the
sensor.

STEP #5
Power down your Pi in preparation for the next activity where you will add the Infrared
line sensor to the circuit.

Lesson 13 – Level Shifting and Infrared Sensors Page 384

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – ADDING THE IR LINE SENSOR
In this activity, you will add the Infrared Line Sensor to the circuit from Activity #2. You
will also modify the program from Activity #2 to add the ability to sense input from the
line sensor.

STEP #1
With your Pi powered off, add the connections that will be required for the Infrared Line
sensor. Add the following jumper wires to your circuit:

Ground – Short jumper wire from N1-61 to A62

5V – Long jumper wire from C61 to P2-55

Output – Long jumper wire from B60 to B34

Lesson 13 – Level Shifting and Infrared Sensors Page 385

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The line sensor is now ready to be installed. In the last step you made connections to
rows 60, 61, and 62 that will now be used for the sensor. Check the markings directly
above the pins on the sensor, and insert the sensor into the breadboard in column E so
that:

OUT is in E60

VCC is in E61

GND is in E62

This will mean that the LEDs on the line sensor are facing away from you when viewed
with column A closest to you. This is the correct orientation to match the wiring above.

Lesson 13 – Level Shifting and Infrared Sensors Page 386

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The output of the IR Line Sensor is connected to channel 2 of the level shifter, but the
output of channel 2 isn't connected to anything. Connect the output of channel 2 to
GPIO20. Using a long jumper wire, connect I35 to I19:

STEP #4
Power on your Raspberry Pi and open the program from Activity #2. You will make
modifications to this program to add the input from GPIO20 as well as program some
additional LED behavior based on this input.

Setup GPIO20 as an input just below the setup line for GPIO21. The additional line is
highlighted below:

GPIO.setup(21, GPIO.IN)

GPIO.setup(20, GPIO.IN)

try:

Lesson 13 – Level Shifting and Infrared Sensors Page 387

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
You will use the blue LED color to indicate when the line sensor does not see an
infrared reflection. Sensing a reflective object will cause the output from the obstacle
sensor to stay high. If this reflection is not present, like the lack of reflection caused by a
black line, then the sensor output will be low. This low will flow through the level shifter
and into GPIO20.

Add an elif statement below the if statement that will check to see if GPIO20 is low or
False. If so, that block will turn off the green and red elements and turn on the blue
element. Turning off the blue element will also be added to the if block. The code
additions are highlighted below:

try:

while True:

if GPIO.input(21) == False:

GPIO.output(green, GPIO.LOW)

GPIO.output(blue, GPIO.LOW)

GPIO.output(red, GPIO.HIGH)

elif GPIO.input(20) == False:

GPIO.output(green, GPIO.LOW)

GPIO.output(red, GPIO.LOW)

GPIO.output(blue, GPIO.HIGH)

else:

GPIO.output(red, GPIO.LOW)

Lesson 13 – Level Shifting and Infrared Sensors Page 388

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Everything is looking good so far except that the else statement is not yet addressing
the blue LED. If the blue LED gets turned on by the line sensor there is no code to turn
it back off.

Add a line of code to the else statement that will turn off the blue LED. The addition is
highlighted below:

else:

GPIO.output(red, GPIO.LOW)

GPIO.output(blue, GPIO.LOW)

GPIO.output(green, GPIO.HIGH)

time.sleep(.1)

Lesson 13 – Level Shifting and Infrared Sensors Page 389

Prepared exclusively for [email protected] Transaction: 5095


Here is the entire program with the line sensor additions:

import RPi.GPIO as GPIO


import time

rgb = [13,19,26]
red = 13
green = 19
blue = 26

GPIO.setmode(GPIO.BCM)
GPIO.setup(rgb, GPIO.OUT)
GPIO.setup(21, GPIO.IN)
GPIO.setup(20, GPIO.IN)

try:
while True:
if GPIO.input(21) == False:
GPIO.output(green, GPIO.LOW)
GPIO.output(blue, GPIO.LOW)
GPIO.output(red, GPIO.HIGH)
elif GPIO.input(20) == False:
GPIO.output(green, GPIO.LOW)
GPIO.output(red, GPIO.LOW)
GPIO.output(blue, GPIO.HIGH)
else:
GPIO.output(red, GPIO.LOW)
GPIO.output(blue, GPIO.LOW)
GPIO.output(green, GPIO.HIGH)
time.sleep(.1)

except KeyboardInterrupt:
GPIO.cleanup()

Lesson 13 – Level Shifting and Infrared Sensors Page 390

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Run the program. Placing white paper near the line sensor will cause the RGB LED to
turn green. Use a black marker to make a line at least ½ inch wide on the paper. Moving
the paper across in front of the sensor will allow the sensor to indicate where the edges
of the black line are located by turning the RGB LED blue. Verify that your obstacle
sensor is still turning the LED red when an obstacle is in its path.

You can now see how obstacle and line sensors can help a robot, and its program,
interact with the environment around it. Additional obstacle and line sensors can be
added to further enhance this capability.

Leave the circuit assembled for use in Lesson B-14.

Lesson 13 – Level Shifting and Infrared Sensors Page 391

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Are the outputs from a 5V sensor safe to run directly into a GPIO pin?

2. Will an Infrared Obstacle Sensor tell you how far away an object is from the
sensor?

3. Can line sensors be used to make a robot follow a line without human
intervention?

Answers can be found on the next page.

Lesson 13 – Level Shifting and Infrared Sensors Page 392

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Are the outputs from a 5V sensor safe to run directly into a GPIO pin?

ANSWER: No. The Raspberry Pi operates off 3.3V so connecting it to a 5V


sensor would damage the Raspberry Pi. To connect a 5V sensor you would
need to use a hardware level shifter.

2. Will an Infrared Obstacle Sensor tell you how far away an object is from the
sensor?

ANSWER: No. The obstacle sensor will only tell you once an object appears in
it field of range.

3. Can line sensors be used to make a robot follow a line without human
intervention?

ANSWER: The input from line sensors can allow a program make a robot follow
a line without human intervention.

Lesson 13 – Level Shifting and Infrared Sensors Page 393

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned how to safely connect sensors with a 5V output to the
Raspberry Pi using a hardware level shifter IC. You also learned to work with two
infrared sensors: An obstacle sensor and a line sensor.

In the next lesson, you will learn to work with an ultrasonic range sensor to detect
objects and measure the distance to an object.

Lesson 13 – Level Shifting and Infrared Sensors Page 394

Prepared exclusively for [email protected] Transaction: 5095


LESSON 14

ULTRASONIC RANGE SENSING


AND NUMPY

OBJECTIVE
In this lesson you will work with ultrasonic range finding sensors and learn to
incorporate them into your programs.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Assembled Circuit from Lesson B-13
• 1 x SR04 Ultrasonic Range Finding Sensor
• 1 x Long Jumper Wire

REVIEW CONCEPTS

If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Level Shifting (Lesson B-13)

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 395

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson you will learn how ultrasonic range sensors work as well as how to
incorporate the range information they provide into your programs.

ULTRASONIC RANGEFINDERS
An ultrasonic rangefinder is made up of an ultrasonic emitter, and ultrasonic receiver,
and a small amount of onboard circuitry that is used to drive the emitter and receiver.
The SR04 rangefinder in your kit is a 5 Volt device meaning it requires 5 volts to operate
and its output will be a 5V signal.

Just like the infrared sensors from the previous lesson, the output of this sensor is not
safe to connect directly to your Raspberry Pi. A level shifter must be used on the output
of the SR04 to make it safe for your GPIO pins. The input or trigger pin on the SR04 can
be safely operated by 3.3V, so this pin is safe to connect directly to a GPIO pin.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 396

Prepared exclusively for [email protected] Transaction: 5095


ULTRASONIC SIGNALS
Have you ever been in a large room or tunnel and heard an echo of your voice? This is
due to the sound you created, bouncing off one or more surfaces, and then back to you.
This same principle of reflection is used for ultrasonic range sensing. Signals are sent
out, reflected off an object, and sensed by a receiver.

Sound waves that are audible to the human ear generally fall in the range of 20Hz to
20kHz. Ultrasonic signals are above the 20kHz audible range meaning that, while it's
not possible for us to hear signals in this range, they can be generated and received by
electronic equipment. Most of the signals that you interact with on a daily basis like
mobile phones, garage door openers, and microwave ovens, operate in the ultrasonic
range or above 20kHz.

FINDING RANGE WITH AN ULTRASONIC SENSOR


The echoes in a room seem to sound fairly random because they are composed of
sounds bouncing off multiple surfaces, due to your voice being sent out in a relatively
wide pattern in the room. Ultrasonic rangefinders operate by focusing their emission or
"trigger" signal into a relatively narrow region of around 30 degrees.

These signals travel at a very specific speed in air, which is around 343 meters per
second, or 34,300 centimeters per second. This means that if you know the exact
amount of time that a signal took to fly to, bounce off, and fly back from an object, you
can calculate its distance from the Ultrasonic sensor

The rangefinder is sent a trigger signal from your program that tells it to send a very
short ultrasonic pulse of 40kHz out of its emitter. Your program will then start a timer.
When the rangefinder receives a 40kHz signal reflected back by an object, it will switch
its echo pin high to indicate this activity. Your program will use the state of the echo pin
to determine what the counter should do:

Echo pin low – no signal has been received so keep resetting the starting counter value.

Echo pin high – a signal has been detected so keep updating the ending counter value.

Keep in mind that we're not talking about a lot of time elapsing here. For a distance of
around 200 centimeters you're only looking at around 0.01136 seconds of flight time.
This means that the signal was emitted, flew to a surface, was reflected, and flew back,
all in 0.01136 seconds.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 397

Prepared exclusively for [email protected] Transaction: 5095


Using the example time from above, and the expected speed of 34,300 centimeters per
second, you can determine how many centimeters the signal traveled by multiplying
these values:

flight time X 34300 = distance flown in centimeters

0.01136 X 34300 = 389.65 cm

You now know the total distance to the object and back, but it would be a little more
helpful to know just the distance to the object. You can determine this by cutting this
value in half:

You have now determined that the object that the ultrasonic rangefinder detected was
194.8 centimeters away from the sensor. Here are some additional flight times with
distance calculations:

(0.00160 X 34300) / 2 = 27.4 cm

(0.00287 X 34300) / 2 = 49.2 cm

(0.00723 X 34300) / 2 = 124.0 cm

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 398

Prepared exclusively for [email protected] Transaction: 5095


USING DISTANCE INFORMATION IN PROGRAMS
Instead of the on/off type readings that come from an IR obstacle sensor, an ultrasonic
rangefinder can supply a robot with much greater intelligence regarding its
surroundings. With an IR obstacle sensor, you can have a program flow like this:

if nothing is detected:

keep driving

elif something is detected:

back up and turn

This is fairly limiting due to information provided by an IR obstacle sensor, but an


ultrasonic rangefinder you can have a much more intelligent program:

if distance to wall is greater than 100 cm:


keep driving at full speed

elif distance to wall is less than 50 cm:

slow to 50% speed

elif distance to wall is less than 30 cm:

back up and turn

elif distance to wall is less than 20 cm:

stop before you hit something

Using the variable output from a rangefinder as input, the additional controls in a
program like this can allow your robot to be much more successful at avoiding
obstacles.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 399

Prepared exclusively for [email protected] Transaction: 5095


ERROR HANDLING
Every now and then an odd echo from the rangefinder might cause an incorrect
distance. This can be caused by an echo bouncing off more than one object at different
distances. This might be displayed as distances like:

61.3
61.2
19.8
61.3
61.3

The distance to the object is around 61.3 cm but the sensor received an odd reflection
that accounted for the 19.8 cm reading. This wouldn't normally be too big of a problem,
but your code may tell the robot to stop anytime the distance is less than 20 cm. This
one bad reading could cause your robot to stop even though it's not actually near an
obstacle.

There are many ways to handle these types of distance anomalies in your program.
One way is to calculate the average of the last few ranges to smooth out the distance
data that the robot uses for it's motion decisions. In the next section you will learn to use
a module called NumPy for these calculations.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 400

Prepared exclusively for [email protected] Transaction: 5095


NUMPY
NumPy is a Python software module installed on the Raspberry Pi that can help with
complex calculations in your Python programs. NumPy has a wide array of advanced
mathematical functions available, and it has been optimized to perform these
calculations very quickly.

One of the most simple, but very useful, functions built into NumPy is the mean or
averaging function. Say that you want to average out five distances from the
rangefinder. Normally you would have to add up all five ranges and divide by five to
obtain the average:

ranges = [61.3, 61.2, 19.8, 61.3, 61.3]

total = ranges[0] + ranges[1] + ranges[2] + ranges[3] + ranges[4]

average = total / 5

This will take each value in the ranges list and add them together, setting that sum
equal to a variable named total. A variable named average is created and set equal
to total divided by 5. While this does work to obtain an average, there is a much
simpler way using the mean function of NumPy:

numpy.mean(list_to_average)

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 401

Prepared exclusively for [email protected] Transaction: 5095


The numpy.mean command followed by the name of a list will take the mean or average
of any values present in the list. Since NumPy is an additional module, it has to be
imported at the beginning of the program. Here is the code from above, rewritten to use
the numpy.mean command:

import numpy
ranges = [61.3, 61.2, 19.8, 61.3, 61.3]

average = numpy.mean(ranges)

This method works and does not rely on the ranges list having exactly five values. The
ranges list could have 25 values and the numpy.mean would average all values present,
without changing any code. This is not true for the manual addition/division method in
the first example.

Using this averaging feature of NumPy can allow you to obtain more reliable distances
from your rangefinding sensor.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 402

Prepared exclusively for [email protected] Transaction: 5095


INTEGERS AND FLOATS
You have already learned about how strings and integers differ when performing certain
types of print commands. It's now time to learn about another type of numeric values
called a floating-point number, or 'float'.

An integer can only represent whole numbers that are positive, negative or zero. Some
examples of integers would be 4, 61, -17, 0, 142, -1653. Integers are good, but they
start to get limited when performing mathematical operations like division.

The result of dividing 7 by 2 is 3.5 which is no longer a whole number. If you try to print
the integer value of 7/2, you will print only the whole number, which is 3 and is not very
accurate. The arrows below are used to show the output of running the code:

x = 7 / 2

print(int(x))

>>> 3

This code will print that 7 divided by 2 is 3 which is not exactly correct. By specifying the
float value of x you can accurately print that 7 divided by 2 is 3.5:

x = 7 / 2
print(float(x))

>>> 3.5

Floats can be used to represent any number of decimal places, but they will always
include at least one decimal place. Dividing 6 by 2 will result in a float value of 3.0:

x = 6 / 2

print(float(x))

>>> 3.0

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 403

Prepared exclusively for [email protected] Transaction: 5095


Floating point values start to come in handy when you're taking distance timing values
like 0.00160, multiplying them by 34300, and dividing them by 2. There are a lot of
decimal points involved in these calculations, so every resulting value must be treated
as a floating point value to avoid losing accuracy.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 404

Prepared exclusively for [email protected] Transaction: 5095


PRINTING WITH % NOTATION
As you have already seen, there are many ways to print values in Python, and here is
yet another. Using % notation allows you to substitute variables into a print statement.
The simplest way to use this would be:

print('%s is awesome' % 'Your name')

The %s specifies that a string-type variable will be dropped into the statement, and the
string that will replace %s is specified as an argument after the %, which is 'Your name'.
You can also load the name from elsewhere within the program by specifying a variable
name as the argument instead:

print('%s is awesome' % your_name)

In this case, %s specifies that the string value of your_name will be printed into this
statement.

An integer can be specified by using %i:

print('Being %i years old is awesome' % your_age)

Be careful with integers and strings in this case. An integer value of 14 for your_age will
allow the statement to print correctly. A string value of '14' (notice the quotation marks
turning it into a string value) will cause this print statement to generate an error,
because it's looking for an integer value and it found a string instead.

One really nice thing about % notation when working with %f or floats, is the ability to
specify how many decimal locations you want to print. When dealing with times related
to distance readings, there are a lot of decimal points involved. Without specifying a limit
on the decimal places when printing, you would end up with distances like:

22.597869
23.601007
23.592830
23.599645
23.586015

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 405

Prepared exclusively for [email protected] Transaction: 5095


These are nice, and very accurate, but they are a little difficult to watch scroll by,
especially at a rate of three or four per second. By specifying how many values you
want after the decimal point, you can control what the output looks like, directly in the
print statement. Here is a print statement that will print the float value of distance,
while limiting it to only one decimal place:

print('%.1f' % distance)

This will result in output distances like the following:

22.6
23.6
23.6
23.6
23.6

These shortened values will make it much easier to detect changes, even when
watching three or four messages scroll by per second.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 406

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will remove the IR obstacle and line sensors from the
circuit you built in Lesson B-13 and replace them with the SR04 Ultrasonic Range
Sensor. You will then write a program to gets ranges using the sensor and add
averaging to increase range sensing accuracy. You will also incorporate the RGB LED
to visually indicate the distance between your sensor and an object.

ACTIVITY #1 – ADDING THE ULTRASONIC RANGE SENSOR


Using the circuit from Lesson B-13 as a starting point, you will remove the IR obstacle
and line sensors and add the Ultrasonic Range Sensor.

STEP #1
The first step will be to remove the IR obstacle and line sensors from the circuit you built
in Lesson B-13. You will leave the 5V to 3.3V level shifter in place as it will be used to
level-shift the Echo output line from the Ultrasonic Sensor.

Shut down the Pi and disconnect power before proceeding.

Remove the IR line sensor and its associated wiring. Also remove the jumper wire
connecting CH1 from the level shifter to GPIO20 as CH1 will not be used in this lesson.
The circuit should look like this when complete:

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 407

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The next step will be to remove the IR obstacle sensor from the circuit in order to make
room for the ultrasonic sensor.

Remove the IR obstacle sensor but leave its jumper wires in place as they will be
relocated in the next step to connect to the ultrasonic sensor. When completed, your
circuit will look like this:

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 408

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Its now time to relocate the jumper wires that will connect to the ultrasonic sensor. Move
three jumper wires to their new locations, while also adding one long jumper wire for the
trigger line:

Relocate – 5V – Move connection from C50 to F53

Relocate – Output (Echo)– Move connection from B48 to F51

Relocate – Ground – Move connection from A49 to F50

Add long jumper wire – Input (Trigger) – Add jumper between I19 and F52

The circuit will look like this after your relocations and addition:

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 409

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
The last step is to install the ultrasonic sensor into column J, between rows 50 and 53.
The emitter/receiver portion of the sensor must point away from the breadboard to align
with the wiring completed in the last step. Ensure the signal labels above the connector
on the sensor match this list before proceeding:

J50 – Ground

J51 – Echo

J52 – Trig

J53 – Vcc

If your signals match up, then power up your Raspberry Pi and proceed to the next
lesson where you will create a program that will read ranges from the sensor.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 410

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – CREATING A PROGRAM TO READ RANGES
In this activity you will create a program that will use the ultrasonic sensor to read
ranges, and display those ranges in the console. You will add more functionality to this
program in Activities #3 and #4.

STEP #1
The first step will be to open Thonny and create a new program. Save this program to
the Desktop as ultrasonic.py so it can be re-used in upcoming activities.

STEP #2
You will now import the time and RPi.GPIO modules, and create two variables. Create a
variable called trigger that is equal to 20 and another variable called echo that is
equal to 21:

import time
import RPi.GPIO as GPIO

trigger = 20
echo = 21
Pins GPIO pins 20 and 21 can now be referred to as trigger and echo. This will make it
much easier to follow the program flow as you're writing code.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 411

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
You will now setup the GPIO pin mode and configure the echo and trigger pins as
inputs and outputs. Set the pin mode to BCM, configure trigger as an output, and
configure echo as an input:

trigger = 20
echo = 21

GPIO.setmode(GPIO.BCM)
GPIO.setup(trigger,GPIO.OUT)
GPIO.setup(echo,GPIO.IN)

STEP #4
The next part of the program will be a function called range_check() that will be called
any time your program wants the range from the sensor. Here is the basic overview of
what range_check() will do:

1. Set trigger pin high


2. Wait 0.00001 seconds
3. Set trigger pin low
4. While the echo pin is low, continually update the value of start_timer
5. Once the echo pin is high, continually update the value of stop_timer
6. Compute elapsed_time by subtracting start-timer from stop_timer
7. Use math on elapsed time to determine distance
8. Return the value of distance back to the main program

This may look like a lot, but most of these items are only one line of code. The first step
in creating the function will be to define it using the name range_check():

GPIO.setup(echo,GPIO.IN)

def range_check():

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 412

Prepared exclusively for [email protected] Transaction: 5095


Next, indented below the definition line, push the trigger line high, wait 0.00001
seconds, pull the trigger line back low, and capture the current time into a variable
called start_timer using the time.time() command:

def range_check():
GPIO.output(trigger, True)
time.sleep(0.00001)
GPIO.output(trigger, False)
start_timer = time.time()

That block of code has sent out the trigger pulse and you will now have to wait for the
echo to return. You will use a while loop to continually reset the value of start_timer
while you’re waiting for the reflected ultrasonic signals. A second while loop will be
used to update the value of stop_timer while the reflected signals are being received:

def range_check():
GPIO.output(trigger, True)
time.sleep(0.00001)
GPIO.output(trigger, False)

while GPIO.input(echo) == False:


start_timer = time.time()

while GPIO.input(echo) == True:


stop_timer = time.time()

Now that the start and stop times have been captured, the only thing left to complete
this function is to calculate elapsed_time by subtracting start_time from stop_time,
calculating the distance in centimeters using your formula of (time * 34300)/2, and
then return that calculated distance back to the main program. Add this block below the
while loops:

while GPIO.input(echo) == True:


stop_timer = time.time()

elapsed_time = stop_timer‐start_timer
distance = (elapsed_time * 34300)/2
return distance

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 413

Prepared exclusively for [email protected] Transaction: 5095


Here is the complete function so you can confirm your code matches before continuing
to the next step:

def range_check():
GPIO.output(trigger, True)
time.sleep(0.00001)
GPIO.output(trigger, False)

while GPIO.input(echo) == False:


start_timer = time.time()

while GPIO.input(echo) == True:


stop_timer = time.time()

elapsed_time = stop_timer‐start_timer
distance = (elapsed_time * 34300)/2
return distance

STEP #5
Now that the range measurement and calculation function is complete, you can build
the rest of the main program. You will use a try/except format for the main program so
you can clean up the GPIO pins in case of a keyboard interrupt or pressing of the stop
button in Thonny.

Inside the try: loop you will use a while True: loop to keep checking range until the
program ends. A variable called distance will be set equal to the value returned from
running the function range_check(). Next, you will print the distance value trimmed
down to 1 decimal place using % notation with the print command. Last, you will add a
.25 second delay to slow down the main loop so it only runs 4 times per second:

distance = (elapsed_time * 34300)/2


return distance

try:
while True:
distance = range_check()
print('%.1f' % distance)
time.sleep(.25)

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 414

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
The last step will be to add an except case to handle cleanup of the GPIO pins when
the program exits. Add the following to the very end of the program

print('%.1f' % distance)
time.sleep(.25)

except KeyboardInterrupt:
GPIO.cleanup()

You now have a complete program for displaying the ranges from an ultrasonic sensor.
Here is the fully assembled program:

import time
import RPi.GPIO as GPIO

trigger = 20
echo = 21

GPIO.setmode(GPIO.BCM)
GPIO.setup(trigger,GPIO.OUT)
GPIO.setup(echo,GPIO.IN)

def range_check():
GPIO.output(trigger, True)
time.sleep(0.00001)
GPIO.output(trigger, False)

while GPIO.input(echo) == False:


start_timer = time.time()

while GPIO.input(echo) == True:


stop_timer = time.time()

elapsed_time = stop_timer‐start_timer
distance = (elapsed_time * 34300)/2
return distance

try:
while True:
distance = range_check()
print('%.1f' % distance)
time.sleep(.25)

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 415

Prepared exclusively for [email protected] Transaction: 5095


time.sleep(.25)

except KeyboardInterrupt:
GPIO.cleanup()

STEP #7
Run the program. Move different objects in front of the sensor and watch the distance
reading change as they move. If your program generates errors, double check your
wiring to the sensor and verify your program matches the example above exactly,
including indentation. This is a fairly long program and any variances or typos will cause
it not to function properly.

Do not proceed to the next activity until your circuit and program are reliably displaying
ranges.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 416

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – AVERAGING THE RANGE VALUES USING NUMPY
You may have noticed a seemingly random range measurement reported once in a
while when using the program from the last lesson. This can happen due to reflections
from multiple objects and the fairly high rate at which you are taking range
measurements. In this activity you will modify the program from the last activity to read
three ranges, average those into one reading, and print that reading to the console.

STEP #1
For the averaging in this program, you will use NumPy. Using the program from the last
activity as a starting point, import numpy just below the GPIO import. The additional line
has been highlighted below:

import time
import RPi.GPIO as GPIO
import numpy

trigger = 20
echo = 21

STEP #2
You will need a list to store the range distances before they are averaged. Create an
empty list called ranges below the echo and trigger pin assignments:

trigger = 20
echo = 21
ranges = []

GPIO.setmode(GPIO.BCM)

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 417

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
You already have a function that is taking care of calculating ranges, but you will need
another function to take and store multiple ranges and average them into one result.
You will create a new function named average() that will:

1. Call the range_check() function three times and append result to the ranges list
2. Delay 0.06 seconds between loops to allow sensor to settle between checks
3. Set distance equal to the mean (average) of the values in the ranges list
4. Clear all values from ranges list so no values are present during next run
5. Return the value of distance back to the main program

Add this block of code just below the end of the range_check() function:

distance = (elapsed_time * 34300)/2


return distance

def average():
for i in range(0,3):
ranges.append(range_check())
time.sleep(0.06)
distance = numpy.mean(ranges)
ranges.clear()
return distance

try:

Ensure the indentation matches above. You only want the for i in range loop to check
the range three times and add those to ranges. The average, clear, and return lines
should only run once, each time the average() function is called.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 418

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
The last step will be to modify the main program, which is currently calling the
range_check() function directly. Instead, you want to have the main program call the
average() function which will get three ranges, average them, and return the average
of the three. Change the name of the function being called in the main program from
range_check() to average():

try:
while True:
distance = average()
print('%.1f' % distance)
time.sleep(.25)

STEP #5
Run the new program to verify that it prints range information to the console as
expected. Here is the entire program for reference:

import time
import RPi.GPIO as GPIO
import numpy

trigger = 20
echo = 21
ranges = []

GPIO.setmode(GPIO.BCM)
GPIO.setup(trigger,GPIO.OUT)
GPIO.setup(echo,GPIO.IN)

def range_check():
GPIO.output(trigger, True)
time.sleep(0.00001)
GPIO.output(trigger, False)

while GPIO.input(echo) == False:


start_timer = time.time()

while GPIO.input(echo) == True:


stop_timer = time.time()

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 419

Prepared exclusively for [email protected] Transaction: 5095


elapsed_time = stop_timer-start_timer
distance = (elapsed_time * 34300)/2
return distance

def average():
for i in range(0,3):
ranges.append(range_check())
time.sleep(0.06)
distance = numpy.mean(ranges)
ranges.clear()
return distance

try:
while True:
distance = average()
print('%.1f' % distance)
time.sleep(.25)

except KeyboardInterrupt:
GPIO.cleanup()
Do not continue to the next activity until this program is working properly. If it is not,
verify all highlighted additions and changes were made correctly.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 420

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #4 – ADDING THE RGB LED AS A DISTANCE INDICATOR
In this lesson you will use the three colors of the RGB LED to indicate the distance of an
object from the ultrasonic sensor:

Blue - greater than 25cm

Green – between 20cm and 25cm

Red – less than 20cm

STEP #1
To use the three GPIO pins connected to the RGB LED, they will have to be set up as
outputs at the beginning of the program. In the interest of shortening the code and
making it as easy to read as possible, create a list called rgb that contains a list of the
RGB pin numbers, as well as set each color name equal to its GPIO pin number. This
will make them easier to keep track of later in the program.

Add these elements just below the module imports at the top of the program:

import numpy

rgb = [13,19,26]
red = 13
green = 19
blue = 26
trigger = 20

Now all three pins can be configured as outputs using the list called rgb, and the each
RGB element can be referred to by its color instead of having to remember its pin
number.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 421

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Before you can use the RGB LED, its GPIO pins will need to be configured as outputs.
This can be done with one line of code using the rgb list you created in the last step.
Add a GPIO.setup line to configure the rgb list as outputs:

GPIO.setmode(GPIO.BCM)
GPIO.setup(trigger,GPIO.OUT)
GPIO.setup(echo,GPIO.IN)
GPIO.setup(rgb,GPIO.OUT)

def range_check():

STEP #3
Next, you will add an if/elif/else block to the main program that will turn the RGB LED
elements on (1) or off (0) based on the current distance value. Just below the print
statement in the main program block, add an if statement that checks to see if
distance is less than 20, and if so, turn the red element on and the others off:

while True:
distance = average()
print('%.1f' % distance)
if distance < 20:
GPIO.output(red, GPIO.HIGH)
GPIO.output(green, GPIO.LOW)
GPIO.output(blue, GPIO.LOW)
time.sleep(.25)

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 422

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
It's time for something to make the green LED turn on when the distance is between 20
and 25. Add an elif statement that checks for distance to be greater than or equal to
20 and less than 25, and if so, turn the green element on and the others off:

if distance < 20:


GPIO.output(red, GPIO.HIGH)
GPIO.output(green, GPIO.LOW)
GPIO.output(blue, GPIO.LOW)
elif 20 <= distance < 25:
GPIO.output(red, GPIO.LOW)
GPIO.output(green, GPIO.HIGH)
GPIO.output(blue, GPIO.LOW)
time.sleep(.25)
Remember to watch the indentation as you add these new cases.

STEP #5
The final step will be to add an else statement that catches any distance greater than
25cm. Since this is an else statement it does not have a condition attached as it runs
automatically in the case that neither of the previous if or elif statements evaluated
as True. Add an else statement that turns the blue element on and the others off:

elif 20 <= distance < 25:


GPIO.output(red, GPIO.LOW)
GPIO.output(green, GPIO.HIGH)
GPIO.output(blue, GPIO.LOW)
else:
GPIO.output(red, GPIO.LOW)
GPIO.output(green, GPIO,LOW)
GPIO.output(blue, GPIO.HIGH)
time.sleep(.25)

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 423

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Run the program. The LED will now indicate blue when the distance is 25cm or greater,
green when the distance is between 20 and 25cm, and red when the distance is below
20cm.

This program will be reused in Lesson B-18, so ensure the updated program has been
saved to the Desktop as ultrasonic.py.

Also, leave the circuit assembled for use in Lesson B-15.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 424

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Can the value 6.75 be stored as an integer or a float?

2. Does NumPy require a specific number of values in a list, or can you use it on
any list of numeric values?

3. Does the ultrasonic sensor supply range information directly, or does it simply
send triggers and echoes, and your program must calculate the time and
distance from the signals received?

Answers can be found on the next page.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 425

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Is the value 6.75 a floating point value or an integer?

ANSWER: 6.75 is a floating point value, so it must be stored as a float. If 6.75


were stored as an integer, the value after the decimal point would be lost,
leaving only the value 6.

2. Does NumPy require a specific number of values in a list, or can you use it on
quantity of numeric values?

ANSWER: The NumPy module can use any quantity of numeric values.

3. Does the ultrasonic sensor supply range information directly, or does it simply
return echoes, and your program must calculate the time and distance from the
signals received?

ANSWER: The Ultrasonic Range Sensor will only return echoes. Your code
must calculate the time and distance based on the signals that are received.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 426

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to work with an Ultrasonic Range Sensor and how to write the
code needed to calculate the distance to objects.

In the next lesson, you will learn a new communication method called I2C and how to
integrate a temperature sensor into your programs.

Lesson 14 – Ultrasonic Range Sensing and NumPy Page 427

Prepared exclusively for [email protected] Transaction: 5095


LESSON 15

I2C AND TEMPERATURE SENSING

OBJECTIVE
In this lesson you will learn about a communication method called I2C and how to use a
temperature sensor.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, mouse, and the internet


• Assembled Circuit from Lesson B-14
• 1 x BMP280 Temperature Sensor
• 2 x Long Jumper Wires
• 2 x Short Jumper Wires

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Using GitHub (Lesson B-8)

Lesson 15 – I2C and Temperature Sensing Page 428

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson you will learn about a communication method called I2C and how to use a
sensor to monitor temperature. You will also learn how to integrate these
measurements into a program that will take different actions based on the values of
these measurements.

I2C COMMUNICATION

Inter-Integrated Communication, or IIC involves the communication between multiple


devices that are interconnected using a bus. The IIC name is often referred to as I²C,
pronounced I-squared-C, or I2C, pronounced I-two-C. These are all names for the same
type of communication system.

In I2C, many devices can be connected on one bus by using different addresses to
identify each sensor:

I2C devices generally consist of a connection to power, ground, a clock signal, and a
data bus. The clock signal is generated by the Raspberry Pi and is used to synchronize
communications between the Pi and all I2C connected devices. A single data bus line is
used to convey data between all connected devices.

Lesson 15 – I2C and Temperature Sensing Page 429

Prepared exclusively for [email protected] Transaction: 5095


I2C ADDRESSING
Since all of the device are using the same communication bus, addresses like 0x76 or
0x77are used to identify each device that is attached to the bus. This way the master, or
Raspberry Pi in this case, can determine which device is sending the information. The
information coming from two temperatures at addresses 0x76 and 0x77 might look
something like this:

0x76,39.2
0x77,6.8
0x77,6.9
0x76,39.1
0x77,6.8

Since the sensors share the same data bus, they must identify themselves by attaching
their address to the beginning of any message they send out. This way the Raspberry
Pi knows that any data beginning with 0x76 is coming from the refrigerator and any data
beginning with 0x77 is coming from the freezer.

Your program will need to be programmed to identify each of these I2C addresses. This
may take the form of something like the following near the top of the program:

fridge = 0x76
freezer = 0x77

Variables like this can be used to set the I2C address for sensors throughout your
program. The variables fridge and freezer can then be referenced without needing to
remember the exact I2C address of each device. Your program can then display or
make decisions based on the values coming from each of these sensors.

Some devices do not allow their I2C address to be modified. This is the case for the
BMP280 Temperature/Pressure sensor in your Level B kit. The address for this sensor
is 0x76 and cannot be changed. This means that only one of these devices can be
connected to your I2C bus. If more than one BMP280 sensor was connected, they
would both use 0X76 to communicate, and you would not be able to determine which
sensor a temperature reading came from.

Lesson 15 – I2C and Temperature Sensing Page 430

Prepared exclusively for [email protected] Transaction: 5095


Some I2C devices allow the selection of the address based on the position of a
soldered connection on the circuit board. In the case of this 128x64 OLED display, the
I2C address is determined by the position of a zero-ohm resistor that is installed during
manufacturing.

The default address for this


device is 128x64 OLED
Display is 0x78:

If you wanted to use two of these devices on the same I2C bus, you would need to de-
solder the zero-ohm jumper that is connected between the center and the 0x78
connection, and re-solder it between the center and the 0x7A position. You would then
be able to connect both displays to your I2C bus, accessing one using the 0x78 address
and the other using the 0x7A address.

Lesson 15 – I2C and Temperature Sensing Page 431

Prepared exclusively for [email protected] Transaction: 5095


ENABLING I2C ON THE RASPBERRY PI
Just like the SPI bus that you enabled in order to communicate with the MCP3008 back
in Lesson B-9, I2C communication is available, but is not enabled by default. It can be
enabled in the same area as SPI, by navigating to Menu > Preferences > Raspberry
Pi Configuration and clicking on the Interfaces tab:

Select the Enabled radio button next to I2C, click OK, and then reboot the Raspberry Pi.
Once this process is completed, GPIO2 (pin 3) and GPIO3 (pin 5) will be devoted to I2C
communication, and they can no longer be used as standard GPIO pins. Pin 3 will be
configured as the SDA or Serial Data line and pin 5 will be configured as SCL or the
Serial Clock line. You will notice these SDA and SCL designations match the markings
on the wedge for pins 3 and 5:

Lesson 15 – I2C and Temperature Sensing Page 432

Prepared exclusively for [email protected] Transaction: 5095


LIST OF I2C DEVICES
The two-sensor example seems very simplistic, but I2C communication can get a lot
more complicated when you have many devices attached to the I2C bus. There are
temperature sensors, analog-to-digital convertors, LED display screens, and many more
devices that can be attached to the I2C bus of your Raspberry Pi. Adafruit has put
together a really great list of available I2C devices, and their default addresses. The list
can be found here:

https://learn.adafruit.com/i2c-addresses/the-list

This list does not contain every single I2C device that's available, but if you're looking
for an I2C device to perform a specific function on your Raspberry Pi, then this list is a
good place to start.

DETECTING ATTACHED I2C DEVICES


Before troubleshooting a program that will communicate with I2C devices, it's always
good to verify that your device is connected properly. This will let you know that:

• I2C is enabled on the Raspberry Pi


• The sensor is properly connected to power and ground
• The sensor is properly connected to the I2C data and clock lines

You can use a command called i2cdetect at the command line to display a list of all
connected I2C devices. The i2cdetect command needs a couple of additional
arguments specified when you run the command:

i2cdetect -y 1

The -y argument will automatically answer yes to the Continue? [Y,n] question that
is normally presented when running this command. The 1 specifies which I2C bus
inside the Raspberry Pi you would like to probe. The Raspberry Pi only has a single I2C
bus that is named 1, but this argument is still required when running this command. The
output of this command will look like this when a device is connected to the I2C bus and
is communicating on address 0x76:

Lesson 15 – I2C and Temperature Sensing Page 433

Prepared exclusively for [email protected] Transaction: 5095


No other devices are present, but the Raspberry Pi is detecting communication from 76
or 0x76. This means I2C is enabled, your sensor is properly connected, and the sensor
is not damaged or dead. You can now be sure that this sensor is communicating
properly with the Raspberry Pi and all that's left is to create a program that will interpret
the information that it's sending.

Lesson 15 – I2C and Temperature Sensing Page 434

Prepared exclusively for [email protected] Transaction: 5095


THE BMP280 TEMPERATURE SENSOR
The temperature sensor included in your kit is the 6-pin version of the BMP280
temperature and pressure sensor. This device can output high accuracy temperature
and barometric pressure data via SPI or I2C. This device runs from a 3.3V supply which
makes it ideal for connecting to the Raspberry Pi.

The BMP280 can output temperature and barometric pressure data, but not humidity.
Another IC in this same family called the BME280 can output temperature, barometric
pressure, and humidity. When researching information about the BMP280 you may see
complaints that the humidity output is not working, but this is by design. A BME280 must
be used if you would like to use humidity measurements for a project.

Communicating with these devices is very complex, so a driver file is used to simplify
communication. These two ICs are very similar in design, so similar in fact that the
same driver file can be used to communicate with either the BME or BMP versions of
this IC. Matt Hawkins from Raspberrypi-spy.co.uk built a very nice module that
combines everything you need to communicate with these sensors into a single file.

The bme280.py module is available in the 42 Electronics GitHub repository called


level_b. You will download this file in Activity #1 of this lesson. No installation of the
module is required, just ensure that bme280.py is located in the same directory as your
program, and import the module named bme280 at the beginning of your program.

Lesson 15 – I2C and Temperature Sensing Page 435

Prepared exclusively for [email protected] Transaction: 5095


BMP280 WIRING
The BMP280 has six pins but only four are used when operating in I2C mode. Here is a
pinout of the BMP280 that is included with your kit:

The 3.3V and GND lines can be connected directly to the power rails of the Raspberry
Pi. SCL connects to GPIO pin 5 and SDA connects to GPIO pin 3. As soon as these
connections are made this device will begin sending temperature and barometric
pressure measurements out via I2C.

BMP280 COMMANDS
Since the bme280.py module was written for the BME280, its commands are centered
around this version of the sensor, but it will also communicate well with the BMP280.
The only difference is that when the BMP280 is asked for a humidity reading, it returns
0 instead of the actual humidity reading that a BME280 would return.

The main command to obtain temperature from a BMP280 using this module is:

temperature,pressure,humidity = bme280.readBME280All()

This command will access the module named bme280.py and execute the function
named readBME280All(). This function will return three values back to your program.
The first value returned will be assigned to the variable temperature, the second value
returned will be assigned to pressure, and the third will be assigned to humidity.

Different variable names could be used in your program to represent these values:

Lesson 15 – I2C and Temperature Sensing Page 436

Prepared exclusively for [email protected] Transaction: 5095


temp,press,humid = bme280.readBME280All()

or

t,p,h = bme280.readBME280All()

The order that values are returned by the module cannot be changed. It will always
return three values in the order of temperature first, then pressure, and humidity last.

You can then use the values stored in your variables to run calculations, print, or
anything else you would like to do with the values now that they're in your program.

TEMPERATURE CONVERSION
The temperature values returned by the BMP280 will be in Centigrade or C format. If
you would like to convert the value to Fahrenheit once it’s in your program, you can use
the following calculation:

temperature = (temperature * 9/5) + 32

This will take the current Centigrade value stored in temperature, multiply it by 9/5 (or
1.8), and then add 32. This result updates the value of temperature and represents the
Fahrenheit conversion of the Centigrade value reported by the BMP280.

Lesson 15 – I2C and Temperature Sensing Page 437

Prepared exclusively for [email protected] Transaction: 5095


THE WGET COMMAND
The wget command is a tool for downloading files from the command line. Running
wget followed by a web address containing the file or folder that you wish to download
will result in that file or folder being download to your current working directory. This can
be useful for downloading files when you’re already in the command line, without having
to open a web browser and navigate to the website and file:

wget https://42electronics.com/switches.png

When typed at the command line, this will download a copy of an image named
switches.png from the 42 Electronics website and save it into your current directory.

Lesson 15 – I2C and Temperature Sensing Page 438

Prepared exclusively for [email protected] Transaction: 5095


RUNNING MODULES AS IMPORTS VS. DIRECTLY
All of the modules that you have used so far have just been a list of function definitions
that can be imported, and called, from within your main program. This is helpful for
keeping your main program simple while still relying on the additional code available
through different imported modules.

Most of these modules cannot be run as a program directly. They do not contain any
kind of main program or loop that will run when executed directly. Some modules, like
bme280.py have the added benefit of being able to be imported into your program, as
well as executed directly.

You can tell if a program offers this executable functionality by the presence of this
block of code:

if __name__=="__main__":

This code checks to see if the program is being directly executed. If the program was
executed directly, in this case by running bme280.py, then this block will evaluate as
True and the code contained in the if block will execute:

def other_things():
print('This program is running as an import')

def main():
print('This program is being executed directly')

if __name__=="__main__":
main()

Lesson 15 – I2C and Temperature Sensing Page 439

Prepared exclusively for [email protected] Transaction: 5095


In this case, the function called main() will run and the print statement contained in that
function will be printed to the console. Here is the actual code contained in the main()
function of bme280.py:

def main():
(chip_id, chip_version) = readBME280ID()
print("Chip ID : %s" % chip_id)
print("Version : %s" % chip_version)

temperature,pressure,humidity = readBME280All()
print("Temperature : %s C" % temperature)
print("Pressure : %s hpa" % pressure)
print("Humidity : %s %%" % humidity)

This adds an extra level of functionality to a module like bme280.py. When first
connecting to a sensor like the BMP280, you can run this file directly and get some
output from the sensor to ensure it's communicating properly with the Raspberry Pi
using this file. Once that communication is confirmed you can then create a program by
importing this module, and calling the bme280.readBME280All() function from within
your own program.

Lesson 15 – I2C and Temperature Sensing Page 440

Prepared exclusively for [email protected] Transaction: 5095


THE SYSTEMEXIT() COMMAND
As you've seen previously, programs can generate errors if they are stopped in an
unexpected way by using CTRL-C or the stop button in Thonny. This is not usually a
problem but there is a command that can be inserted into your programs that will cause
the program to end more gracefully. This command is called SystemExit().

This command is built into Python and can be used without any additional module
imports. SystemExit() can be used anywhere in your program where you might want
to end the program:

if continue == 'y'
keep_running()
else:
SystemExit()

If continue equals 'y' then the function named keep_running will run. If not, the
SystemExit() will be executed and the program will end.

This command will be used to create a graceful exit for the programs you build in
Activities #3 and #4 of this lesson. An except condition will catch the
KeyboardInterrupt created by pressing CTRL-C or the stop button in Thonny, and
SystemExit() will be used to end the program without errors.

Lesson 15 – I2C and Temperature Sensing Page 441

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will remove the Ultrasonic Range Sensor from the circuit
you built in Lesson B-14 and connect the BMP280 Temperature Sensor. You will then
write a program that will display temperatures read from the sensor, as well as modify
that program to change the color of the RGB LED based on the temperature value.

ACTIVITY #1 – ENABLING THE I2C INTERFACE

In this activity you will enable the I2C interface on the Raspberry Pi. This interface must
be enabled to communicate with the BMP280 temperature sensor.

STEP #1
With the Raspberry Pi powered up, enter the configuration menu by clicking the
raspberry icon in the top-left corner and selecting Preferences and then Raspberry
Pi Configuration from the menu:

Lesson 15 – I2C and Temperature Sensing Page 442

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Once you're in the configuration
menu, click on the Interfaces tab
and click the Enabled radio button
for I2C. Click OK to save the setting
change.

STEP #3
The Raspberry Pi must be rebooted before the I2C setting will take effect. Instead of
rebooting, shut down the Pi so you can safely make changes to the circuit on the
breadboard. After the Pi has completely shut down, proceed to the next activity.

Lesson 15 – I2C and Temperature Sensing Page 443

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – ADDING THE BMP280 TEMPERATURE SENSOR
Using the circuit from Lesson B-14 as a starting point, you will remove the Ultrasonic
Range Sensor and level shifter and add the BMP280 Temperature Sensor.

STEP #1
The first step will be to remove the Ultrasonic Range Sensor and 5V to 3.3V level shifter
from the circuit you built in Lesson B-14. Remove these two components and any
jumper wires associated with them. Leave the jumper wire in place between 3.3V and
P1-3 as it will be used later to power the BMP280. With this step completed, your circuit
should look like this:

Lesson 15 – I2C and Temperature Sensing Page 444

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Next, add the BMP280 to column J of the breadboard between rows 34-39. Make sure
the main body of the sensor is near the P2/N2 power rails as this will ensure its pinout
matches the wiring you will complete in the next step.

Lesson 15 – I2C and Temperature Sensing Page 445

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The last step in the circuit build will be to add jumper wires that will connect the BMP280
to your Raspberry Pi. Add the following jumper wires to your circuit:

Short jumper wire – 3.3V – between P1-34 and F34

Short jumper wire – GND – between N1-34 and F35

Long jumper wire – SCL – between B3 and F36

Long jumper wire – SDA – between A2 and F37

Lesson 15 – I2C and Temperature Sensing Page 446

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Double-check all of your connections from the last step and power on the Raspberry Pi.
The BMP280 will now be powered up and transmitting temperature and barometric
pressure data to your Raspberry Pi, but you will need to obtain the bme280.py driver file
that will be used to decode the output from the sensor. First, you will create a directory
in /home/pi that will contain the driver file as well as the Python programs you create
that will read the temperature sensor data.

Open up a new Terminal window and create a new folder called temp_sensor:

mkdir temp_sensor

Next, move into that directory so you can get ready to download the driver file:

cd temp_sensor

STEP #5
You should now be located in /home/pi/temp_sensor and you're ready to download the
bme280.py driver file from our GitHub repository using the wget command. Enter the
following command and the file will be downloaded to your current directory:

wget https://raw.githubusercontent.com/42electronics/level_b/master/bme280.py

Ensure you type the web address exactly as listed above or the download will fail. You
will see the file download progress and confirmation that 'bme280.py' was saved.
Confirm the file is present in your directory by using the ls command to list the contents
of your directory:

The file bme280.py will be listed in your directory contents.

Lesson 15 – I2C and Temperature Sensing Page 447

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Before you try to write a program that will use data from the sensor, use the i2cdetect
command to ensure the sensor is powered up and is being seen by the Raspberry Pi on
address 0x76. Run the command below:

i2cdetect -y 1

This will print a list of all I2C devices that the Raspberry Pi sees and your BMP280
should be listed at address 0x76:

If your sensor is not listed, power down the Raspberry Pi and double-check your wiring.
Do not proceed to the next activity until your sensor is reporting in at 0x76 as seen in
the image above.

Lesson 15 – I2C and Temperature Sensing Page 448

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – CREATING A PROGRAM TO DISPLAY TEMPERATURE
In this activity you will create a program that will display the temperature being sensed
by the BMP280 in your circuit.

STEP #1
The driver file can only be accessed by programs within the same folder, so your new
program must also reside in the /home/pi/temp_sensor directory. Open Thonny and
click the green plus to create a new file. Click File and Save As to save the new file.
Ensure the directory is /home/pi/temp_sensor so your program will be in the same
folder as bme280.py:

Use the filename temp_reading and click Save.

STEP #2
Now that you've saved your new file in the /home/pi/temp_sensor directory, you’re ready
to start building the program. The first section of the program will need to import the
bme280 and time modules:

import bme280
import time

Lesson 15 – I2C and Temperature Sensing Page 449

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Now that you've imported the modules you need, create a try/except loop that will
contain the main program and also catch any presses of CTRL-C or the stop button in
Thonny. Use except KeyboardInterrupt: to catch these presses and run a
SystemExit() if one is encountered:

import bme280
import time

try:

except KeyboardInterrupt:
SystemExit()
Remember to indent the SystemExit() command so it belongs to the except
condition.

STEP #4
The program now needs a main loop so you can check the temperature repeatedly. Add
a while True: loop that will pull in readings from the BMP280 using the bme280.py
driver file:

import bme280
import time

try:
while True:
temperature,pressure,humidity = bme280.readBME280All()

except KeyboardInterrupt:
SystemExit()

NOTE: Even though the BMP280 included with your kit will not output humidity
data, the driver file will still send a 0 for this value. Failing to assign this extra
value in your program will result in errors when the program receives sensor
information from bme280.py.

Lesson 15 – I2C and Temperature Sensing Page 450

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
The temperature data is now available in your program, but it's currently in Centigrade
instead of Fahrenheit. Add another line of code that will convert the value stored as
temperature from C to F:

import bme280
import time

try:
while True:
temperature,pressure,humidity = bme280.readBME280All()
temperature = (temperature * 9/5) + 32

except KeyboardInterrupt:
SystemExit()

The value stored as temperature will be multiplied by 9/5 and then added to 32. This
new value will then overwrite the old value of temperature.

STEP #6
Now that the temperature has been converted to F, print it to the console. Using %
notation in the print statement, trim the printed value down to a float of two decimal
points by specifying %.2f inside the print statement.

You should also add a time delay of .3 seconds to avoid printing temperature values so
fast that you can't read them:

import bme280
import time

try:
while True:
temperature,pressure,humidity = bme280.readBME280All()
temperature = (temperature * 9/5) + 32
print('Temp= %.2f' %temperature)
time.sleep(.3)

except KeyboardInterrupt:
SystemExit()

Lesson 15 – I2C and Temperature Sensing Page 451

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Run the program. The temperature values from the sensor will be printed in the console
every .3 seconds:

Press CTRL-C or the stop button in Thonny to end the program. If the program does not
run as expected, confirm every line matches the program in Step #6. Ensure the
program is running as expected before proceeding to the next activity.

Lesson 15 – I2C and Temperature Sensing Page 452

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #4 – ADDING THE RGB LED TO INDICATE TEMPERATURE
In this activity you will modify the program from the last activity to add the RGB LED. A
temperature reading below a setpoint will be indicated by green on the RGB LED and a
temperature reading above will cause the RGB LED to turn red.

STEP #1
You will be making quite a few
additions to the program from the
last activity, so make a new copy
of temp_reading that you can
modify without affecting your
original program. With
temp_reading open , click File
and Save As. Name the new file
temp_led and click Save:

STEP #2
Now that you have a copy of temp_reading saved as temp_led you can start making
modifications to the temp_led program. The RGB LED is connected to three GPIO pins
so you will need to import the GPIO module:

import bme280
import time
import RPi.GPIO as GPIO

try:
while True:

Lesson 15 – I2C and Temperature Sensing Page 453

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The red element of the RGB LED is connected to GPIO13 and green is connected to
GPIO19. While you could refer to these numbers GPIO pins directly, make things a little
easier by using a couple of variables named red and green that will hold each color's
pin number: This way you can use the values red and green to reference the pins
instead of 13 and 19:

import bme280
import time
import RPi.GPIO as GPIO

red = 13
green = 19

try:

Now you can use the values red and green to reference the pins instead of 13 and 19.

STEP #4
The next step will be to set the GPIO pin mode as BCM and configure GPIO13 and
GPIO19 as outputs. Instead of using the pin numbers for configuring the outputs you
can substitute the variables from the last step:

import RPi.GPIO as GPIO

red = 13
green = 19

GPIO.setmode(GPIO.BCM)
GPIO.setup(red, GPIO.OUT)
GPIO.setup(green, GPIO.OUT)

try:

Lesson 15 – I2C and Temperature Sensing Page 454

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
The GPIO pins connected to the RGB LED are now ready to use in your program, and
they can be referenced directly by their color names of red and green. Add an if/else
block inside the main loop that will check the value of temperature.

If the temperature is equal to or above 80 degrees, the green element will turn off and
the red element will turn on. The else will be True for any temperature below 80 and will
turn off the red element and turn on the green.

Insert this block of code between printing the temperature and the time.sleep
delay:

temperature,pressure,humidity = bme280.readBME280All()
temperature = (temperature * 9/5) + 32
print('Temp= %.2f' %temperature)

if temperature >= 80:


GPIO.output(green, GPIO.LOW)
GPIO.output(red, GPIO.HIGH)
else:
GPIO.output(green, GPIO.HIGH)
GPIO.output(red, GPIO.LOW)
time.sleep(.3)

STEP #6
The last modification to the program will be adding a GPIO.cleanup() to the exception
that's caught when CTRL-C or stop in Thonny is pressed. This will keep the program
from generating errors when exiting, and this will also keep the LED from being left on
after the program exits.

Add GPIO.cleanup() just before the SystemExit() in the except block:

except KeyboardInterrupt:
GPIO.cleanup()
SystemExit()

Lesson 15 – I2C and Temperature Sensing Page 455

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Run the program and the LED will indicate the current temperature.

NOTE: You can carefully touch the metal case of the BMP280 IC to increase the
temperature and trigger the LED to change. Take caution to avoid touching the
electrical connections on the circuit board. While there are no voltages present
that could cause injury, introducing static electricity into the electrical connections
could damage the BMP280 IC.

NOTE – 80 degrees was selected as a reasonable setpoint to change the LED


color based on the environment here at 42 Electronics. If your room is already
above 80 degrees then the LED will always indicate red. You can adjust this
setpoint value in the program as needed in order to make the temperature range
fit your environment.

Lesson 15 – I2C and Temperature Sensing Page 456

Prepared exclusively for [email protected] Transaction: 5095


Here is the entire program in case you need to check your program for errors:

import bme280
import RPi.GPIO as GPIO
import time

red = 13
green = 19

GPIO.setmode(GPIO.BCM)
GPIO.setup(red, GPIO.OUT)
GPIO.setup(green, GPIO.OUT)

try:
while True:
temperature,pressure,humidity = bme280.readBME280All()
temperature = (temperature * 9/5) + 32
print('Temp= %.2f' %temperature)

if temperature >= 80:


GPIO.output(green, GPIO.LOW)
GPIO.output(red, GPIO.HIGH)
else:
GPIO.output(green, GPIO.HIGH)
GPIO.output(red, GPIO.LOW)
time.sleep(.3)

except KeyboardInterrupt:
GPIO.cleanup()
SystemExit()

Leave the circuit assembled for use in Lesson B-16.

Lesson 15 – I2C and Temperature Sensing Page 457

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Does the BMP280 Temperature Sensor need a driver file or can your programs
access its readings directly?

2. How are different devices connected to the I2C bus identified?

3. What does the following code mean when you find it in a module file?

if __name__=="__main__":

Answers can be found on the next page.

Lesson 15 – I2C and Temperature Sensing Page 458

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Does the BMP280 Temperature Sensor need a driver file or can your programs
access its readings directly?

ANSWER: The BMP280 Temperature Sensor needs a driver file for


communication. Files that intend to access temperature readings must be
located in the same directory as the driver file.

2. How are different devices connected to the I2C bus identified?

ANSWER: I2C devices are identified by unique addresses on the I2C bus such
as 0x3C or 0x78.

3. What does the following code mean when you find it in a module file?

if __name__=="__main__":

ANSWER: This block of code checks to see if the program was executed
directly, and if so, the code contained in this if: block will execute. If the file was
imported as a module, this line will evaluate as False, and the code inside this
block will be skipped.

Lesson 15 – I2C and Temperature Sensing Page 459

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned to use I2C communication and work with a temperature
sensor.

In the next lesson, you will learn to work with another I2C device, an OLED display.

Lesson 15 – I2C and Temperature Sensing Page 460

Prepared exclusively for [email protected] Transaction: 5095


LESSON 16

OLED I2C DISPLAY

OBJECTIVE
In this lesson you will learn to work with an OLED display to display information from the
program and including data gathered from external sensors.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, mouse, and the internet


• Assembled Circuit from Lesson B-15
• 1 x SSD1306 OLED I2C Display
• 5 x Short Jumper Wires
• 1 x Long Jumper Wires
• 1 x Slide Switch

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Slide Switches (Lesson B-5)


• Github (Lesson B-8)
• I2C Communication, Temperature Sensors (Lesson 15)

Lesson 16 – OLED I2C Display Page 461

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson you will use how to connect and integrate a small display into your
programs. This type of display can be used to display information from the program,
information from external sensors, and small images, making it a very useful addition to
a circuit.

OLED DISPLAY HARDWARE

The display included in your kit is a 128x64 OLED I2C display. Here are the details:

• 128x64 – The display is 128 pixels wide by 64 pixels tall


• OLED – Organic Light Emitting Diode
• I2C – Communicates with the Raspberry Pi using I2C

RESOLUTION AND PIXELS


A pixel is the smallest dot that a display can produce. The display will use these pixels
to build the image or text that is displayed on the screen. The display in your kit is made
up of a matrix of 128 pixels horizontally by 64 pixels vertically:

The horizontal/vertical pixel count will often be referred to as the resolution of the
display, so this display would have a resolution of 128x64. This means that the images
or text it displays must be made up of no more than 128 pixels horizontally and 64
pixels vertically.

Lesson 16 – OLED I2C Display Page 462

Prepared exclusively for [email protected] Transaction: 5095


128x64 may not seem like very many pixels, but this display is .96" wide and .55" tall,
so these pixels are very small and packed close together. This allows for the display of
fairly sharp text and images in this small space. The number of pixels that fit in one inch
of screen space is called the PPI value, or pixels per inch. This means that with 128
pixels fitting into about 1" of space, this display would be about 128PPI or 128 pixels per
inch.

Screens that are meant to be viewed at close distances, like mobile phones, must have
fairly high PPI values to make text and images appear smooth. Devices like TVs will
have a much lower PPI value as they are meant to be viewed at much greater distances
than mobile phones.

You can calculate the PPI value of any device by knowing the dimensions of the display
and the number of pixels present. By dividing 128 pixels by .96 inches of screen, you
come up with a PPI of 133.3.

A 55 inch television is around 49 inches wide. If this television has standard HD


resolution of 1920x1080 then the PPI can be computed by dividing 1920 pixels by 49,
resulting in a PPI of around 39. If a similarly sized television is capable of displaying 4K
video at a resolution of 3840x2160, then the PPI would be 3840 divided by 49, resulting
in 78.

Understanding pixel size and location is important for building programs that will display
images or fonts. Displaying full screen images is fairly simple as they use every pixel on
the display, but when displaying smaller images and text, X and Y position information
must to provided to determine where it will be positioned on the display:

Lesson 16 – OLED I2C Display Page 463

Prepared exclusively for [email protected] Transaction: 5095


Each pixel has an X and Y value that corresponds with its position in the matrix. X
values start at 0 on the left of the screen and increase as you move to the right:

Y positions start at the top of the display and increase as you move down:

The position of any pixel can be specified by combining its X and Y values into the
expression (X,Y). The top left corner of the display is X = 0 and Y = 0 which are
expressed as (0,0). Here are all four corners of the display marked with their X and Y
coordinates:

Lesson 16 – OLED I2C Display Page 464

Prepared exclusively for [email protected] Transaction: 5095


A pixel at X = 50 and Y = 36 can be specified by the position (50,36):

This may seem very complicated, but you will often only need to specify the starting
pixel position for whatever you want to print, and the driver file for that display will take
care of the rest.

Lesson 16 – OLED I2C Display Page 465

Prepared exclusively for [email protected] Transaction: 5095


OLED TECHNOLOGY
Most small displays in the past used LCD, or Liquid Crystal Display technology to
display images and text. In an LCD screen, each pixel can be turned on or off. This type
of display is often found on calculators as it is a very low-cost display technology. One
problem that LCDs have is low-light viewing. A backlight module may be added to
increase the contrast of the display. A backlight is a panel, often made of large, white
LED elements, that is used to shine visible light through each the pixels of an LCD
screen. In a monochrome or one-color LCD display, pixels can either be black or white.
A black pixel will not allow the backlight to pass through while a white pixel will allow the
backlight to pass front back to front, making that pixel appear to be lit when viewed from
the front:

LCD displays work well for many devices, however some backlight modules can require
a fair amount of energy to run, making this type of display problematic for mobile use.

Lesson 16 – OLED I2C Display Page 466

Prepared exclusively for [email protected] Transaction: 5095


OLED, or Organic LED technology got
rid of the backlight required by LCDs. In
an OLED display, each pixel is a tiny,
low-powered LED. This means that
OLED displays can use much less
energy than their LCD counterparts. A
lot of the devices you interact with like
mobile phones, televisions, and
smartwatches might be using OLED
display technology.

I2C COMMUNICATION
The OLED screen that comes with your Level B kit uses I2C for communication. This
means that using only 3.3V, GND, and two data lines, you can control every pixel on the
display. This is in contrast to other types of displays that may need 10 or more
connections to control even fewer pixels. Using less connections will make it easier to
integrate this display into more of your projects, while keeping required wiring to a
minimum.

Lesson 16 – OLED I2C Display Page 467

Prepared exclusively for [email protected] Transaction: 5095


SSD1306 DISPLAY DRIVER
Your 128x64 OLED screen uses the SSD1306 chipset to drive the display, which
means you need a driver file to communicate with the display from within your Python
programs. Fortunately, Adafruit has created a driver package that works well with this
display.

You will go through the download and installation of the Adafruit driver package later in
Activity #1, but before that you will need to review some of the commands that will be
used in your program to communicate with the display.

REQUIRED MODULES
Communication with your OLED display from within a Python program requires the
following modules be imported:

import Adafruit_SSD1306

This will import the SSD1306 library from Adafruit that contains information for
communicating with the hardware inside the display.

import RPi.GPIO as GPIO

The Adafruit library requires that the RPi.GPIO library be imported to communicate with
the GPIO pins connected to the display.

from PIL import Image, ImageDraw, ImageFont

PIL stands for Python Imaging Library. It contains code that allows the manipulation of
images and text from within a Python program. In this case the functions Image,
ImageDraw, and ImageFont are being imported from the module named PIL so they
can be used to interact with the display.

Lesson 16 – OLED I2C Display Page 468

Prepared exclusively for [email protected] Transaction: 5095


SIZE CONFIGURATION
There are many different display sizes that can be driven by the SSD1306 chipset, so
the size of your display must be communicated to the driver module. This allows images
and text to be formatted properly. The Adafruit driver has many size options commented
out in the driver file, but you will be using the one below:

disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)

This line will create a variable named disp that can send the Adafruit_SSD1306 driver
file the exact model of our display which is SSD1306_128_64. This configuration line
also contains the argument rst=None which lets the driver file know that your display
does not require a reset pin.

STARTING THE DISPLAY


Now that the configuration has been saved as the variable disp, the command below
can be used to initialize or turn on the display:

disp.begin()

This will send the configuration information to the Adafruit_SSD1306 driver file and turn
the display on using those configuration parameters.

Lesson 16 – OLED I2C Display Page 469

Prepared exclusively for [email protected] Transaction: 5095


VARIABLES THAT SIMPLIFY COMMUNICATION WITH THE DISPLAY
While you could specify the height and width dimensions every time you want to send
something to the display, it's easier to set these as variables that can be reused
throughout the program. Using disp.width and disp.height will allow these values to be
configured by the driver file:

width = disp.width
height = disp.height

The driver file was written to accommodate many display sizes which is the reason
these values are variable and being fed from the driver file. Since you are only using the
128x64 display you could also specify width = 128 and height = 64 directly in your
program without causing issues.

Instead of sending every command to the display individually, a display buffer is used to
store up display commands. It's much like printing a word processor file on your printer.
You can get the document exactly the way you want and then print it all at once. The
display buffer operates in the same way. You can fill it up with all the images and text
you would like to display, and once it's ready, you can send the entire contents to the
display, so it will look exactly the way you want.

Your program will create a value called image that will create an image buffer named
Image.new:

image = Image.new('1', (width, height))

This image buffer will hold anything that you would like to send to the display. The
argument of 1 specifies that the image buffer will be 1-bit color, or black and white. The
width and height specify the dimensions of Image.new.

Another variable named draw will be used to send information to the image buffer:

draw = ImageDraw.Draw(image)

This variable will use a function named Draw in an imported module named ImageDraw
to draw images and text within the image buffer. The argument of image specifies that

Lesson 16 – OLED I2C Display Page 470

Prepared exclusively for [email protected] Transaction: 5095


any draw command will be applied to the previously created image buffer named
Image.new.

Different fonts can be specified when printing text to the display. The Raspberry Pi has
a few fonts loaded into /usr/share/fonts/truetype that can be used in your projects. One
of the fonts available in this folder is called FreeSans:

font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf',18)

This line of code will create a variable named font that will tell the ImageFont module
to use the truetype font named FreeSans.ttf located at the specified path. The
argument of 18 specifies the size of the font to be used, in pixels.

DRAWING ON THE DISPLAY


Now that you have some variables saved to streamline communication with the display,
you can start to use some commands to load and display the image buffer.

draw.rectangle((0,0,width,height), outline=0, fill=0)

This command will draw a rectangle in the image buffer. The top corner of the rectangle
will start at the coordinate X=0,Y=0 and the values width and height specify the
dimensions of the box, in this case 128 wide by 64 tall. The outline value of 0 means the
rectangle will have no border outline, and the fill value of 0 means that the rectangle will
be black.

Drawing a black rectangle over the entire image buffer will essentially "erase" the entire
image buffer. This is generally done as a first step to clear the image buffer of content
from any previous commands.

draw.text((0, 0), "Hello World!!", font=font, fill=255)

This command will print the text Hello World!! in the upper-left corner of the image
buffer starting at pixel 0,0. The font of this text will be determined by the value of the
font variable that was specified earlier in the program. The color of the text will be
white due to the fill value of 255 which represents the 8-bit equivalent of white.

Lesson 16 – OLED I2C Display Page 471

Prepared exclusively for [email protected] Transaction: 5095


The 8-bit color scale is made up of grayscales values from 0 representing black up to
255 representing white. Values of gray can be represented using 1-254 of this scale,
however your display is only capable of black and white, so the only values it
understands are zero and non-zero.

Once you have filled the image buffer with everything you want to display, the following
commands can be used to send the stored image buffer to the screen:

disp.image(image)

This command will load the image buffer stored as image and make it ready to be
displayed.

disp.display()

This command tells the display to show the image from the image buffer. The image on
the screen is only updated when this command runs. Exiting the program after
displaying an image will cause that image to continue displaying even after the program
has exited, until power is removed from the screen. If you wish to clear the display
during your program, or on program exit, you can use the following command:

disp.clear()

This will load a cleared image buffer into the screen, but this will not immediately clear
the screen. Another disp.display() command must be issued to update the screen
with the cleared image buffer. Together the commands to clear the screen would be:

disp.clear()

disp.display()

Using these prior to your program exiting will ensure that the screen is cleared of all
contents before the program exits.

Lesson 16 – OLED I2C Display Page 472

Prepared exclusively for [email protected] Transaction: 5095


UNICODE CHARACTERS
Unicode characters are special characters that can create small symbols or images
along with text. They are like emoticons or emojis, but there are limited options, and
there are no animations.

Unicode characters can be specified by their Unicode designator. Here are just a few:

Name Symbol Unicode designator


Happy face ☺ 263A
Check box ☑ 2611
Carriage return ↵ 21B5
Ohm symbol Ω 2126

For a full list of Unicode characters, you can visit:


https://en.wikipedia.org/wiki/List_of_Unicode_characters

You can use these Unicode characters in your programs by prefacing the Unicode
designator with \u to let the program know the following four digits represent a Unicode
character. For example, here is the code to print Hello:

print('Hello')

If you would like to add a happy face after Hello, you can add \u263A after Hello:

print('Hello \u263A')

The \u263A will be treated as a single character in the print statement and will be
replaced with ☺ when the statement is printed to the console.

Different systems will have varying levels of compatibility with these Unicode
characters. Some programs will support the display of all characters, while others won't
display any of them.

When using a small display like the SSD1306, Unicode character support will depend
on the internal software that's running inside the display. The Raspberry Pi program can

Lesson 16 – OLED I2C Display Page 473

Prepared exclusively for [email protected] Transaction: 5095


send the command to print a Unicode 263A, but if the display doesn't understand that
specific Unicode character, it will be replaced by a small box containing a question
mark:

Luckily, the SSD1306 is programmed to display the happy face and quite a few other
Unicode characters.

Lesson 16 – OLED I2C Display Page 474

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will add the 128x64 OLED display and a slide switch to the
temperature sensing circuit from Lesson B-15. You will also write a program that will
display text on the screen and then modify that program to incorporate real-time input
from the temperature sensor and slide switch.

ACTIVITY #1 – MODIFY THE CIRCUIT

In this activity you will add the 128x64 OLED display and the slide switch to the circuit
you build in Lesson B-15. You will also download the Adafruit driver files as well as test
the I2C connection to the screen

STEP #1
The Raspberry Pi must be powered off to make changes to the breadboard circuit.
Power down the Raspberry Pi.

Lesson 16 – OLED I2C Display Page 475

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Next, you will add the 128x64 OLED display to the breadboard. Ensure the main body
of the display is over the P1 and N1 rails and connect the pins to column C between
rows 43 and 46. The signal labels in the display should match the following:

GND – C43

VCC – C44

SCL – C45

SDA – C46

Lesson 16 – OLED I2C Display Page 476

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
You will now make the 3.3V, GND, and I2C connections to the display. Since multiple
devices can be connected in parallel to the same I2C bus, you will be connecting to the
temperature sensor I2C connections instead of going all the way across the breadboard
to connect near the wedge.

Connect four jumper wires between the following points:

GND – short jumper wire between E43 and N1-37

VCC – short jumper wire between E44 and P1-36

SCL – short jumper wire between E45 and H36

SDA – short jumper wire between E46 and H37

Lesson 16 – OLED I2C Display Page 477

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
The last circuit change will be to add a slide switch that will be used when you build
programs that will display information on the screen. The wiring for the slide switch will
be connected between ground and GPIO21. Add two jumper wires and the slide switch
to the following points:

Slide switch – Column A between rows 54 and 56

Short jumper – between E55 and N2-59

Long jumper – between E54 and J20

Lesson 16 – OLED I2C Display Page 478

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
All the changes have now been made to the circuit. Double-check that your circuit
matches the image from Step #3, and then power up your Raspberry Pi. The next step
will be to download the Adafruit SSD1306 driver so your Python programs can use it to
communicate with the display.

Once the Raspberry Pi has fully booted up, open a Terminal window and enter the
command below:

git clone https://github.com/42electronics/Adafruit_Python_SSD1306.git

This will download a copy of the Adafruit_Python_SSD1306 library from our


42electronics GitHub repository. Once completed, you will have a new folder named
Adafruit_Python_SSD1306 containing the modules that will be used to send information
to the display.

STEP #6
The last step in the installation process for the SSD1306 module is to run the install
script. First, change into the directory by typing the following command:

cd Adafruit_Python_SSD1306

Now that you're in the directory containing the script, all that's left is to run the script
using:

sudo python3 setup.py install

This will make the module available in the Python 3 environment so the programs you
write can run from within Thonny.

Lesson 16 – OLED I2C Display Page 479

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Before writing a program, confirm that your Raspberry Pi is communicating with the
128x64 OLED screen by using the i2cdetect command. In your Terminal window, run
the following command:

i2cdetect -y 1

Both your screen and the temperature sensor should show up as devices connected to
the I2C bus. The temperature sensor will be the device at 0x3c and the screen will be
at 0x76:

Do not proceed to Activity #2 until both devices are reporting in properly when using
i2cdetect. If they are not detected, check wiring and jumper wire connections. If a
connection is bad, then a reboot of the Pi may be required to restore I2C
communication.

Lesson 16 – OLED I2C Display Page 480

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – CREATE A PROGRAM TO DISPLAY TEXT
In this activity you will build a program that will display some text strings on your OLED
screen. You will also add some Unicode characters to this message to make it more
interesting.

STEP #1
In this activity and the next, you will be building a program, and then make modifications
which will allow it to communicate with the temperature sensor. This means that your
second program will need to be in the same folder as the bme280.py driver file, or
inside the temp_sensor directory you created in Lesson B-15.

To eliminate confusion when making modifications to this program in Activity #3, you will
build the initial program in the temp_sensor directory. Your initial program will not need
access to the temperature sensor, but access to the bme280.py file will be required for
Activity #3.

Open Thonny and create a new program. Click File and then Save As to save your new
program. Navigate inside the /home/pi/temp_sensor directory and name your program
display_text:

Lesson 16 – OLED I2C Display Page 481

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Now that you have a file created and saved in the temp_sensor directory, you can start
adding the code you need to display text strings on the screen. Start by importing the
time, Adafruit_SSD1306, and RPi.GPIO modules. You will also import the Image,
ImageDraw, and ImageFont modules from PIL or the Python Imaging Library. The PIL
imports can all be on one line separated by commas to save space:

import time
import Adafruit_SSD1306
import RPi.GPIO as GPIO
from PIL import Image, ImageDraw, ImageFont

STEP #3
Next, you need to let the Adafruit_SSD1306 library know what display type it will be
communicating with when the disp variable is used throughout the program. Configure
the display as a 128x64 display with no reset line needed, and initialize the display with
the disp.begin() command by adding the following lines:

import RPi.GPIO as GPIO


from PIL import Image, ImageDraw, ImageFont

disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)
disp.begin()

Lesson 16 – OLED I2C Display Page 482

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Now you will create some variables that will store height, width, image buffer, and draw
information that will be used throughout the program. Add the following four lines to
name and set these variables:

disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)
disp.begin()

width = disp.width
height = disp.height
image = Image.new('1', (width, height))
draw = ImageDraw.Draw(image)
If you have questions about what any of these lines are used for, please refer back to
the subsection on Variables That Simplify Communication with the Display in this
lesson.

STEP #5
Next, you will select the font that will be used for text on the display by creating a
variable named font. When the font variable is used it will let the ImageFont function
from the PIL know that you want to use the FreeSans font at the specified path, in a
height of 18 pixels:

image = Image.new('1', (width, height))


draw = ImageDraw.Draw(image)

font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf',18)

Lesson 16 – OLED I2C Display Page 483

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Now that the setup elements are in place, you can start to build the main program loop.
To start the main part of the program, add a try/except loop that will catch keyboard
interrupts and clear the display. This will blank the display before the program exits.

Add the following lines to the bottom of your program:

font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf',18)

try:

except KeyboardInterrupt:
disp.clear()
disp.display()
SystemExit()

STEP #7
The main loop is now ready for some code. Add a while True: loop that will print a
few lines of text. Line 1 of the text will read 'Hello from" and line 2 will read "42
Electronics". Line 3 will be a row of five happy faces using the Unicode character 263A.
Each line will start at position X=0, but the Y value will change so each line is below the
last. Line 1 will use a Y position of 0, line 2 will use a Y position of 22, and line 3 will use
a Y position of 44. This will ensure the lines are properly spaced vertically.

Add a while True: loop inside the existing try: loop that contains the following text
lines:

try:
while True:
draw.text((0, 0), 'Hello from', font=font, fill=255)
draw.text((0, 22), '42 Electronics', font=font, fill=255)
draw.text((0, 44), '\u263A \u263A \u263A \u263A \u263A',
font=font, fill=255)

except KeyboardInterrupt:

NOTE: Although line 3 of the text is split into two lines above, this is due to
display limitations within this document. To execute properly, this will need to be
one continuous line of code in Thonny.

Lesson 16 – OLED I2C Display Page 484

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
The image buffer has been loaded with the text to be displayed, and the last step is to
push the image buffer out to the screen by adding the disp.image(image) and
disp.display() commands. Add these commands just below line three of the text:

draw.text((0, 0), 'Hello from', font=font, fill=255)


draw.text((0, 22), '42 Electronics', font=font, fill=255)
draw.text((0, 44), '\u263A \u263A \u263A \u263A \u263A',
font=font, fill=255)
disp.image(image)
disp.display()

except KeyboardInterrupt:

Lesson 16 – OLED I2C Display Page 485

Prepared exclusively for [email protected] Transaction: 5095


STEP #9
The last step is to run the program. Click the Run button in Thonny and your OLED
screen will display the three-line message. Click the Stop button and the screen will go
blank. If your screen does not operate as expected, verify your code matches the
program below. Do not proceed to the next activity until this program and the screen are
working as expected.

import time
import Adafruit_SSD1306
import RPi.GPIO as GPIO
from PIL import Image, ImageDraw, ImageFont

disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)

disp.begin()

width = disp.width
height = disp.height
image = Image.new('1', (width, height)) # '1' converts image to 1-bit color

draw = ImageDraw.Draw(image)

font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf',18)

try:
while True:
draw.text((0, 0), 'Hello from', font=font, fill=255)
draw.text((0, 22), '42 Electronics', font=font, fill=255)
draw.text((0, 44), '\u263A \u263A \u263A \u263A \u263A',
font=font, fill=255)
disp.image(image)
disp.display()

except KeyboardInterrupt:
disp.clear()
disp.display()
SystemExit()

Lesson 16 – OLED I2C Display Page 486

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – MODIFY PROGRAM TO DISPLAY SENSOR INFORMATION
In this activity, you will build a modification to the program from the last activity to
display the status of a switch connected to GPIO21, display temperature readings from
the BMP280, as well as display an alert when the temperature exceeds a setpoint.

STEP #1
First, make a new copy of the display_text.py program that you created in the last
lesson. That way you will have the original if you want to run it again or make small
modifications to that program later.

With display_text.py open in Thonny, click File and then Save As. Enter the new
filename as display_info and click the Save button. This will keep the original
display_text.py file intact, while making a new copy called display_info that will contain
all the same code as the original file.

STEP #2
Now that you have a copy of the original file to work with, start adding the code to
display temperature and switch information. The bme280.py module will need to be
imported to communicate with the temperature sensor. Import it just below the rest of
the import block:

import RPi.GPIO as GPIO


from PIL import Image, ImageDraw, ImageFont
import bme280

disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)

Lesson 16 – OLED I2C Display Page 487

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Next, the switch connected to GPIO21 will need to be configured as an input. Configure
the GPIO mode as BCM and configure GPIO21 as an input with an internal pull-up so
that making a connection to ground through the switch will trigger the input. Insert these
two lines just below the import block:

from PIL import Image, ImageDraw, ImageFont


import bme280

GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_UP)

disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)

STEP #4
The setup items are now in place to communicate with the screen, the temperature
sensor, and the slide switch. It's now time to start replacing the lines of code from the
main program loop with new lines that will:

1. Pull a temperature reading from the BMP280


2. Blank the image buffer by drawing a black rectangle
3. Check the slide switch position and display a message based on position
4. Display the temperature reading
5. Check the temperature reading and display an alert if value is above setpoint

The first step in this process will be to remove everything from the while True: loop
except for two lines at the end that push the image buffer to the screen and display the
image. Those two lines will still be used at the end of our new loop.

When complete, your while True loop should look like this:

try:
while True:

disp.image(image)
disp.display()

except KeyboardInterrupt:

Lesson 16 – OLED I2C Display Page 488

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
Start off the loop by pulling the temperature from the BMP280, converting from C to F,
and storing the current value as temperature. This code is pulled directly from your
work with the temperature sensor in Lesson B-15. Insert the two lines below at the
beginning of your while True: loop:

try:
while True:
temperature,pressure,humidity = bme280.readBME280All()
temperature = (temperature * 9/5) + 32

disp.image(image)
disp.display()

STEP #6
Since you will be refreshing live data on the screen, you don’t want old data left in the
image buffer interfering with new data. This would result in both old and new text being
displayed at the same time, making any updated areas of the screen unreadable.

You can draw a black rectangle over all the pixels in the image buffer every time the
main loop runs. This will clear out any old pixel data and make the image buffer ready
for new data.

Add the line of code below to draw a black rectangle the width and height of the screen
to clear the image buffer:

try:
while True:
temperature,pressure,humidity = bme280.readBME280All()
temperature = (temperature * 9/5) + 32

draw.rectangle((0,0,width,height), outline=0, fill=0)

disp.image(image)

Lesson 16 – OLED I2C Display Page 489

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
The image buffer has now been cleared and it's ready for some text. Use the first line to
display the position of the slide switch connected to GPIO21. Add this if/else block below
the rectangle code:

try:
while True:
temperature,pressure,humidity = bme280.readBME280All()
temperature = (temperature * 9/5) + 32

draw.rectangle((0,0,width,height), outline=0, fill=0)

if GPIO.input(21) == True:
draw.text((0, 0), "Switch is OFF", font=font, fill=255)
else:
draw.text((0, 0), "Switch is ON", font=font, fill=255)

disp.image(image)

GPIO21 will be checked for it's True (High) or False (Low) status. If GPIO21 is High then
Switch is OFF will be displayed on the first line of the screen. If GPIO21 is not High
then Switch is ON will be displayed on the first line of the screen. The coordinates of
(0,0) will cause either of these lines to be positioned at the top-left of the screen.

Lesson 16 – OLED I2C Display Page 490

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
The next line of the screen will simply print the current temperature on the second line
of the screen. A Y position of 22 will cause this line of text to be positioned 22 pixels
from the top of the screen.

Add the following line below the block of code displaying the switch information:

try:
while True:
temperature,pressure,humidity = bme280.readBME280All()
temperature = (temperature * 9/5) + 32

draw.rectangle((0,0,width,height), outline=0, fill=0)

if GPIO.input(21) == True:
draw.text((0, 0), "Switch is OFF", font=font, fill=255)
else:
draw.text((0, 0), "Switch is ON", font=font, fill=255)

draw.text((0, 22), "Temp: %.2f" %temperature, font=font, fill=255)

disp.image(image)

Lesson 16 – OLED I2C Display Page 491

Prepared exclusively for [email protected] Transaction: 5095


STEP #9
The last modification will be the alert code. This will print a third line at Y position 44 that
will indicate an alert at any temperature above a setpoint. This block will be a simple if
condition that will add the alert message if the value of temperature is above 80. No
else: condition is required as the third line will just be left blank if no alert is required.

Add the if: block for the alert code just below the temperature code:

try:
while True:
temperature,pressure,humidity = bme280.readBME280All()
temperature = (temperature * 9/5) + 32

draw.rectangle((0,0,width,height), outline=0, fill=0)

if GPIO.input(21) == True:
draw.text((0, 0), "Switch is OFF", font=font, fill=255)
else:
draw.text((0, 0), "Switch is ON", font=font, fill=255)

draw.text((0, 22), "Temp: %.2f" %temperature, font=font, fill=255)

if temperature > 80:


draw.text((0, 44), " **ALERT**", font=font, fill=255)

disp.image(image)

Lesson 16 – OLED I2C Display Page 492

Prepared exclusively for [email protected] Transaction: 5095


STEP #10
Run the program. Change the switch position and its status will be updated on the
screen.

NOTE: You can carefully touch the metal case of the BMP280 IC to increase the
temperature and trigger the alert message for temperatures above 80F.

Take caution to avoid touching the electrical connections on the circuit board.
While there are no voltages present that could cause injury, however introducing
static electricity into the electrical connections could damage the BMP280 IC.

If your screen does not display as expected, double-check your program with the code
below:

import time
import Adafruit_SSD1306
import RPi.GPIO as GPIO
from PIL import Image, ImageDraw, ImageFont
import bme280

GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_UP)

disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)

disp.begin()

width = disp.width
height = disp.height
image = Image.new('1', (width, height)) # '1' converts image to 1-bit color

draw = ImageDraw.Draw(image)

font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf',18)

try:
while True:
temperature,pressure,humidity = bme280.readBME280All()
temperature = (temperature * 9/5) + 32

draw.rectangle((0,0,width,height), outline=0, fill=0)

if GPIO.input(21) == True:
draw.text((0, 0), "Switch is OFF", font=font, fill=255)
else:

Lesson 16 – OLED I2C Display Page 493

Prepared exclusively for [email protected] Transaction: 5095


draw.text((0, 0), "Switch is ON", font=font, fill=255)

draw.text((0, 22), "Temp: %.2f" %temperature, font=font, fill=255)

if temperature > 80:


draw.text((0, 44), " ** ALERT **", font=font, fill=255)
disp.image(image)
disp.display()

except KeyboardInterrupt:
disp.clear()
disp.display()
SystemExit()

Leave the circuit assembled for use in Lesson B-17.

Lesson 16 – OLED I2C Display Page 494

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. When text is drawn using the draw.text command, is it sent to the image buffer
or written directly to the screen?

2. In (64, 17), what is the value of X and Y? Approximately where is that coordinate
located on the 128x64 screen below:

3. What changes must be made to the program from Activity #3 to reorder the lines
on the screen to match the layout below?

Temp: 80.28
** ALERT **
Switch is OFF

Answers can be found on the next page.

Lesson 16 – OLED I2C Display Page 495

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. When text is drawn using the draw.text command, is it sent to the image buffer
or written directly to the screen?

ANSWER: Text drawn using the draw.text command is sent to the image
buffer.

2. In (64, 17), what is the value of X and Y? Approximately where is that coordinate
located on the 128x64 screen below:

ANSWER: X = 64, Y = 17. The exact position is noted in the image below.

Lesson 16 – OLED I2C Display Page 496

Prepared exclusively for [email protected] Transaction: 5095


3. What changes must be made to the program from Activity #3 to reorder the lines
on the screen to match the layout below?

Temp: 80.28
** ALERT **
Switch is OFF

ANSWER: While you could change the order of the lines of code in the
program, it's not necessary. The order the lines are written to the image buffer is
not important, only their Y position. By changing the Y position values of each
line, you can make the lines appear wherever you want on the screen.

if GPIO.input(21) == True:
draw.text((0, 44), "Switch is OFF", font=font, fill=255)
else:
draw.text((0, 44), "Switch is ON", font=font, fill=255)

draw.text((0, 0), "Temp: %.2f" %temperature, font=font, fill=255)

if temperature > 80:


draw.text((0, 22), " ** ALERT **", font=font, fill=255)
disp.image(image)
disp.display()

The switch status will now be displayed at Y=44, the temperature reading at
Y=0, and the ALERT at Y=22.

Lesson 16 – OLED I2C Display Page 497

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you continued to learn to work with I2C devices by learning to set up and
code for an OLED display.

In the next lesson, you will learn to work with capacitors and a capacitive touch sensor.

Lesson 16 – OLED I2C Display Page 498

Prepared exclusively for [email protected] Transaction: 5095


LESSON 17

CAPACITORS AND
CAPACITIVE TOUCH SENSORS

OBJECTIVE
In this lesson you will learn to use capacitors and a capacitive touch sensor in a circuit.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, mouse, and the internet


• Assembled Circuit from Lesson B-16
• 1 x LED
• 1 x RGB LED
• 2 x Short Male-to-Male Jumper Wires
• 1 x Long Male-to-Male Jumper Wires
• 6 x Long Male-to-Female Jumper Wires
• 1 x 220uf Capacitor
• 1 x 2200uf Capacitor
• 1 x Capacitive Touch Sensor

REVIEW CONCEPTS
If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Series and Parallel Circuits, Ohms Law, Forward Voltage (Lesson A-3)

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 499

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson you will learn how capacitors work and how to use them in a circuit. You
will also learn about the capacitive touch sensor included in your kit and how it can be
used in future projects.

CAPACITORS
Capacitors are used to quickly store and discharge energy into other components. The
amount of energy a capacitor can store is known as its capacitance. Capacitance is
measured in a unit called a Farad, but most of the capacitors you see in projects will be
measured in millionths of Farads or micro-Farads. The symbol used to indicate micro-
Farads is uF.

The capacitance value determines how much energy can be stored in a capacitor. A
higher value indicates that it can store more energy, so a 2200uF capacitor can store
around 10 times more energy than a 220uF capacitor.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 500

Prepared exclusively for [email protected] Transaction: 5095


CONSTRUCTION
Capacitors store energy by using two foil plates separated by an insulating material like
paper or plastic. In tiny capacitors this may appear like a tiny foil and plastic sandwich,
but in larger capacitors the sandwich is rolled up. This helps minimize the horizontal
size of the component:

Smaller capacitors are not often polarized so they can be used in a circuit in either
direction. Larger capacitors, known as electrolytic, are polarized and must be placed in
the circuit properly to avoid damage to components. The negative of an electrolytic
capacitor will be marked with minus signs and often a large gray band.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 501

Prepared exclusively for [email protected] Transaction: 5095


CURRENT LIMITING
A capacitor will try to charge or discharge as fast as the electrons can move in or out of
the device. When initially connected to a supply voltage, a fully discharged capacitor will
act like a short circuit to your voltage source. Current will rush in to charge the capacitor
as fast as possible, and if this amount of current exceeds what your power supply can
deliver, the supply could be damaged.

The same type of damage could occur during the discharge of a capacitor. If you
connect a full charged capacitor directly to a low GPIO pin, the grounded GPIO pin will
attempt to provide a current path for the charge inside the capacitor. If the current in or
out of a GPIO pin exceeds 16mA, then it could be permanently damaged.

Instead, a current limiting resistor should be placed in series with the capacitor. Using
the supply voltage and the value of the series resistor, you can then calculate the
maximum amount of current that will be allowed to flow in and out of that GPIO pin.

You calculated current using a series resistor with an LED back in Lesson A-3, but
calculating current in a series resistor attached to a capacitor is slightly different. Unlike
an LED, a capacitor will not create a voltage drop in a circuit, so it's Vforward is

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 502

Prepared exclusively for [email protected] Transaction: 5095


essentially zero. This means the entire source voltage will be dropped across the
resistor.

Below, an LED drops 2.8V of the 3V supply, leaving only 0.2V to be dropped across the
resistor. Ohms law shows that a 220-Ohm resistor in this configuration will limit the LED
current to around 0.9 mA:

Here is the same circuit, but the LED has been replaced with a capacitor. Since the
capacitor has no Vforward value, or zero, the entire supply voltage will be consumed by
the resistor:

This results in much higher current running through the circuit despite both circuits using
identical supply voltage levels and current limiting resistors. This is why a 1K-Ohm
resistor will be used as a current limiter in the upcoming activities:

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 503

Prepared exclusively for [email protected] Transaction: 5095


GPIO voltage / Current limiting resistor = Current running through GPIO pin

3.3V / 1K-Ohm = 3.3mA

This will limit current flowing through the GPIO pin to 3.3mA, which is safely below the
limit of 16mA.

CHARGE TIME
Charge time is the amount of time that it takes a capacitor to fully charge. This value will
be affected by factors like the capacitance value and the voltage level being used to
charge the capacitor.

Assuming a charging voltage of 3.3V, a 2200uF capacitor will take longer to charge than
a smaller 220uF capacitor. The larger capacitor will take longer to charge, but once it's
charged, it can put out much more energy than the smaller capacitor.

A capacitor that is allowed to fully charge will reach very near the voltage it's being
charged with. A capacitor being charged by 3.3V will likely reach somewhere between
3.25 and 3.29V.

CALCULATING CHARGE TIME


The time required to fully charge a capacitor can be computed by using a value called
the RC time constant. One RC time constant is the amount of time required for a
capacitor to charge to 63%, and five RC time constants are generally considered to be
the time required to fully charge. The RC time constant for a circuit can be computed by
using the following formula:

Series Resistor in Ohms X Capacitor in Farads = 1 RC Time Constant in Seconds

Since a micro-Farad is one-millionth of a Farad, the uF value of your capacitor must be


converted to its Farad value by dividing by 1000000:

2200uF / 1000000 = 0.0022F

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 504

Prepared exclusively for [email protected] Transaction: 5095


If a 1K-Ohm resistor is used with the 2200uF capacitor, the amount of time of one RC
can be calculated by:

1000 Ohms X .0022 Farads = 2.2 Seconds

This means that it will take 2.2 seconds for a 2200uF capacitor to charge to 63% of the
supply voltage when in series with a 1K-Ohm resistor. To compute the time required to
fully charge the capacitor, this single RC value would be multiplied by 5:

2.2 Seconds X 5 = 11 Seconds

A 2200uF capacitor with a series resistor of 1K-Ohms will take 11 seconds to fully
charge to the supply voltage. Any less time than this and the capacitor will not be fully
charged.

Calculate the full charge time (5RC) on a 220uF capacitor with a 1K-Ohm series
resistor:

1000 Ohms X .00022 Farads = 0.22 Seconds

0.22 Seconds X 5 = 1.1 Seconds

You can see from this calculation that a 220uF will fully charge much faster than a
2200uF, in exactly 1/10th the time to be more specific.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 505

Prepared exclusively for [email protected] Transaction: 5095


As seen in the above image, at the 1 second mark the 220uF is almost fully charged,
while the 2200uF is still only charged to about 30% of its capacity. This means to utilize
the full capacity of the larger 2200uF capacitor, it must be given at least 11 seconds to
fully charge.

While it might be nice to give the 2200uF capacitor a full 11 seconds to charge, as you
can see from the graph above, after about 6 seconds it has charged to 95% capacity.
95% capacity will likely be more than enough for any projects you plan to work on using
capacitors. The chart below shows a capacitor's charge level at each time constant:

Time Constant Percentage of Maximum Charge


1RC 63.2%
2RC 86.5%
3RC 95.0%
4RC 98.2%
5RC 99.3%

Most of the charge is stored during the first couple of RC time constants. After that,
each additional RC time constant adds less and less charge. For this reason, in the
activities for this lesson, you will only allow the capacitor to charge for 6 seconds. The
RC value for a 2200uF/1K-Ohm pair is 2.2 seconds. Allowing the capacitor to charge for
6 seconds is just short of the 3RC value of 6.6 seconds, but this will be more than
enough for the purpose of the upcoming activities.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 506

Prepared exclusively for [email protected] Transaction: 5095


PARALLEL VS SERIES
Capacitors are exactly the opposite of resistors when it comes to calculating the total
value of components in series versus parallel.

With resistors wired in series, their total value is all individual values added together:

R1 + R2 + R3 + … = RT

1K-Ohm + 1K-Ohm + 1K-Ohm = 3K-Ohms

Capacitors must be wired in parallel to achieve this same effect:

C1 + C2 + C3 + … = CT

220uF + 220uF + 220uF = 660uF

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 507

Prepared exclusively for [email protected] Transaction: 5095


In Activity #2 you will build a program that will measure the amount of time that two
capacitors in parallel take to discharge. Since these capacitors will be connected in
parallel, their two values are added together to determine the total capacitance:

220uF + 2200uF = 2420uF

The 2200uF will be removed from the circuit and its removal from the total capacitance
will result in a drastic change in the measured discharge time.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 508

Prepared exclusively for [email protected] Transaction: 5095


CAPACITIVE TOUCH SENSOR
The Capacitive Touch Sensor (CTS) in your kit measures the capacitance of four
touchpads, and will toggle four output lines high or low based on which pads are being
touched. Output lines will be low until a pad is touched, at which point the corresponding
output will go high.

This model of CTS can run from 3.3V supplied by your Raspberry Pi. This means that
the output lines from the CTS will not exceed 3.3V, making them safe to connect directly
to GPIO pins on your Pi.

When 3.3V and ground are initially applied to the CTS, it will determine the baseline
capacitance of the four input pads. When you touch a pad after this initialization
process, your skin will change the capacitance that is measured at the pad, and the
output line for that pad will be pushed high.

The CTS uses the baseline measurement to determine when a pad is touched or not. If
you happened to be touching a pad when the CTS first turned on, your skin capacitance
would change the baseline value, and now the CTS can no longer determine if a pad
has been touched. To avoid this problem, ensure that you are not touching the pads
when the CTS is initially powered on.

There is not much to working with the CTS as an input for your programs. It handles the
capacitive touch sensing internally, so you end up with four outputs that switch between
high and low, just like any other switch input.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 509

Prepared exclusively for [email protected] Transaction: 5095


MALE-TO-FEMALE JUMPER WIRES
Your Level A kit included male-to-male jumper wires with pins on each end for making
connections between locations in the breadboard. You Level B kit included male-to-
female jumper wires with a pin on one and a socket on the other. These jumpers can be
used as "extensions" to add space between devices that would normally plug directly
into the breadboard.

The capacitive touch sensor, for example, has pins coming out the top of the circuit
board. It would need to be turned over to be inserted into the breadboard, at which point
you could no longer see the signal names, or the touchpad numbers. The male-to-
female jumper wires included in your kit make it possible to connect the CTS to the
breadboard, while still allowing you to read the markings on the top of the circuit board.

Anything that would normally be directly connected to the breadboard could be


connected remotely using the male-to-female jumper wires. This includes the RFID
reader, the BMP280 temperature sensor, the ultrasonic range sensor, or even
something as small as an LED.

GPIO PIN LEVEL SENSING

As you have seen throughout various projects, GPIO inputs are usually high or low to
trigger different events in a program. Sometimes there are voltages that are slowly
transitioning from high to low, like a capacitor going from charged to discharged.

Connecting this capacitor to a GPIO input through a current limiting resistor, will allow
you to watch how slowly this high-to-low process occurs. The input will start out as a
high since the capacitor is charged, and then as the capacitor slowly discharges, the
GPIO pin will eventually register as a low. In the Raspberry Pi this happens right around
1.4V. Voltage levels above 1.4V will register as high, and anything below 1.4V will
register as low.

The sensing of this high-to-low point will be a key part of the program that you create in
Activity #2 that will time the discharge of capacitors from charged down to 1.4V.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 510

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will build a circuit that will demonstrate how capacitors
store charge. You will also connect a capacitor circuit to a GPIO pin of your Raspberry
Pi and write a program that will measure and display discharge time, while modifying
the capacitance of the circuit to observe changes in the discharge time. Finally, you will
incorporate the capacitive touch sensor and create a simple program that will use its
inputs to control the RGB LED.

ACTIVITY #1 – POWERING A CIRCUIT USING A CAPACITOR


In this activity you will build a small LED circuit that will be powered by 3.3V. You will
then add a capacitor in parallel that will charge while the LED is illuminated. At that point
you will remove 3.3V from the circuit and watch the energy stored in the capacitor
continue to power the LED.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 511

Prepared exclusively for [email protected] Transaction: 5095


STEP #1
Power off your Raspberry Pi so changes can be made to the breadboard. Using the
circuit that you built in Lesson B-16 as a starting point, remove the two short jumper
wires that are relaying I2C communication between the Temperature Sensor and the
Display. Disconnect the remaining I2C communication lines from the Temperature
Sensor and relocate them to the Display:

SDA – relocate from F37 to E46

SCL – relocate from F36 to E45

When completed, your circuit will look like this:

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 512

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The Temperature Sensor will no longer be required, so it can be removed from the
circuit. Remove the Temperature Sensor and its power wires from the circuit:

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 513

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Next, you will build a small LED circuit that will be powered by 3.3V from the Raspberry
Pi. Add an LED, (2) 1K-Ohm resistors, and two short jumper wires to the circuit in the
locations noted below:

LED – cathode in H61 and anode in H59

1K-Ohm resistor – between F51 and F55

1K-Ohm resistor – between G55 and G59

Short jumper wire – 3.3V – between P1-52 and H51

Short jumper wire – GND – between N1-61 and F61

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 514

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
Power your Raspberry Pi back on. Since the LED circuit is connected directly between
3.3V and GND, the LED will turn on as soon as power is applied to the Raspberry Pi.
Carefully remove and reinsert the 3.3V jumper wire at P1-52.

With the jumper wire removed, the circuit no longer has a power source, and the LED
will turn off instantly. With it inserted, the LED circuit will receive power, and the LED will
turn back on.

ENSURE THE CAPACITOR IS CORRECTLY POLARIZED BEFORE INSERTING OR


YOUR COMPONENTS COULD BE DAMAGED! Carefully insert the 2200uF capacitor
into J55 and J61, with the negative of the capacitor in J61. The negative side will be
marked with a gray band:

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 515

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
The capacitor will begin to charge as soon as it's inserted into the circuit, but the LED
circuit in parallel with the capacitor will be unaffected.

Carefully remove the 3.3V jumper wire between P1-52 and H51. 3.3V will no longer be
supplied by the Raspberry Pi but the LED will remain on, temporarily running on the
energy stored in the capacitor. You can re-install and remove the jumper wire if you
would like to watch the charge/discharge cycle again.

STEP #6
Power down the Raspberry Pi so additional changes can be made to the circuit.

The circuit will now be modified so the capacitor circuit can be charged by a GPIO pin.
Remove the 1K-Ohm resistor and LED that are in in parallel with the capacitor. Keep
the 1K-Ohm resistor handy as you will be inserting it into a new location in the next step.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 516

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Next, you will add the 220uF capacitor and 1K-Ohm load resistor to complete the
capacitor circuit. Temporarily remove the 2200uF capacitor from the circuit to allow for
easier access to add these new parts to your circuit. Add new components between the
locations specified below:

220uF capacitor – between H55 and H61. Ensure that negative of capacitor is inserted
into H61

1K-Ohm resistor – between G55 and G61.

Reinstall 2200uF capacitor between J55 and J61 (negative), after the components
above are installed.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 517

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
The last circuit modification will be to connect the 1K-Ohm current limiting resistor to
GPIO20. Connect the following points:

Long jumper wire – between J19 and H51

STEP #9
Double-check all component placement and capacitor polarity with the diagram in Step
#5, and then power on the Raspberry Pi. Once the Raspberry Pi is fully booted,
continue to the next activity.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 518

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – MEASURING CAPACITOR DISCHARGE TIME
In this activity you will build a program that will charge your capacitor circuit, and then
measure the time it takes for the capacitor network to discharge from 3.3V to around
1.4V. The measured discharge time will be displayed in the console.

STEP #1
Open Thonny and save a new program to the Desktop called discharge_time. This
program will use the RPi.GPIO and time modules, as well as the BCM pin mode. You
will also use a variable named pin to hold the GPIO pin number that will be used to
measure your capacitors. Set these up at the beginning of the program:

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)
pin = 20

NOTE: By referring to pin instead of 20 throughout the rest of the program, you
can easily reconfigure this program to use a different pin later just by modifying
this one value. If you used 20 throughout the program, you would have to modify
every instance of this value if you wanted to reconfigure the program to use
another GPIO pin.

STEP #2
Next, use a try/except loop that will catch keyboard exceptions and run a
GPIO.cleanup. The try: loop should also contain a while True: loop so your main
block of code will continue looping. Add the following code to the end of your program:

GPIO.setmode(GPIO.BCM)
pin = 20

try:
while True:

except KeyboardInterrupt:
GPIO.cleanup()

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 519

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
Now that the main framework of the program is in place, add the program inside the
while True: loop. The first step in the main program loop will be to push GPIO20 high
for 3 seconds to charge the capacitors. The value of pin will be used to configure
GPIO20 as an output, push GPIO20 high, and then wait for 3 seconds while the
capacitors charge:

try:
while True:
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, GPIO.HIGH)
time.sleep(3)

except KeyboardInterrupt:

STEP #4
The capacitors have been given time to charge from the 3.3V supplied by GPIO20. It's
now time to switch GPIO20 to an input, store the current time as time_start, and hold
the program in a while loop as long as GPIO20 is still high by using the pass command:

try:
while True:
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, GPIO.HIGH)
time.sleep(3)
GPIO.setup(pin, GPIO.IN)
time_start = time.time()
while GPIO.input(pin) == GPIO.HIGH:
pass

except KeyboardInterrupt:

This will cause the program to stay in the while loop until GPIO20 transitions to a low
state. The pass command is used as a placeholder and tells the program that you do
not wish for anything to happen inside this while loop. You're only using it to delay the
program right up until GPIO transitions low, and then the rest of the program will
execute.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 520

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
The while loop has now held the program in place until the capacitors discharge enough
for GPIO20 to register as a low. Once this occurs you want to store the current time as
time_stop and create a variable named timer that's equal to time_stop -
time_start. This will find the difference between the start and stop times, and store it
in the variable named timer that will later be printed:

try:
while True:
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, GPIO.HIGH)
time.sleep(3)
GPIO.setup(pin, GPIO.IN)
time_start = time.time()
while GPIO.input(pin) == GPIO.HIGH:
pass
time_stop = time.time()
timer = time_stop - time_start

except KeyboardInterrupt:

STEP #6
Now that the discharge time has been calculated and stored as timer, print the first 3
decimal places of that value using % formatting, along with the word seconds. Add the
print statement below to the end of your main loop:

try:
while True:
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, GPIO.HIGH)
time.sleep(3)
GPIO.setup(pin, GPIO.IN)
time_start = time.time()
while GPIO.input(pin) == GPIO.HIGH:
pass
time_stop = time.time()
timer = time_stop - time_start
print('%.3f seconds' % timer)

except KeyboardInterrupt:

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 521

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Run the program and watch the discharge times that are reported in the console. With
both capacitors in parallel their capacitance adds together, so you have 220uF +
2200uF or 2420uF of total capacitance, resulting in a discharge time of around 0.840
seconds.

You can drastically reduce the total capacitance by removing the 2200uF capacitor from
the circuit. With your program running and displaying discharge values, remove the
2200uF capacitor and watch what happens to the discharge time.

NOTE: The times reported by your circuit will not match these exactly due to
slight differences in AC power supply voltages and variations in capacitors.

The average values went from around 0.840 seconds with 2420uF of total capacitance
to around 0.084 seconds with only 220uF of total capacitance. This shows you how
much more energy storage capacity is added with the 2200uF capacitor:

With only 220uF of energy storage, the circuit discharged from 3.3V down to around
1.4V in 0.084 seconds. By adding the 2200uF capacitor and increasing the total
capacitance to 2420uF, it took 10x as long to discharge the capacitors from 3.3V down
to 1.4V.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 522

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – USING THE CAPACITIVE TOUCH SENSOR
In this activity you will add the capacitive touch sensor to the circuit from Activity #2 and
create a program that will use input from touch pads 1 and 3 to control the red, green,
and blue elements of an RGB LED.

STEP #1
Shut down the Pi and disconnect power before proceeding.

Once the shutdown is complete, remove the capacitor experimentation circuit from your
breadboard. This includes the long jumper wire connected to GPIO20. When
completed, your breadboard should look like this:

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 523

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Next, add the capacitive touch sensor using the female to male jumper wires. Make
each of these connections one at a time to avoid connecting anything improperly. The
six connections to the capacitive touch sensor are listed below:

Part Required Capacitive Touch Signal Breadboard Connection


Long M/F Jumper Wire VCC C9
Long M/F Jumper Wire GND C5
Long M/F Jumper Wire OUT4 C4
Long M/F Jumper Wire OUT3 C6
Long M/F Jumper Wire OUT2 C7
Long M/F Jumper Wire OUT1 C8

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 524

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The capacitive touch sensor is now connected and pads 1-4 can be used to trigger
inputs connected to GPIO22, GPIO27, GPIO17, and GPIO4. You will now create a
program that will use pads 1-3 to control the RGB elements connected to GPIO13,
GPIO19, and GPIO26.

Power on your Raspberry Pi and open Thonny. Create a new program called
cap_touch.py and save it to the Desktop.

STEP #4
This program will interact with GPIO pins and will use a time.sleep delay, so the
RPi.GPIO and time modules will need to be imported.

import RPi.GPIO as GPIO, time

STEP #5
Next, the GPIO pins connected to the touchpad will need to be configured as active-
high inputs (software pull-down required), and the GPIO pins connected to the RGB
LED will need to be configured as outputs.

To make this task easier, use some lists to hold the pin information so setup can be
completed in two lines instead of six. Use a variable named pads to hold [22,27,17]
and a variable named rgb to hold [13,19,26]. Set the pin mode to BCM and use the
lists above to configure the inputs and outputs:

import RPi.GPIO as GPIO, time

pads = [22,27,17]
rgb = [13,19,26]
GPIO.setmode(GPIO.BCM)
GPIO.setup(pads, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(rgb, GPIO.OUT)

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 525

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
Add a try/except loop to catch keyboard exceptions and run a GPIO.cleanup. Also, add
a while True: to the try: block so the main program block will keep looping:

GPIO.setup(pads, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)


GPIO.setup(rgb, GPIO.OUT)

try:
while True:

except KeyboardInterrupt:
GPIO.cleanup()

STEP #7
Now, the main program will need to check each of the pads, and if they've gone high
indicating they've been touched, turn on an element of the RGB LED. U the following
mapping of inputs and outputs:

Input Output RGB Element Color


GPIO22 GPIO13 red
GPIO27 GPIO19 green
GPIO17 GPIO26 blue
The first line for example, means that touching the pad connected to GPIO22 should
push GPIO13 high, turning on the red element of the RGB LED. You can use while:
loops to keep each element high as long as the corresponding touchpad is activated:

try:
while True:
while GPIO.input(22) == True:
GPIO.output(13, GPIO.HIGH)
while GPIO.input(27) == True:
GPIO.output(19, GPIO.HIGH)
while GPIO.input(17) == True:
GPIO.output(26, GPIO.HIGH)

except KeyboardInterrupt:

Each touchpad and RGB element combination will need its own while: loop.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 526

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
Each LED element will now turn on when its corresponding touchpad is activated, but
there is a problem. They will never turn back off. You can turn all the LED elements off
at once by using the rgb list that you created at the beginning of the program. Set the
list of pins low using rgb and add a time.sleep of 0.1 to slow the main loop down so
the touchpads aren't being checked 1000 times per second:

try:
while True:
while GPIO.input(22) == True:
GPIO.output(13, GPIO.HIGH)
while GPIO.input(27) == True:
GPIO.output(19, GPIO.HIGH)
while GPIO.input(17) == True:
GPIO.output(26, GPIO.HIGH)
GPIO.output(rgb, GPIO.LOW)
time.sleep(0.1)

except KeyboardInterrupt:
GPIO.cleanup()

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 527

Prepared exclusively for [email protected] Transaction: 5095


STEP #9
Run the program and touch each touchpad 1-3. Touching the pad will make an RGB
element turn on and releasing the pad will make that RGB element turn off. If your
program does not work as expected, confirm it matches the code below:

import RPi.GPIO as GPIO, time

pads = [22,27,17]
rgb = [13,19,26]
GPIO.setmode(GPIO.BCM)
GPIO.setup(pads, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(rgb, GPIO.OUT)

try:
while True:
while GPIO.input(22) == True:
GPIO.output(13, GPIO.HIGH)
while GPIO.input(27) == True:
GPIO.output(19, GPIO.HIGH)
while GPIO.input(17) == True:
GPIO.output(26, GPIO.HIGH)
GPIO.output(rgb, GPIO.LOW)
time.sleep(0.1)

except KeyboardInterrupt:
GPIO.cleanup()

Leave the circuit assembled for use in Lesson B-18.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 528

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Will a capacitive touch sensor initialize properly if you are touching one of the
pads when it first turns on?

2. To yield the greatest total capacitance of two or more capacitors, should they be
connected in series or parallel?

3. At around what voltage does the Raspberry Pi sense a high signal (3.3V)
transitioning to a low (0V)?

Answers can be found on the next page.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 529

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Will a capacitive touch sensor initialize properly if you are touching one of the
pads when it first turns on?

ANSWER: No, when first plugged in the capacitive touch sensor must establish
a baseline. If you are touching it at the time it is turned on, that will be the
baseline.

2. To yield the greatest total capacitance of a network of two or more capacitors,


should they be connected in series or parallel?

ANSWER: The greatest total capacitance of a network with multiple capacitors


is available by connecting them in parallel.

3. At around what voltage does the Raspberry Pi sense a high signal (3.3V)
transitioning to a low (0V)?

ANSWER: At around 1.4V the Raspberry Pi will begin to recognize the signal
as low. Anything above 1.4V will generally be read as high and anything below
1.4V will generally be read as low.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 530

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
In this lesson you learned about capacitors and how to work with a capacitive touch
sensor.

The next lesson is the final lesson for Level B. In that lesson you will learn how to use a
voltage divider to do simple signal level shifting, the absolute value function, and how to
modify a file so it can be used as an import for another program. You will then move on
to the final project for this course.

Lesson 17 – Capacitors and Capacitive Touch Sensors Page 531

Prepared exclusively for [email protected] Transaction: 5095


LESSON 18

RANGE SENSING GAME

OBJECTIVE
In this lesson you will learn how to use a voltage divider to do simple signal level
shifting, the absolute value function, and how to modify a file so it can be used as an
import for another program. You will then move on to the final project for this course.

MATERIALS

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Assembled Circuit from Lesson B-17
• 1 x Ultrasonic Range Sensor
• 2 x 1k-ohm Resistors
• 2 x Long Jumper Wires
• 5 x Short Jumper Wires

REVIEW CONCEPTS

If you do not feel comfortable with the following concepts, please review them before
proceeding.

• Slide Switch (Lesson B-5)


• Voltage Dividers (Lesson B-9)
• Level Shifting Integrated Circuit (Lesson B-13)
• Ultrasonic Range Sensing (Lesson B-14)
• Running Modules as Imports vs. Directly (Lesson B-15)
• OLED Display (Lesson B-16)
• Capacitive Touch Sensors; GPIO High vs. Low (Lesson B-17)

Lesson 18 – Range Sensing Game Page 532

Prepared exclusively for [email protected] Transaction: 5095


LESSON
In this lesson you will learn how to use a voltage divider to do simple signal level
shifting, the absolute value function, and how to modify a file so it can be used as an
import for another program. You will then move on to build the final project.

LEVEL SHIFTING WITH RESISTORS


In Lesson B-9 you learned how voltage dividers can be used to create new voltage
levels. This same principle can also be used for level shifting from a higher voltage to a
lower voltage.

The following voltage divider with two equal value 1K-ohm resistors will cut the supplied
5V signal in half:

The great thing about this circuit is that the 5V signal does not have to come from a
constant supply. It can also be connected to the output from a 5V device, like the
Ultrasonic Range Sensor, whose 5V output is not safe to connect directly to a GPIO pin.
By using the output of the Ultrasonic Range Sensor to supply the input voltage, the
output will be 2.5V which is safe to connect to a GPIO pin.

Lesson 18 – Range Sensing Game Page 533

Prepared exclusively for [email protected] Transaction: 5095


By watching the voltage divider output pin with the GPIO, you can determine if the 5V
signal is present or not:

• 2.5V present means the Ultrasonic Range Sensor is applying 5V to the input
• 0V present means the Ultrasonic Range Sensor is not applying 5V to the input

You learned in Lesson B-17 that a GPIO pin will register as high for any voltage above
1.4V. So a 2.5V level will easily trigger a high in the GPIO pin:

• GPIO high means the Ultrasonic Range Sensor is applying 5V to the input
• GPIO low means the Ultrasonic Range Sensor is not applying 5V to the input

Using a voltage divider can be a very useful way to quickly reduce the voltage from a
sensor, using only two resistors. When using several 5V sensors, it makes more sense
to use a device like the 74LVC245 (Level Shifting) IC instead, due the amount and
complexity of resistors that would be required to make a voltage divider for each
channel.

Lesson 18 – Range Sensing Game Page 534

Prepared exclusively for [email protected] Transaction: 5095


ABSOLUTE VALUE IN PYTHON
An absolute value represents how far a value is away from zero. It essentially removes
the positive or negative sign of a value, so the absolute value of -5 is 5. The same goes
for the positive value. The absolute value of 5 is still just 5. The absolute value function
in Python is:

abs()

The absolute value of will be taken of anything inside the parentheses:

abs(-23) becomes 23

abs(42) becomes 42

The second example doesn't seem to be very useful, but what if you don’t know what
the value in the parentheses will be until your program starts running:

abs(x-y)

If x is greater than y then the abs() function will have no effect on the value that this
function outputs. However, if y is greater than x, the result of x-y will be negative and
abs() will strip off the sign from the negative number.

In the upcoming activity, you will build a game that will be taking the difference of two
numbers, without knowing which will be larger. The abs() function will be used to strip
the sign from the result, leaving only the positive value of the difference between the
two values.

Lesson 18 – Range Sensing Game Page 535

Prepared exclusively for [email protected] Transaction: 5095


MODIFYING A FILE FOR IMPORT USE
When you import a file, that file is completely run from beginning to end. Any imports,
variable assignments, function definitions, or anything else in that program, will run as if
those lines were included in your program.

What if a file that you choose to import includes the following:

def thing1():
x = 1

while True:
print('Hello World')

On import, the imported file would define the function named thing1(), and then get
stuck in the while True: loop, never returning to your program. This is obviously not
ideal, and this is why back in Lesson B-15, you learned about the __name__ variable
that can be used to determine if a file has been imported, or has been executed directly:

if __name__=="__main__":

Any code indented below this if condition will only run when the file was run directly
and will be ignored if the file is being executed as an import to another file. You can
modify the earlier example to allow for both imported and direct execution:

def thing1():
x = 1

if __name__=="__main__":
while True:
print('Hello World')

Importing the file will now result in the code inside the if block being completely ignored,
and when the program is executed directly, it will run from top to bottom, including
everything contained in the if block.

In the following activities, the ultrasonic range sensing program that you created in
Lesson B-14 will be modified so its function definitions can be used for import, without
executing the main program it contains.

Lesson 18 – Range Sensing Game Page 536

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITIES
In the following activities you will modify the ultrasonic.py program you created in
Lesson B-14, add the Ultrasonic Range Sensor to the circuit you built in Lesson B-17,
and create a game program that uses the Ultrasonic Range Sensor, the OLED screen,
the Capacitive Touch Sensor, the RGB LED, and the slide switch.

ACTIVITY #1 – MODIFYING ULTRASONIC.PY


In this activity, you will modify the ultrasonic.py program that you created in Lesson
B-14 so its range sensing functions can be imported without running the main loop
program that it contains.

Lesson 18 – Range Sensing Game Page 537

Prepared exclusively for [email protected] Transaction: 5095


STEP #1
The first step is to open ultrasonic.py from your Desktop in Thonny. Once open,
scroll down to the try: loop. You want to enclose this entire try: loop inside of an if
__name__ condition to ensure that it does not run when the file is used as an import.
Add the following line above the try: block and add another level of indentation to the
try: block as well as everything below, including the except: block:

if __name__=="__main__":
try:
while True:
distance = average()
print('%.1f' % distance)
if distance < 20:
GPIO.output(red, GPIO.HIGH)
GPIO.output(green, GPIO.LOW)
GPIO.output(blue, GPIO.LOW)
elif 20 <= distance < 25:
GPIO.output(red, GPIO.LOW)
GPIO.output(green, GPIO.HIGH)
GPIO.output(blue, GPIO.LOW)
else:
GPIO.output(red, GPIO.LOW)
GPIO.output(green, GPIO.LOW)
GPIO.output(blue, GPIO.HIGH)
time.sleep(.25)

except KeyboardInterrupt:
GPIO.cleanup()

All of the gray boxes above are additional spaces that were added to realign the code
below the new if block.

STEP #2
Save your updated file so you can use it as an import later when building the game
program in Activity #3.

Lesson 18 – Range Sensing Game Page 538

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #2 – ADDING THE ULTRASONIC RANGE SENSOR
In this activity, you will add the ultrasonic range sensor to the capacitive touch circuit
you built in Lesson B-17, Activity #3.

STEP #1
Shut down the Pi and disconnect power before proceeding.

Once that's done, the first circuit modification will be to get 5V power and ground over to
the P2/N2 power rails so it can be used to power the Ultrasonic Range Sensor. Make
the following two connections using short jumper wires:

Short jumper wire – 5V – between J1 and P2-3

Short jumper wire – GND – between J3 and N2-3

Lesson 18 – Range Sensing Game Page 539

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
Next, add a voltage divider made of two 1K-Ohm resistors that will be used to level-shift
the Echo output of the ultrasonic range sensor. Add two resistors and a short jumper
wire between the points below:

1K-Ohm resistor – between G55 and G59

1K-Ohm resistor – between F51 and F55

Short jumper wire –GND - between F59 and N2-61

Lesson 18 – Range Sensing Game Page 540

Prepared exclusively for [email protected] Transaction: 5095


STEP #3
The ultrasonic.py file that will be used to capture distance readings will be looking for
echo signals on GPIO21, but the slide switch is currently in that position. Move the slide
switch connection from GPIO21 over to GPIO16:

The voltage divider will take care of bringing the Echo line from the ultrasonic range
sensor down to a safe level, but you will need to make a few more connections before
you can use it for ranges. Make the following four connections between the points listed
below:

Short jumper wire – 5V – between H53 and P2-43

Short jumper wire - GND – between H50 and N2-42

Long jumper wire – Trigger – between H52 and J19

Long jumper wire – Echo – between H55 and J20

Lesson 18 – Range Sensing Game Page 541

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
The last step is to add the ultrasonic range sensor. Connect it to the breadboard at J50
through J53 with the sensor pointing away from the OLED display. Ensure the sensor is
properly oriented and connected to the correct locations, or it could be damaged:

Power the Raspberry Pi on so it can be used to create a program to use with your new
circuit.

Lesson 18 – Range Sensing Game Page 542

Prepared exclusively for [email protected] Transaction: 5095


ACTIVITY #3 – BUILDING THE RANGE GAME
In this activity, you will build a program to create the game outlined below:

The game will ask you to place your hand or another obstacle at a pre-determined
distance from the Ultrasonic Range Sensor. The game will consist of the following
actions:

• The user selects the desired difficulty level using the slide switch. Easy mode
allows for 20cm of error while Hard mode only allows for 10cm. The program
waits 2 seconds before the position of the slide switch determines which difficulty
level will be used for that round.
• The screen displays a random target distance between 20cm and 100cm.
• The user is asked to press capacitive touch pad 1-4 to start the distance capture
process. Each pad represents the number of seconds before the capture occurs,
which can be used to add more difficulty. Less time to get ready before the
capture makes it more difficult.
• The user range is captured and compared to the target distance. The RGB LED
will turn green if the user distance was within 20cm in Easy mode, or 10cm in
Hard mode. Errors above these amounts will result in the RGB LED turning red.
• The program loops back to the beginning.

This program will be the largest you've written yet, but it will borrow large blocks from
programs in previous activities. So, don’t be intimidated! Build the program one block at
a time, just like you would with a shorter program.

STEP #1
The first step will be to open Thonny and create a new program called range_game.py.
Save the file to your Desktop so it will have access to the ultrasonic.py file that you
modified in Activity #1.

As you go through the steps below, save your program often to avoid losing an of your
work.

Lesson 18 – Range Sensing Game Page 543

Prepared exclusively for [email protected] Transaction: 5095


STEP #2
The first area in the program will be the imports and there are quite a few. You will be
using time, RPi.GPIO, random, ultrasonic, Adafruit_SSD1306, and the PIL imports
you used for the OLED display. Here are the import lines to add:

import time, RPi.GPIO as GPIO, random, ultrasonic, Adafruit_SSD1306


from PIL import Image, ImageDraw, ImageFont

STEP #3
Next, you will assign the input and output pins using a lot of variables.

This will help you later if you want to modify this program and move an input or output to
another pin. This list will contain pin assignments for the slide switch, RGB elements,
and Capacitive Touch inputs.

The lists for rgb and cap will allow those groups of pins to be configured as inputs and
outputs as a group, using only one line each:

import time, RPi.GPIO as GPIO, random, ultrasonic, Adafruit_SSD1306


from PIL import Image, ImageDraw, ImageFont

slide = 16
rgb = [13,19,26]
red = 13
green = 19
blue = 26
cap = [22,27,17,4]
cap1 = 22
cap2 = 27
cap3 = 17
cap4 = 4

Lesson 18 – Range Sensing Game Page 544

Prepared exclusively for [email protected] Transaction: 5095


STEP #4
The next step will be the GPIO configuration. Here are the configuration steps that need
to be completed:

• Set GPIO pin mode to BCM


• Configure slide as an input with pull-up
• Configure cap as an input (this will take care of all four touchpads)
• Configure rgb as an output (this will take care of all three RGB elements)
• Set the output of rgb to low or 0 (this will ensure the RGB LED is off initially in
case your program has errors and exits improperly)

Here is the code to accomplish these five tasks:

cap2 = 27
cap3 = 17
cap4 = 4

GPIO.setmode(GPIO.BCM)
GPIO.setup(slide, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(rgb, GPIO.OUT)
GPIO.setup(cap, GPIO.IN)
GPIO.output(rgb, GPIO.LOW)

Lesson 18 – Range Sensing Game Page 545

Prepared exclusively for [email protected] Transaction: 5095


STEP #5
There is one more configuration step that must be accomplished but it can't be included
with the GPIO configuration. It's the code to configure all the settings required for your
OLED display to operate.

This block of code is pulled directly from the program you created in Lesson B-16,
Activity #2, and each line of code is broken down in the section titled SSD1306 Display
Driver. If you're unsure about anything below, please refer to that section for more
information:

GPIO.setup(cap, GPIO.IN)
GPIO.output(rgb, GPIO.LOW)

disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)
disp.begin()
width = disp.width
height = disp.height
image = Image.new('1', (width, height)) # '1' converts image to 1-bit color
draw = ImageDraw.Draw(image)
font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf',18)

Lesson 18 – Range Sensing Game Page 546

Prepared exclusively for [email protected] Transaction: 5095


STEP #6
There are a couple lines of code that will be used every time the display needs to be
updated. They are disp.image(image) and disp.display().

Instead of using these two lines every time you need to update the display, create a
function named update_display() that can be called every time an update is needed:

draw = ImageDraw.Draw(image)
font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf',18)

def update_display():
disp.image(image)
disp.display()

You are now just over 30 lines into the program. Ensure your program matches the
program on the next page before continuing to the next step.

Lesson 18 – Range Sensing Game Page 547

Prepared exclusively for [email protected] Transaction: 5095


import time, RPi.GPIO as GPIO, random, ultrasonic, Adafruit_SSD1306
from PIL import Image, ImageDraw, ImageFont

slide = 16
rgb = [13,19,26]
red = 13
green = 19
blue = 26
cap = [22,27,17,4]
cap1 = 22
cap2 = 27
cap3 = 17
cap4 = 4

GPIO.setmode(GPIO.BCM)
GPIO.setup(slide, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(rgb, GPIO.OUT)
GPIO.setup(cap, GPIO.IN)

disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)
disp.begin()
width = disp.width
height = disp.height
image = Image.new('1', (width, height))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf',18)

def update_display():
disp.image(image)
disp.display()

Lesson 18 – Range Sensing Game Page 548

Prepared exclusively for [email protected] Transaction: 5095


STEP #7
Now that all the configuration steps have been completed, it's time to start building the
main program loop. Start with a try: loop that contains a while True: loop, and an
except KeyboardInterrupt: that contains the following:

disp.clear() clears the image buffer

disp.display() pushes the image buffer to display to so it's clear before shutdown

GPIO.cleanup() resets all GPIO pins to their default state

The code to add these elements is listed below:

def update_display():
disp.image(image)
disp.display()

try:
while True:

except KeyboardInterrupt:
disp.clear()
disp.display()
GPIO.cleanup()

Any new code in upcoming steps will be inserted in the while True: loop.

Lesson 18 – Range Sensing Game Page 549

Prepared exclusively for [email protected] Transaction: 5095


STEP #8
The first step in the main program loop is to display the difficulty level based on input
from the slide switch. You will control the timing of this portion of the program by looping
20 times with a 0.1 delay in each loop, for a total time of 2 seconds allowed for difficulty
selection.

Inside this skill_selection loop, you will nest two if/else conditions:

If the slide switch is low or False, then draw a rectangle to clear the display, draw the
strings "Difficulty?" and "Easy" to two lines of the image buffer, push the image
buffer to the screen, and set a variable named skill equal to 2. This variable will be
used later to determine the size of the error window that will result in a green LED.

If the slide switch is not low, the else: block will execute, just like the block above but
"Hard" will be printed to the second line of the display, and skill will be set equal to 1.

Here is the code to accomplish the program functions outlined above:

try:
while True:
for skill_selection in range(0,20):
if GPIO.input(slide) == False:
draw.rectangle((0,0,width,height), outline=0, fill = 0)
draw.text((0, 0), "Difficulty?", font=font, fill=255)
draw.text((0, 22), "Easy", font=font, fill=255)
update_display()
skill = 2
else:
draw.rectangle((0,0,width,height), outline=0, fill = 0)
draw.text((0, 0), "Difficulty?", font=font, fill=255)
draw.text((0, 22), "Hard", font=font, fill=255)
update_display()
skill = 1
time.sleep(.1)

Make sure your indentation matches the code above. This is crucial for each block of
the program to operate as expected.

Lesson 18 – Range Sensing Game Page 550

Prepared exclusively for [email protected] Transaction: 5095


STEP #9
Now that the difficulty has been selected, the random target number can be selected
and displayed on the screen to let the player know the target distance for this round.
First, set a variable named target to a random integer between 20 and 100 by using
the randon.randint() function.

Next, show the target distance on the display by using % notation to print 'Target =
%s" %target on the first line of the display. The second and third lines of the display
should read "Press pad" and "to start". An update_display() will be used to
send this new information to the display:

update_display()
skill = 1
time.sleep(.1)

target = random.randint(20,100)

draw.rectangle((0,0,width,height), outline=0, fill=0)


draw.text((0, 0), "Target = %s" %target, font=font, fill=255)
draw.text((0, 22), "Press pad", font=font, fill=255)
draw.text((0, 44), "to start", font=font, fill=255)
update_display()

Be careful again with the indentation on this section of the program. It should be at the
same indentation level as the for: loop. You want it to run after, and not as part of the
for: loop.

Lesson 18 – Range Sensing Game Page 551

Prepared exclusively for [email protected] Transaction: 5095


STEP #10
The user has now been informed of the target distance and has been prompted to press
a touchpad to start the capture process. Now hold the program in a while: loop until a
touchpad is pressed and goes high.

This can be done by using a while: loop that requires cap1, cap2, cap3, and cap4 all
to be False to keep the loop running. As soon as any of the GPIO pins connected to
those pads goes high, the loop will exit and continue with the rest of the program. A
time.sleep of 0.05 will be added inside the loop to keep the program from using too
many resources while waiting for input from a touchpad:

draw.text((0, 44), "to start", font=font, fill=255)


update_display()

while GPIO.input(cap1) == GPIO.input(cap2) == GPIO.input(cap3) ==


GPIO.input(cap4) == False:
time.sleep(0.05)

Even though the while: condition didn't fit on one line in this document, it should be
one continuous line in your code from while all the way to False:. The
time.sleep(0.05) should be indented so it runs each time the while condition is met.

Lesson 18 – Range Sensing Game Page 552

Prepared exclusively for [email protected] Transaction: 5095


STEP #11
If the while: loop stops running, that means one of the capacitive touch pads had been
triggered, but you don’t know which one.

Create some if conditions following the while: loop that will check to see if each pad
is high or True, and assign a variable named delay equal to that pad’s number. If cap1
was pressed then delay = 1, and if cap4 was pressed then delay = 4, etc.

time.sleep(.05)

if GPIO.input(cap1) == True:
delay = 1
if GPIO.input(cap2) == True:
delay = 2
if GPIO.input(cap3) == True:
delay = 3
if GPIO.input(cap4) == True:
delay = 4

Since the while: loop exited you know that one of the pads was pressed. This code will
quickly check each pad and assign the value of delay based on which pad was pressed.

Lesson 18 – Range Sensing Game Page 553

Prepared exclusively for [email protected] Transaction: 5095


STEP #12
Now that you have the selected delay time stored as a variable, you can display a
message letting the user know the target distance, and a message about when the
range will be captured. There is, however, a small problem with this plan. The string
"Capturing in X seconds" works for selections 2, 3, and 4, but not for 1. Since you
don’t want to display "1 seconds", the second line will have to be customized based on
the value of delay to maintain correct grammar.

The first two lines of the message will be very similar to the Press pad to start block
of code form Step #9. The first line will display the target distance and the second will
display "Capturing range". The third line of the display will need to be customized
using an if/else block.

If delay is 1 then the third line should be "in %i second" %delay to maintain proper
grammar for the single second. If delay is anything else, then the third line should be
"in %i seconds" %delay to properly display multiple seconds.

At the end of this block you will update the display and insert a delay equal to the value
of the delay variable by using time.sleep(delay):

if GPIO.input(cap4) == True:
delay = 4

draw.rectangle((0,0,width,height), outline=0, fill=0)


draw.text((0, 0), "Target = %s" %target, font=font, fill=255)
draw.text((0, 22), "Capturing range", font=font, fill=255)
if delay == 1:
draw.text((0, 44), "in %i second" %delay, font=font, fill=255)
else:
draw.text((0, 44), "in %i seconds" %delay, font=font, fill=255)
update_display()
time.sleep(delay)

Lesson 18 – Range Sensing Game Page 554

Prepared exclusively for [email protected] Transaction: 5095


STEP #13
The delay is over, and you are now ready to capture the range to the user. You can do
this by accessing the average() function inside your ultrasonic.py file, and setting
the result equal to a variable named distance.

Next, you will create a variable named diff that will hold the absolute value of the
random target variable minus the distance returned from the average() function.
This will be expressed as diff = abs(target-distance).

Now you will calculate the range of values that will get a green LED based on the
difficulty that's been selected. Create a variable named window that equals 10 *
skill. This means:

Easy mode, skill = 2 so window will equal 10 X 2 or 20

Hard mode, skill = 1 so window will equal 10 X 1 or 10

Add these program tasks with the following lines of code:

update_display()
time.sleep(delay)

distance = ultrasonic.average()
diff = abs(target-distance)
window = 10 * skill

STEP #14
It's now time to light the LED using the diff and window variables. If diff is smaller
than or equal to window, the user distance is good, so the LED should turn green. If not,
the user distance that round was larger than the window, so the LED should turn red.
This can be accomplished by using a simple if/else block:

distance = ultrasonic.average()
diff = abs(target-distance)
window = 10 * skill

if diff <= window:


GPIO.output(green, GPIO.HIGH)
else:
GPIO.output(red, GPIO.HIGH)

Lesson 18 – Range Sensing Game Page 555

Prepared exclusively for [email protected] Transaction: 5095


STEP #15
The program is almost done.

Finally, you will display the target and player distances, along with the difference
between the two, add a 5 second delay so there is time to view the results, and turn off
the LED so it's ready for the next round.

The display block is exactly like other blocks above it:

• Blank the image buffer with a black rectangle


• Line 1 will display "Target =" along with the value of target
• Line 2 will display "Player =" along with the value of distance
• Line 3 will display "Diff =" along with the value of diff
• Update the display

You will use time.sleep(5) for the delay and the rgb list to turn off all LED elements:

else:
GPIO.output(red, GPIO.HIGH)

draw.rectangle((0,0,width,height), outline=0, fill=0)


draw.text((0, 0), "Target = %s" %target, font=font, fill=255)
draw.text((0, 22), "Player = %.0f" %distance, font=font, fill=255)
draw.text((0,44), "Diff = %.0f" %diff, font=font, fill=255)
update_display()
time.sleep(5)
GPIO.output(rgb, GPIO.LOW)

Lesson 18 – Range Sensing Game Page 556

Prepared exclusively for [email protected] Transaction: 5095


STEP #16
Double check your program and indentation against the code below:

import time, RPi.GPIO as GPIO, random, ultrasonic, Adafruit_SSD1306


from PIL import Image, ImageDraw, ImageFont

slide = 16
rgb = [13,19,26]
red = 13
green = 19
blue = 26
cap = [22,27,17,4]
cap1 = 22
cap2 = 27
cap3 = 17
cap4 = 4

GPIO.setmode(GPIO.BCM)
GPIO.setup(slide, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(rgb, GPIO.OUT)
GPIO.setup(cap, GPIO.IN)

disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)
disp.begin()
width = disp.width
height = disp.height
image = Image.new('1', (width, height)) # '1' converts image to 1-bit color
draw = ImageDraw.Draw(image)
font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf',18)

def update_display():
disp.image(image)
disp.display()

try:
while True:
for skill_selection in range(0,20):
if GPIO.input(slide) == False:
draw.rectangle((0,0,width,height), outline=0, fill = 0)
draw.text((0, 0), "Difficulty?", font=font, fill=255)
draw.text((0, 22), "Easy", font=font, fill=255)
update_display()
skill = 2
else:
draw.rectangle((0,0,width,height), outline=0, fill = 0)
draw.text((0, 0), "Difficulty?", font=font, fill=255)
draw.text((0, 22), "Hard", font=font, fill=255)
update_display()
skill = 1
time.sleep(.1)

target = random.randint(20,100)

Lesson 18 – Range Sensing Game Page 557

Prepared exclusively for [email protected] Transaction: 5095


draw.rectangle((0,0,width,height), outline=0, fill=0)
draw.text((0, 0), "Target = %s" %target, font=font, fill=255)
draw.text((0, 22), "Press pad", font=font, fill=255)
draw.text((0, 44), "to start", font=font, fill=255)
update_display()

while GPIO.input(cap1)==GPIO.input(cap2)==GPIO.input(cap3)==GPIO.input(cap4)==False:
time.sleep(.05)

if GPIO.input(cap1) == True:
delay = 1
if GPIO.input(cap2) == True:
delay = 2
if GPIO.input(cap3) == True:
delay = 3
if GPIO.input(cap4) == True:
delay = 4

draw.rectangle((0,0,width,height), outline=0, fill=0)


draw.text((0, 0), "Target = %s" %target, font=font, fill=255)
draw.text((0, 22), "Capturing range", font=font, fill=255)
if delay == 1:
draw.text((0, 44), "in %i second" %delay, font=font, fill=255)
else:
draw.text((0, 44), "in %i seconds" %delay, font=font, fill=255)
update_display()
time.sleep(delay)

distance = ultrasonic.average()
diff = abs(target-distance)
window = 10 * skill

if diff <= window:


GPIO.output(green, GPIO.HIGH)
else:
GPIO.output(red, GPIO.HIGH)

draw.rectangle((0,0,width,height), outline=0, fill=0)


draw.text((0, 0), "Target = %s" %target, font=font, fill=255)
draw.text((0, 22), "Player = %.0f" %distance, font=font, fill=255)
draw.text((0,44), "Diff = %.0f" %diff, font=font, fill=255)
update_display()
time.sleep(5)
GPIO.output(rgb, GPIO.LOW)

except KeyboardInterrupt:
disp.clear()
disp.display()
GPIO.cleanup()

Lesson 18 – Range Sensing Game Page 558

Prepared exclusively for [email protected] Transaction: 5095


STEP #17
Now that your program is complete, run the program. Select the difficulty by using the
slide switch and start the capture process by pressing one of the Capacitive Touch
pads.

If your program is not working as expected, identify which area of the program needs to
be checked. Each block of code is performing a very specific function, so identify what
part of the game is not working properly and check out the block of code that is
controlling that behavior.

If you still can't get your program to work, you can download a copy of this program from
the Level B Resource Page.

Lesson 18 – Range Sensing Game Page 559

Prepared exclusively for [email protected] Transaction: 5095


QUESTIONS FOR UNDERSTANDING
1. Does signal level-shifting require an IC or can it be done with two resistors?

2. Can every file be used an import without causing any problems in your main
program?

3. What function can be used to take the absolute value of a variable or


expression?

Answers can be found on the next page.

Lesson 18 – Range Sensing Game Page 560

Prepared exclusively for [email protected] Transaction: 5095


ANSWERS TO THE QUESTIONS FOR UNDERSTANDING
1. Does signal level-shifting require an IC or can it be done with two resistors?

ANSWER: Signal level shifting can be done with two resistors but if multiple 5V
devices are being used, using an IC for level shifting is recommended.

2. Can every file be used an import without causing any problems in your main
program?

ANSWER: No. Everything in the imported file will run on import unless it's inside
an if __name__=="__main__": condition. Loops or other types of code in
the imported file could cause problems when imported and must be enclosed in
this if: condition to isolate it during your import.

3. What function can be used to take the absolute value of a variable or


expression?

ANSWER: The abs() function will return the absolute value of a variable or
expression.

Lesson 18 – Range Sensing Game Page 561

Prepared exclusively for [email protected] Transaction: 5095


CONCLUSION
Congratulations! You have completed Level B of this course. You have learned a great
number of new skills and you should be proud of yourself!

What’s next?

• Order a copy of Level C of this course to continue to learn to work with more
electronic components and coding commands (available Spring 2019).

• The skills you have gained and the components you have amassed working with
Level A and Level B, have put you in a great position to tackle beginner and
intermediate projects online. We recommend searching for projects that use both
the Raspberry Pi and Python. You are likely to find that you have worked with
much of the code and components that will be called for in the projects, and
those you haven’t, you likely have the skills to figure out.

Intro to Robotics Course of Study:


Level A: Building Circuits and Beginning Programming

Level B: Working with Sensors and Intermediate Programming

Level C: Audiovisual and Advanced Programming NEXT STEP

Level D: Working with Motors and Taking It Mobile

Lesson 18 – Range Sensing Game Page 562

Prepared exclusively for [email protected] Transaction: 5095


QUICK REFERENCES

Page 563

Prepared exclusively for [email protected] Transaction: 5095


QUICK REFERENCE:

COMPONENTS

OVERVIEW
The supply kit for this course will be shipped to you within several days of your order.
Here are a few things to keep in mind:

• Specific instructions for using all kit components will be included in the lessons.
For a few parts, you’ll find notes in this overview for how to get the part ready to
use, etc.

• Leave all components in their packages until you need them for a lesson. This
keeps them protected.

While all components for circuit building needed for the lessons are included in the kit, if
you wish to purchase extra parts or replacement parts, please visit:
www.42electronics.com/products/replacement-parts.

PIEZO SPEAKER

Piezo Speaker

Qty. 1

Level B – Quick Reference: Components Page 564

Prepared exclusively for [email protected] Transaction: 5095


SLIDE SWITCH

Slide Switch

Qty. 1

3X4 MATRIX KEYPAD

3x4 Matrix Keypad

Qty. 1

Level B – Quick Reference: Components Page 565

Prepared exclusively for [email protected] Transaction: 5095


3X4 KEYPAD HEADER

Keypad Header

Qty. 1

RFID READER

RFID Reader

Qty. 1

RFID TAG & CARD

RFID Tag & Card

Qty. 2

Level B – Quick Reference: Components Page 566

Prepared exclusively for [email protected] Transaction: 5095


ANALOG-TO-DIGITAL CONVERTOR

Analog-to-Digital Convertor

Qty. 1

10K POTENTIOMETER WITH KNOB

10k Potentiometer with Knob

Qty. 1

PHOTOTRANSISTOR

Phototransistor

Qty. 1

Level B – Quick Reference: Components Page 567

Prepared exclusively for [email protected] Transaction: 5095


LEVEL SHIFTING INTEGRATED CIRCUIT

Level Shifting Integrated Circuit

Qty. 1

INFRARED OBSTACLE SENSOR

Infrared Obstacle Sensor

Qty. 1

ULTRASONIC RANGE SENSOR

Ultrasonic Range Sensor

Qty. 1

Level B – Quick Reference: Components Page 568

Prepared exclusively for [email protected] Transaction: 5095


INFRARED LINE SENSOR

Qty. 1

TEMPERATURE SENSOR

Qty. 1

OLED DISPLAY

Qty. 1

CAPACITIVE TOUCH SENSOR

Qty. 1

Level B – Quick Reference: Components Page 569

Prepared exclusively for [email protected] Transaction: 5095


CAPACITORS

220uF - Qty. 2

2200uF – Qty. 1

MALE TO MALE JUMPER WIRES

Short Male to Male Jumper Wires

Qty. 8

Long Male to Male Jumper Wires

Qty. 4

MALE TO FEMALE JUMPER WIRES

Qty. 8

REPLACEMENT PARTS
While all components for circuit building needed for the lessons are included in the kit, if
you need to purchase extra parts or replacement parts, please visit:
www.42electronics.com/products/replacement-parts.

Level B – Quick Reference: Components Page 570

Prepared exclusively for [email protected] Transaction: 5095


QUICK REFERENCE:

LIST OF MATERIALS
FOR EACH LESSON

LESSON 1

• Raspberry Pi connected to a monitor, keyboard, and mouse

LESSON 2

• Raspberry Pi connected to a monitor, keyboard, and mouse

LESSON 3

• Raspberry Pi connected to a monitor, keyboard, and mouse

LESSON 4

• Raspberry Pi connected to a monitor, keyboard, and mouse


• 1 x Breadboard
• 1 x Ribbon Cable
• 1 x Wedge
• 1 x 220-ohm Resistor
• 1 x LED
• 4 x Short Jumper Wires
• 1 x Piezo Speaker

Level B – Quick Reference: List of Materials for Each Lesson Page 571

Prepared exclusively for [email protected] Transaction: 5095


LESSON 5

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Circuit from Lesson B-4
• 1 x Slide Switch
• 1 x Pushbutton Switch
• 3 x Long Jumper Wires
• 2 x Short Jumper Wires

LESSON 6

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Circuit from Lesson B-5

LESSON 7

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Assembled Circuit from Lesson B-6
• 1 x Matrix Style Keypad plus Male-to-Male Pin Adapter
• 4 x Short Jumper Wires

LESSON 8

• Raspberry Pi connected to a monitor, keyboard, mouse, and the internet

Level B – Quick Reference: List of Materials for Each Lesson Page 572

Prepared exclusively for [email protected] Transaction: 5095


LESSON 9

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Assembled Circuit from Lesson B-7
• 1 x MCP3008 (Analog to Digital Convertor Integrated Circuit)
• 4 x Long Jumper Wires
• 6 x Short Jumper Wires

LESSON 10

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Assembled Circuit from Lesson B-9
• 1 x Potentiometer
• 1 x Phototransistor
• 1 x 10k-ohm Resistor
• 5 x Short Jumper Wires

LESSON 11

• Raspberry Pi connected to a monitor, keyboard, mouse, and internet access


• Assembled Circuit from Lesson B-10
• 1 x RFID Reader
• 1 x RFID Tag
• 1 x RFID Card

LESSON 12

• Raspberry Pi connected to a monitor, keyboard, mouse, and the internet


• Assembled Circuit from Lesson B-11

Level B – Quick Reference: List of Materials for Each Lesson Page 573

Prepared exclusively for [email protected] Transaction: 5095


LESSON 13

• Raspberry Pi connected to a monitor, keyboard, and mouse


• 1 x Breadboard
• 1 x Wedge and Ribbon Cable
• 1 x RGB LED
• 3 x 1k-ohm Resistors
• 1 x 10k-ohm Resistor
• 13 x Short Jumper Wires
• 6 x Long Jumper Wires
• 1 x 74LVC245 Level Shifting Integrated Circuit
• 1 x Infrared Obstacle Sensor
• 1 x Infrared Line Sensor

LESSON 14

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Assembled Circuit from Lesson B-13
• 1 x SR04 Ultrasonic Range Finding Sensor
• 1 x Long Jumper Wire

LESSON 15

• Raspberry Pi connected to a monitor, keyboard, mouse, and the internet


• Assembled Circuit from Lesson B-14
• 1 x BMP280 Temperature Sensor
• 2 x Long Jumper Wires
• 2 x Short Jumper Wires

Level B – Quick Reference: List of Materials for Each Lesson Page 574

Prepared exclusively for [email protected] Transaction: 5095


LESSON 16

• Raspberry Pi connected to a monitor, keyboard, mouse, and the internet


• Assembled Circuit from Lesson B-15
• 1 x SSD1306 OLED I2C Display
• 5 x Short Jumper Wires
• 1 x Long Jumper Wires
• 1 x Slide Switch

LESSON 17

• Raspberry Pi connected to a monitor, keyboard, mouse, and the internet


• Assembled Circuit from Lesson B-17
• 1 x LED
• 1 x RBG LED
• 2 x Short Male-to-Male Jumper Wires
• 1 x Long Male-to-Male Jumper Wires
• 6 x Long Male-to-Female Jumper Wires
• 1 x 220uf Capacitor
• 1 x 2200uf Capacitor
• 1 x Capacitive Touch Sensor

LESSON 18

• Raspberry Pi connected to a monitor, keyboard, and mouse


• Assembled Circuit from Lesson B-17
• 1 x Ultrasonic Range Sensor
• 2 x 1k-ohm Resistors
• 2 x Long Jumper Wires
• 5 x Short Jumper Wires

Level B – Quick Reference: List of Materials for Each Lesson Page 575

Prepared exclusively for [email protected] Transaction: 5095

You might also like