Lab 3 Activity
The goal of Lab 3 is to prepare for Lab 4 (Thermal Design), scooping up
loads of useful skills along the way. By the end, you want to have the
capability of reading in four channels of RTD temperature in an automated
Python program. In overview, the steps consist of:
- Establish connection to the bench Raspberry Pi.
- Exercise some basic Linux commands.
- Create a simple Python program and execute.
- Initiate I²C communication; verify on scope; get voltage from ADC.
- Breadboard single RTD current source and read voltage (temperature) via
Python program.
- Expand program and hardware to multiple channels and establish periodic
logging.
Ready for all this?
Task Breakdown
Note: see Tips section below for helpful hints.
Raspberry Pi Connection
- Use PuTTY (64-bit) on lab machines to connect to
benchX.physlabs.ucsd.edu, where X is the bench number as
it appears on the label on the Pi unit at your bench. The username is
benchX (same X), and the password defaults to bench1
(same independent of bench number). You can also use ssh from
any campus Unix/Linux/mac environment via the command ssh
benchX@benchX.physlabs.ucsd.edu
- The first task is to change the password to something unique to your
bench. Discuss with your bench partner and come up with something you can
both remember. Once decided, enter the command passwd at the
prompt, which will first ask to confirm the current (original) password,
then have you enter a new one twice (must match).
Linux Familiarity
You might find any number of Linux tutorials online. Please explore
as much as you like. The suggestions below outline a minimal familiarity
level, some of which you may need to ask Google to make progress.
- Make a directory (mkdir) to hold your Lab 3 work (call it
whatever you want, but be aware that spaces in filenames are not
Linux-friendly; see tips).
- Learn to use cd, pwd,
ls,
ls -l, what . and .. mean.
- Make some text files to mess with, using various techniques:
- $ echo "customized text to end up in file" > filename
(modify text to suit, and provide some descriptive filename). Try the
echo command without the > filename part to see what
it does (and why it's called echo). The > filename part puts
the result (stuffs it into) a file instead of the terminal window.
- Edit a new file into existence using an editor like vi or
nano. See this
synopsis of Command-line editors on the Pi. Note that these machines are configured so that
typing vi actually runs the (better) vim. To edit a
new file, just type the editor command followed by the (new) filename.
Create files containing more than ten lines of stuff, to help illustrate a
few things later.
- Once you have a few files, make copies the following two ways (making
up your own names instead of literal use of the placeholders below):
- $ cp some_file diff_file (copy)
- $ cat another_file > new_file (catenate)
- $ cat third_file >> append_to_file (concatenate)
- $ cat fourth_file >> append_to_file (concatenate)
Note the difference between the two flavors of the cat
commands. A single greater-than sign creates a new file to receive the
contents, whereas two together (>>) appends the content to an
existing file (or creates if non-existent). For illustrative purposes,
we do the concatenate command twice (possibly using distinct/different
input files) so you can see the appending feature in action.
- Now do ls (list) to see what you've got;
pwd (print working directory) to verify where you are;
ls -l (long list) to
see more detail on files (permissions; size, creation time); ls
-lt to sort by creation time (newest at top);
mv to rename (move) a file; cat to dump
the contents of a file to the screen; head to print the
first 10 lines of a file; head -n3 to print the first
three lines; tail to look at the last 10 (or 3; similar
arguments to head); less to just look at (not
edit) a file (q to exit).
- Explore word count (wc), operating on single files,
and on the all the entire contents of the directory (via *).
The output tells how many lines, words, and characters a file has. Also
not a bad idea to look at how the wild-card (*) operator works in
command lines so you can target particular file types (try this
tutorial, and note the power of the bracket convention to limit the
range of possibilities).
- Look up how to use the grep command and try it
out on some of your files. Grep is a super powerful way to find text in a
file. In fact, pick a word/string that only appears in one of your files,
and do: $ grep string * to look for the instance in all
files in a directory. This is hugely useful for finding stuff.
- Pipes! Unix/Linux commands can be strung together so that the output of
one process feeds the input to another, and these can be strung along in
arbitrarily-long sequences. Try the following examples to get the hang of
what's happening:
- $ head some_long_file | grep some_string looks
for string in first ten lines of file
- $ head some_long_file | grep some_string >
output_file same as above, but stuffs (any) output into a new file
- $ grep some_string some_file | less look for all
lines containing a certain string in a certain file (or * for
all files in that directory) and put the output to less for easier viewing
(especially if the result is voluminous and won't fit on one screen; less
allows you to navigate more easily)
- $ ls -lt | head shows (up to) ten most recent files
in directory—something I often use to discover any files just created
by running a process/program
- Use the man (manual) command to look up details
on commands (e.g., man ls). Note all the flag options.
The interface is based on less, so: q exits;
space bar pages forward; b goes back one page; G
goes to end of file; 1G goes to beginning; and super-important:
/ initiates a search (just type in text to find after
/ and hit enter; n finds next and N
finds previous; best to start at top of file (1G) because it won't wrap
Python Intro
- Type python at the command line to launch an
interactive session of Python. Note the version number (may be useful).
To exit, type Ctrl-D. The session allows you to test syntax, how lists and
indexing works, etc. It's super useful.
- Edit a new file with a .py extension. For the first line in
the file, put: #!/usr/bin/env python. The shell interprets the
#! (pronounced she-bang, helping me keep the order straight) as
an interpreter directive. The /usr/bin/env part queries the
computer's environment to find the location of the final argument,
python, and runs that. It would be possible to just have
#!/usr/bin/python to run Python directly, but Python's location
(path) may vary from one computer to another, so the env trick makes
the code more general.
- To get started, have a simple print statement in the program. Save and
exit the editor. Now we have to change the permissions of the file to make
it executable. We only have to do this once for a file, and future edits
will not require re-classifying as executable. Using ls
-l, note the permissions of the file may look like
-rw-r--r--. The first dash indicates a regular file
(d is a directory, for instance). Then come three groups of
rwx (or dashes) for read, write, execute. The three groups
pertain to the owner (benchX, likely), the group (users, likely), and the
rest of the world (account holders not part of the same group). In the
example above, everyone can read the file, but only the owner can write
(edit). To make the file executable, any of the following will work:
- $ chmod +x file.py → -rwxr-xr-x →
add executable for all
- $ chmod u+x file.py → -rwxr--r-- →
add executable for user (owner) only; if starting from -rw-r--r--
- $ chmod 755 file.py → -rwxr-xr-x →
direct control of all fields 4=r; 2=w; 1=x
- $ chmod 744 file.py → -rwxr--r-- →
- $ chmod 754 file.py → -rwxr-xr-- →
- Run the program from the command line using ./file.py
(using your program's name instead of the place holder file.py).
The ./ convention says: run this thing that sits in my current
(.) directory.
- Now expand the functionality of the program (or start a new one) to
incorporate command lines, write to a file, and any other features you need
to develop in pursuit of the goal. Use the Google to find tutorials to
help.
- Use import sys to bring in the system command
module for command lines; arguments are now available in
sys.argv[i], where i is an integer index.
- Have the program do something with a command line argument (or
multiple ones) and print the result to screen.
- Now open a file for writing (outfile =
open('myfile.out','w')) and write something to it
(outfile.write("some text\n")) and in the end, show good manners
by closing the file (outfile.close()). Run the program and
check the file for correct/expected content. The \n is a
newline; outfile is just a name/handle used in the program—could be anything you
want.
- Familiarize yourself with the time module
(import time). Import into an interactive session and
do dir(time) to see what commands are available, and
help(time.some_command) to learn more about individual
commands. Try the following interactive commands to get a feel for
key functions:
- time.time() → Time in seconds from Unix epoch (8.5 days before I was born)
- time.localtime(time.time()) → familiar format
- time.localtime(time.time())[0] → grab year
(integer)
- time.gmtime(time.time()) → in UTC
- time.sleep(5) → note lag before prompt returns
These can help you schedule readings and print times to the file for
recording a data series.
I²C Communications
- Following the lead from lecture notes, write a bare-bones I²C
interface to at least "energize" the communication lines on the Raspberry
Pi header and see activity on the scope:
import smbus
i2c_bus = smbus.SMBus(1)
ADDR = 0x48
i2c_bus.write_i2c_block_data(ADDR,1,[0x85,0x83])
# or later: data = i2c_bus.read_i2c_block_data(ADDR,0,2)
- The communication will fail (including Python I/O error,
usefully) without a device connected, but this is fine as a first step.
It allows us to verify that the I²C lines are active and doing
what we expect. Probe the SCL and SDA lines on the scope to see what
it's doing. Recommended settings: normal trigger mode; 1x probes; 2
V/div; trigger either signal at about 1 V level; adjust time base to get
full pattern. You should be able to verify the address is being sent
correctly (MSB first), and the read/write bit at the expected value.
But crucially, you should see the acknowledge fail (not pulled low)
since the device is not connected. Sketch out the waveforms (aligned)
for reference and for the report. Do for both read and write commands
to see how they differ.
- Now hook up the ADS1015 4-channel ADC chip (power, ground, I²C
lines; see ADS1015 datasheet). Try the same
write and read commands. The Python code should not throw an
error this time (big cheer if not). Now the scope should show the
acknowledge pulled low, and the read command should be longer, showing data
coming back. The goal is to understand each bit in the
communication stream: dissect it and compare to the data sheet waveforms to
verify the signals are what you expect and every bit is what and where you
expect.
- Now study the
ADS1015 datasheet to figure out how to
configure for reading the four individual channels referenced to ground.
Each read will consist of a configuration write followed by a read.
- Verify that the data coming back makes sense, and that if putting
a volt or so on the associated channel, the read/query returns a
sensible/valid number. Completion of this step and the previous one
represents a significant hurdle cleared. It's downhill from here.
RTD Signal Conditioning
- Follow the circuit diagram in the lecture and use the parts provided in
the lab to configure a current source for the RTD readout. Use a
(precision, ideally) 1.00 kΩ resistor in place of the RTD to tune
the potentiometer for a 1.00 V result, meaning you've nailed
1.00 mA. Alternatively, a 1.20 kΩ resistor (or some other
value) should produce 1.20 V, for instance.
- Connect the RTD and interpret the voltage in terms of room temperature
(may also just use ohm-meter to compare). Be careful about handling the
RTD, as you can easily change its temperature. Best to tape it to the table
or piece of metal to stabilize it.
Read RTD Data
- Hook the voltage output of the RTD readout to one channel of the
ADS1015 and verify that your Python program can read the voltage correctly.
- Convert to temperature and perform regular polling at a cadence of once
every second or two, printing result to
screen for now. Put your finger on the RTD and see it change.
- Modify the program to save the time series to file. Each line should
have a time stamp and a temperature. The time stamp may be most usefully
expressed as seconds since the start (grab time.time() at the
beginning and then for each sample, subtract that off of the current
(evolving) time.time() value.
- One tip to keep your files from overwriting each other each time
you run the program is to format the filename to contain a time stamp.
For instance, you can get time.localtime(time.time())
at the start of the program and use the year, month, day, hour,
minute, second to make something like 191014-164503.out. Hint:
"%02d%02d%02d-%02d%02d%02d.out".
Configure Program for Multiple Channel Logging
- Finally, expand your program to read as many as 4 RTDs and save the time
sequence to file.
- Configure the hardware for at least two channels. This can be done either
by populating the breadboard with more channels, or using our snazzy
custom-designed and built circuits made to interface gracefully to the
ADS1015. It's the same circuit, just different packaging.
- Verify that you get reasonable temperature readouts from all channels
in a log file.
Tips
Here are some tips that may be useful:
- One minute spent reading and understanding each tip may save you ten
minutes or more in execution of the lab. What a bargain!
- Lines that begin with $ indicate a Linux prompt. Do
not type the $ character in this context.
- Don't rigidly or literally adhere to filename placeholders, like
file.py: create your own names.
- Spaces in filenames are frowned upon in Linux. As a command line
language, spaces are what separate arguments in the command line. It's
very confusing to have chopped up filenames that look like separated
command arguments. It is possible to have spaces in filenames,
and they can be "escaped" by a backslash (e.g., Stupid\ Windows\
Style\ Filaname\ with\ Stupid\ Spaces.txt). But why would we want to
sink to this level?
- Unix commands are short for efficient typing, though as a result
cryptic-looking.
- In the less or vi/vim environment,
the command G (go) moves around the file by line number.
1G goes to the beginning, G goes to the end,
and 234G would go to line 234. In nano, the
c flag shows line numbers, which is useful (open with
nano -c filename).
- It is often useful to have multiple windows up so you can edit a file
in one, run a program in another, have an interactive Python for testing
syntax in another, and look at supplemental information in
others. Feel free to open multiple sessions to give you more interface!
- It would be handy to print the current line of the temperature readout
to the screen as well as to file so you can track progress.
Write-Up
The write-up will be deferred until after Lab 4, since most of the work
this week is aimed at building familiarity. This also gives you time to
complete all tasks before Lab 4 without spending time on the write-up.
However, some things you do this week are worthy of inclusion in the
write-up, and are called out below.
- Include a sketch of the failed I²C communication signals (SCL and
SDA, aligned in time) for read and write requests, noting in particular
where the R/W bit is, the address, and the acknowledge. Note how these
waveforms change when the communication is successful.
- While it may be a bit much to draw out the full waveforms for
successful communications (much longer trains), you may want to scan the
results and write down the ones and zeros to make sense of the
communication contents. Is the address right? Is the read/write bit as
expected? Is the acknowledge always pulled low? Is the register address
(second byte) correct? What data is being transmitted, and does it make
sense?
Back to Physics 122 page