This is only a preview of the January 2011 issue of Silicon Chip. You can view 29 of the 104 pages in the full issue, including the advertisments. For full access, purchase the issue for $10.00 or subscribe for access to the latest issues. Items relevant to "Cheap-N-Easy 433MHz Sniffer":
Items relevant to "Cranial Electrical Stimulation Unit":
Items relevant to "Digital/Analog USB Data Logger, Pt.2":
Items relevant to "Hearing Loop Signal Conditioner":
Purchase a printed copy of this issue for $10.00. |
By MAURO GRASSI
Universal USB
Data Logger: Pt.2
Last month, we described the main features of the USB Data
Logger and gave the circuit details. This month, we give the
assembly procedure, explain how to install the Windows driver
and PC host software and describe how the unit is used.
A
S MENTIONED last month, a
major feature of the USB Data Logger is its custom scripting language.
This makes it very versatile and allows
it to be interfaced to many different
sensor types. It can be interfaced to
most digital sensors, almost all I2C and
1-wire sensors, and almost any analog
sensor, frequency input or counter.
Custom scripting also makes it
highly configurable. If you have a logging application in mind, this unit will
almost certainly be suitable.
The accompanying PC software is
used to compile the custom “scripts”.
These provide the instructions for
reading the various sensors and for
processing the data. So this unit can
not only log data but can also analyse
that data!
34 Silicon Chip
Towards the end of this article, we
run through a number of scenarios and
give some example custom scripts.
These are a good starting point for
learning to write scripts for your own
logging applications.
What it can do
Before moving on to the construction, let’s run through a few things the
USB Data Logger can do.
First, if you have a weather station,
you can log a whole day’s worth of
temperatures and then compute the average. You could also extract the daily
maximum and minimum temperatures
and log them as well.
Second, if you have a number of
digital sensors connected to the I2C
bus, you can send commands to read
from them, log their values or send
commands to power them down during extended periods when no logging
needs to occur. Note that the USB Data
Logger itself will automatically switch
into standby mode during extended
periods of inactivity to save power.
You can also read from a sensor and
execute code depending on the reading
reported by the sensor. For example,
if you have a temperature sensor, you
can monitor its value and turn an external relay on or off (eg, to control an
air-conditioner) if the value is outside
a specific range.
These are just some examples of
what is possible.
If you’ve ever programmed before,
it should be very easy to understand
and write programs for the USB Data
siliconchip.com.au
Fig.2: install the parts on the PC board as shown on this layout
diagram, starting with surface-mount parts REG1 (bottom, left)
and CON1 (the memory card socket). The photo at right shows
the fully-assembled board. Note that there are some minor
differences between this unit and the final layout shown above.
Logger (the scripting language’s syntax
is simple and loosely based on C). If
not, we give a very quick introduction at the end of this article, with a
number of examples showing code
that can be used.
It shouldn’t take too long to learn
and a full description of the language
can be downloaded as a PDF file from
the January 2011 section of the SILICON CHIP website – www.siliconchip.
com.au
Board assembly
The Universal USB Data Logger
is built on a double-sided PC board
coded 04112101 and measuring 60
x 78 mm. This is housed in a plastic
instrument case measuring 68 x 130 x
25mm (W x D x H).
Fig.2 shows the PC board assembly
details. It should take no more than a
couple of hours to assemble but before
starting, check it carefully for hairline
cracks in the copper pattern and for
shorts between tracks and pads.
Once you are satisfied that everything is OK, start the assembly by
soldering in the SMT (Surface Mount
Technology) boost regulator (REG1).
This is a a TPS61097-33DBVT device
in a SOT23 5-pin package and is
mounted on top of the board, towards
the bottom left corner. You will need a
fine-tipped soldering iron and a steady
hand to solder it in.
A magnifying lamp will also be useful, if you have one.
The best way to install REG1 is to
first position it over its pads (it can
only go one way) and then hold it in
position using some sticky tape, leav-
ing pin 5 uncovered (see Fig.1 in Pt.1).
That done, heat the pin and apply the
solder quickly, taking care not to apply
the heat for more than a few seconds.
The solder should melt easily and
secure the pin and pad.
Let it cool, then solder pin 3, which
is diagonally opposite. Once that is
done, you can remove the sticky tape
and solder the remaining three pins.
If any solder bridges form, use solder
Table 4: Capacitor Codes
Value
100nF
10nF
33pF
12pF
µF Value
0.1µF
0.01µF
NA
NA
IEC Code
100n
10n
33p
12p
EIA Code
104
103
33
12
Table 3: Resistor Colour Codes
o
o
o
o
o
o
o
o
siliconchip.com.au
No.
1
3
2
10
2
3
2
Value
330kΩ
33kΩ
15kΩ
4.7kΩ
1kΩ
470Ω
10Ω
4-Band Code (1%)
orange orange yellow brown
orange orange orange brown
brown green orange brown
yellow violet red brown
brown black red brown
yellow violet brown brown
brown black black brown
5-Band Code (1%)
orange orange black orange brown
orange orange black red brown
brown green black red brown
yellow violet black brown brown
brown black black brown brown
yellow violet black black brown
brown black black gold brown
January 2011 35
12
23.5
TOP END
(CUTOUT
FOR CON3)
(S1)
(RIGHT HAND SIDE OF LOWER PART OF CASE)
54
7
(S2)
54
12
27
4
17
(CUTOUT FOR CON4)
7
END PANEL INSERT
6
45
TOP END
6
40
130
ALL DIMENSIONS IN MILLIMETRES
18
5
23
(TOP END)
(LEFT HAND SIDE OF LOWER PART OF CASE)
Fig.3: follow this diagram to make the cutouts in the base of the case. The side cutouts provide clearance for switches
S1 & S2 and the two screw terminal blocks, while the end cutout provides access to the memory card socket.
SILICON
CHIP
D0
USB
D1
D2
D3
D4/A0
GND
D5/A1
+3.3V OUT
A2
5.5 – 7V (Vin)
A3
+3.3V(HI) OUT
START/STOP
USB POWER/Vin
(UP/DOWN)
USB DATA LOGGER
Fig.4: this front-panel artwork can be
used as a template for cutting out the
access hole for the USB socket and
drilling a hole for the 3mm blue LED.
wick (or desoldering braid) to remove
them.
The memory card socket is next on
the list and this is also soldered to the
36 Silicon Chip
top of the board. It has two small plastic locating posts that fit into matching
holes in the PC board. These correctly
place it in position over its pads.
Solder the two holding pads on its
sides first to secure it in place. Once
that is done, solder the rest of the pins
but be careful not to apply heat to the
plastic body, as it will melt. As before,
use solder wick if you accidentally
create solder bridges between adjacent pins.
Now for the resistors. Table 3
shows the resistor colour codes but
you should also check each one using a DMM before installing it on the
board, just to make sure. Note that,
due to space restrictions, the resistors
are all mounted vertically (see photos
and Fig.2).
The five Schottky diodes can go in
next. Unlike the resistors, these need to
be orientated correctly. Their cathodes
are indicated by a grey stripe at one
end, while each anode connection is
indicated with an “A” on the screened
overlay.
The TO-220 regulator (REG2)
mounts horizontally on the PC board.
To do this, first bend its leads down
through 90° about 4mm from its
body, then mount it in position. Note
that a screw is not normally used to
secure it, as it is not strictly required
and would interfere with the case.
However, if you are concerned about
mechanical stress, you can secure it
using an M3 screw and nut and drill
a hole through the bottom of the case
to provide clearance.
Once the regulator is in position,
the three leads can then be soldered
and trimmed.
The 28-pin IC socket for the microcontroller (IC1) can now be installed.
If you don’t have a 28-pin 0.3-inch
socket, you can use two 14-pin sockets
arranged end to end. Be sure to install
the socket (or sockets) with the notch
facing in the correct direction (ie, towards CON4) to avoid confusion when
installing IC1 later on.
Follow on by installing the capacitors. There are four types on the board:
monolithic, ceramic, tantalum and
electrolytic. Note that the tantalum
(brown on Fig.2) and electrolytic
capacitors are polarised and must be
installed with the correct orientation.
Crystals X1 & X2 should be installed next. These can go in either
way around but note that X1 is the
20MHz crystal while X2 is the smaller
32.768kHz crystal. Note also that X2’s
leads are delicate so take care when
installing it. Push it down as far as it
will comfortably go without stressing
the leads before soldering.
The 2N7000 FET (Q1) can now go
in, followed by switches S1 & S2. S1
is a mini toggle switch (S1), while S2
is a momentary pushbutton switch.
Make sure they sit flush against the
PC board before soldering their leads.
The 8-way and 4-way horizontal
terminal block headers must also be
mounted flush against the PC board.
Solder these in place now, then install
the vertical mounting USB Type-B
socket. This socket has two mounting
siliconchip.com.au
The PC board is secured to the base using four selftapping screws that go into integral pillars. Power can
come from two AAA NiMH batteries, from a USB port
or from some other external supply.
tabs on either side that secure it in
place – solder these first, then solder
the four pins towards the centre.
The adjacent 3mm blue LED (LED1)
must be installed with its body 11mm
above the surface of the PC board. A
10mm cardboard spacer between the
leads can be used to set the height.
Make sure that the LED is correctly
orientated (ie, anode to the left).
Finally, connect the 2-way AAA cell
holder to the supply terminals (BATT)
at bottom left. The red lead goes to the
“+” terminal, while the black lead goes
to the remaining terminal.
That completes the PC board assembly, apart from installing IC1. This is
left out of its socket until after some
initial power supply checks.
First switch-on
You should use two AAA cells to
initially power the unit and check the
supply rails. We recommend that you
use two NiMH, 900-950mAh batteries, although cells of greater or lesser
capacity can also be used. Note, however, that you may have to change the
10Ω charging resistor in parallel with
D2, depending on your battery type.
The method used for calculating this
resistor value was given in Pt.1 last
month (page 43).
Make sure that the two AAA cells
are charged before attempting to use
them.
siliconchip.com.au
Assuming they are charged, insert
them into the battery holder and check
the voltage between pins 2 (VDD) and 1
(GND) of CON3. This should be close
to 3.3V. If this is incorrect, disconnect
the batteries immediately and recheck
your work around REG1. If there’s no
voltage at REG1’s output, check the
orientation of diode D2.
If you do get the correct 3.3V, remove
the cells and insert IC1 into its 28-pin
socket. Make sure it is correctly orientated, with its notched end matching
the component overlay.
Final assembly
The case requires several cut-outs to
be made before installing the PC board.
In all, five cut outs are required in
the base of the case – one each for the
two switches, one each for the two
terminal blocks and a slot for the
memory card. Fig.3 shows the locations of these cut-outs. Each can be
made by drilling a line of small holes
just inside the cut-out area, then breaking out the section and carefully filing
to a smooth finish.
Once the cut-outs have been completed, slide the PC board into position
and secure it using four of the selftapping screws supplied with the case.
Two holes are also required in the
top section (lid) of the case – one for
the USB socket and one for the blue
3mm LED. The front panel artwork
(see Fig.4) can be used as a drilling
template. You can either copy this artwork or it can be downloaded in PDF
format from the SILICON CHIP website.
If you purchase a kit, a sticky label
will probably be supplied. If not, print
the label out, laminate it and attach it
to the lid using some silicone sealant
as the adhesive.
The 2 x AAA battery holder is stored
in the battery compartment of the case.
It can either be left loose or it can be
glued to the case lid.
Finally, complete the assembly by
fitting the top half of the case into position and securing the assembly using
the supplied self-tapping screws. The
two 20mm-long screws go into the
two top holes, while the 9mm-long
screws are used for the two bottom
holes. Note that the bottom two holes
are accessed by removing the battery
compartment cover.
That completes the assembly of the
USB Data logger. The next step is to
install the Windows driver and the
supplied PC host program.
Installing the Windows driver
The USB Data Logger requires that
a driver be installed on your Windows
PC, so that it will work with the PC
host program. The supplied LibUSB
driver should work with almost all
Windows versions, including 64-bit
Windows 7 versions.
January 2011 37
Fig.6: select “Browse my computer for driver
software when this window appears.
Fig.5: this is how the USB Data Logger entry appears in Device
Manager before the Windows driver is installed.
Fig.7: ignore this warning by clicking “Install
this driver software anyway”.
The step-by-step driver installation
procedure for a Windows 7 machine
is as follows (the procedure is similar
for other Windows versions):
(1) Download the file usbdatalogger.
zip from the SILICON CHIP website (it’s
in the January 2011 downloads section). This zipped archive contains
both the Windows driver and the PC
host software files.
(2) Unzip the contents of usbdatalogger.zip to a directory on your hard disk
(this can be done by right clicking on
the file and choosing “Extract All...”).
(3) Connect the USB Data Logger to
your PC using a Type A to Type B USB
cable (eg, Jaycar WC-7700 or Altronics P-1911A). The unit can now be
powered directly from the USB port
by moving switch S1’s position to up.
(4) Windows should now recognise the
new device and prompt for the installation of the driver. It may then try to
install the driver automatically but this
will fail because the driver won’t be
part of the driver database yet.
You will then get the message “De38 Silicon Chip
Fig.8: you’ll get this message when the driver has
been successfully installed.
vice Driver Software was not successfully installed”.
(5) Go to Control Panel –> Device
Manager. A window will appear as
shown in Fig.5 and this should show
the “USB Memory Card Data Logger”
device with a yellow exclamation
mark next to it.
(6) Right click this entry and select the
“Update Driver Software” option. A
window similar to the one shown in
Fig.6 will appear. Choose “Browse my
computer for driver software”.
An open file dialog will appear and
you should navigate to the directory
where you unzipped the driver files
using the “Browse” button. Choose
the “USBMemoryCardDataLogger.
inf” file that appears.
For recent Windows OS versions
Higher Baud Rates
In order to support baud rates greater
than 57.6kbps (ie, up to 0.5Mbps), we
have changed the two 100nF monolithic
capacitors on the A0/D4 and A1/D5 inputs
to 10nF.
(eg, Vista and Windows 7), a security
message will appear as shown in Fig.7.
(7) Click “Install this driver software
anyway”. Windows will then proceed
to install the driver and this may take
a few minutes, depending on your
system. Once complete, a window
should appear saying that “Windows
has successfully updated your driver
software” – see Fig.8.
(8) Return to Device Manager and
check that the driver has been installed
correctly. You should see the “USB
memory Card Data Logger” entry
under the “Libusb-Win32 Devices”
group, without the exclamation mark
(provided, of course, that the USB Data
Logger is connected to the PC).
That completes the driver installation.
Launching the host software
The supplied PC host program is
used to compile, simulate and load
custom scripts onto the USB Data
Logger. It’s also used to configure the
unit and to transfer files to and from
siliconchip.com.au
it (including logs). The host program
also synchronises the logger’s realtime clock with the PC.
Note that since all files are stored in
a FAT file system, the memory card can
also be connected directly to a PC via
a memory card reader. This would be
desirable if transferring very large files
(eg, more than 15MB), as the PC can
access the memory card substantially
faster than the USB Data Logger’s microcontroller can.
The host software is launched by
double-clicking on usbdatalogger.exe.
This executable program is included
in the zipped archive you downloaded
earlier to obtain the Windows driver.
You’ll find it in the same folder as the
extracted Windows driver.
The easiest approach is to create a
shortcut to this file on your desktop.
Just right-click it and choose “Send To
Desktop” from the drop-down menu.
Once that is done, you can launch the
program via the desktop icon. Fig.10
shows the opening dialog.
Using the host program
The PC host program is based
around a Windows GUI (Graphical
User Interface) and was written in
Visual C++. The custom scripting
language compiler and parser were
also written in C++ (with help from
the open source parser and lexical
analyser generators, Bison and Flex).
The VM engine was written using the
full version of the C18 compiler from
Microchip.
Fig.9: once the Windows driver has been installed, the USB Memory
Card Data Logger entry will appear in Device Manager under “LibusbWin32 Devices”. Note that the yellow exclamation mark is now gone.
When launched, the host program
detects the USB Data Logger automatically. You can then write, compile and
send custom programs to the unit (each
script is a separate file).
The main feature here is the custom
scripting language support, so let’s
now take a closer look at this and give
some examples.
Scripting language
The scripting language is a lightweight functional language implemented on a virtual machine that
incorporates virtual memory support.
The best way to start is to see some
sample code, which we present in
the sections that follow. The PC host
program converts the source code to
machine code that then executes on
the USB Data Logger.
At this stage, it’s customary to give
the “Hello World” program, as shown
in Script 1.
A script consists of a header declared by the HEADER keyword,
followed by its name (which you can
choose), in turn followed by the header’s body enclosed in curly brackets.
The header can contain settings to alter
Fig.10: this screen
grab shows the PC
host program that’s
used to compile and
send scripts to the
USB Data Logger. It
can also simulate
scripts, change
various settings and
synchronise the time.
siliconchip.com.au
January 2011 39
Script 1: Hello World Program
HEADER helloWorldHeader
{
// Empty header
}
SCRIPT helloWorldScript
{
// Simple Hello World program for the USB Data Logger, by Mauro Grassi
PRINT “Hello World”, NEWLINE;
}
the default behaviour of the script but
in most cases, its body will be empty
and the defaults can be used.
In these examples, we’ve used
capital letters for all the keywords, to
easily identify them, but the compiler
accepts keywords in lower-case letters
as well. However, you must use either
all lower case or all upper-case letters
for keywords. Usually, it is a syntax
error to use a combination of upper
and lower-case letters for keywords,
eg, HEADER and header are both OK
but heAder is not.
Note that all other parts of the compiler are case-sensitive. The compiler
will give useful error and warning
messages, together with the line and
column number of the error/warning.
This makes it easy to fix any syntax
errors.
The header is followed by the
script’s body of code. This is similarly
defined using the SCRIPT keyword,
followed by the name of the script,
followed by the custom script code,
again enclosed in curly brackets. Lines
starting with two slashes are comments and are ignored by the compiler
(as in C). Curly brackets are used to
group statements, which are always
terminated by a semi-colon.
In this case, the script has a single
command, PRINT, which takes the argument “Hello World” (a string) and a
newline. The arguments to the PRINT
command are separated by commas.
The output is actually written to the
log file for that script (each script has
its own log file – although it is also
possible for a script to write another
script’s log file).
So that’s our first program. Let’s now
run through a number of scenarios
and present some custom scripts to
do particular tasks. We’ve chosen the
most common tasks that readers are
likely to request (the sample code can
also be downloaded from the SILICON
CHIP website).
Reading an analog sensor
One of the most common things
you’ll want to do is to log a voltage that
Script 2: Analog Temperature Sensor
HEADER myAnalogSensorHeader
{
}
SCRIPT myAnalogSensorScript
{
// Basic Script Showing How To Read and Log an Analog Sensor, by Mauro Grassi
<at><at>openADC(0);
PRECISION(1);
WHILE(1)
{
$T=(<at><at>readV(0)-0.25)/0.028;
PRINT “The Temperature is: “, $T, “ degrees Celsius”, NEWLINE;
SLEEP(60);
}
}
40 Silicon Chip
varies over time. The USB Data Logger
has four analog inputs which can be
used for this purpose, labelled A0-A3.
Remember that two of the analog
inputs are for low voltages (0-3.6V),
while the other two are for higher
voltages (0-13.8V) – see Pt.1 last
month. They differ only in the voltage
divider used.
An analog sensor typically outputs
a voltage that’s proportional to the
measured quantity (ie, it’s ratiometric).
However, although most analog sensors are ratiometric, they may differ in
the specific “linear transfer function”.
Nevertheless, they can all be used with
this data logger.
You will have to consult the datasheet for your particular sensor to
configure it properly. However, the
general method will be similar to the
following example which describes
how to connect an Analog Devices
AD22103KTZ temperature sensor.
The AD22103KTZ is a 3-pin temperature sensor in a TO-92 package.
Two pins are used for the supply
(3.3V), while the third pin is the
output. It produces an output voltage
that’s proportional to temperature and
which ranges from 0-3.3V.
To use this sensor, connect the supply rails and connect its output pin to
one of the four analog input pins. In
this example, we’ll use A0 as the input
since it is suitable for 0-3.6V operation.
The transfer function of the AD22103
temperature sensor (according to its
datasheet) is given by:
Vo = (Vs/3.3)(0.25 + 0.028T)
where Vo is the voltage at its output
terminal, Vs is the supply voltage to
the sensor and T is the temperature
(between 0 and 100, in °C). For the
sake of simplicity, let’s assume that Vs
= 3.3, so the equation becomes:
Vo = 0.25 + 0.028T
Rearranging this equation to get the
temperature as a function of the output
voltage gives:
T= (Vo - 0.25)/0.028
A suitable custom program to read
this temperature sensor and log its
value every minute (ie, every 60s) is
shown in Script 2.
Much of this script is largely selfexplanatory but we’ll run through
a few basics that are not obvious.
Variables (which store data as 32-bit
floating point numbers) and Functions
(which execute code) can be both Local and Global. Local ones can only
be accessed by the custom script and
siliconchip.com.au
are defined there. Globals can be accessed by all running scripts and are
implemented internally.
Full details of the custom scripting
language’s syntax, built-in functions
and built-in global variables can be
downloaded (in a PDF file) from the
SILICON CHIP website (from the January 2011 downloads folder).
Program execution begins at the
<at><at>openADC(0); statement. As mentioned, each statement ends with a
semi-colon (as in C). The <at><at>openADC
statement is a built-in global function.
Their names always start with two “<at>”
characters (so it’s easy to tell which
are built-in functions and which are
user-defined functions, as the names
of the latter always start with just one
“<at>” character).
This particular function takes one
argument, which is the channel num
ber. In this case, <at><at>openADC(0);
simply configures the A0 pin as an
analog input.
The next statement, PRECISION(1);
is a built-in command (rather than a
built-in global function). It simply configures the number of decimal points
for printing floating point values, used
later on to display the temperature.
Next, the program enters its “main
loop” where it will execute its infinite
loop. This is the WHILE(1) built-in
command that executes the block of
code enclosed in its curly brackets
whenever the condition is non-zero
(as in C).
The next line in the script reads:
$T=(<at><at>readV(0)-0.25)/0.028; and
should be self-explanatory. There are
built-in rules for which arithmetic
operators take precedence over others
(eg, multiplication takes precedence
over addition, so that 8 * 3 + 2 = 26
rather than 40) but you can use brackets whenever in doubt. Apart from
the four arithmetic operators, you can
also use the “^” (exponent) and “%”
(modulo) operators (unlike in C where
“^” is used for XOR).
The above statement simply computes the temperature ($T) by reading
the voltage at channel 0 (using the
built-in global function <at><at>readV,
subtracting 0.25 from the value and
dividing the result by 0.028). It stores
the result in the local variable $T.
Local variables are “local” to the
current script, so cannot be accessed
by other running scripts (as opposed
to global variables which can). Local
variables’ names always start with a
siliconchip.com.au
Script 3: Reading A Frequency Input
HEADER myFrequencySensorHeader
{
}
SCRIPT myFrequencySensorScript
{
// Basic Script Showing How To Read and Log a Frequency Input, by Mauro Grassi
<at><at>openFrequency(0);
PRECISION(3);
WHILE(1)
{
PRINT “The Frequency is: “, <at><at>readFrequency(0), “ Hz”, newline;
SLEEP(5);
}
}
single “$” character. Global variables’
names always start with two “$” characters (in analogy with global and local
functions).
Once the temperature is computed
and stored in the local variable $T
(which is a 32-bit floating point value),
the next statement logs the result
to the memory card. A typical line
would read: The temperature is: 21.4
degrees Celsius
The PRINT command
PRINT is a built-in command and it
takes as argument a comma-separated
list. Each item in the list is either a
constant string, enclosed in quotes (“),
or an expression (in this case the value
of $T), or a special print command. In
this case, we are using the NEWLINE
print command to add a line return
to the log file.
The last line is another built-in
command: SLEEP. It takes a single numeric argument, which is the number
of seconds to suspend execution of the
script. It simply suspends the script
for the specified period, letting other
scripts run. The script will be woken
after this period and begin execution
after the SLEEP command.
In this case, since it is the last statement in the WHILE loop, a new value
will be read and logged and the process
will repeat indefinitely.
Another command for sending a
script to sleep is the SLEEPUNTIL
command (or sleepUntil if in lowercase). Unlike the SLEEP command, it
takes an absolute time (in the future),
as argument. For example, writing:
SLEEPUNTIL(16:00:10); will suspend
the execution of the script until just
after 4pm.
Now suppose you wanted to display
the reading in degrees Fahrenheit
as well. Then you could change the
PRINT statement to:
PRINT “The temperature is: “, $T, “
degrees Celsius, or “, ($T*(9/5)+32),
“ degrees Fahrenheit”, NEWLINE;
Logging the time
Another thing you can do is timestamp the logging. You can do this
using one of the built-in print functions, PF(#TIME), where PF stands for
PRINT FUNCTION and is used with
the built-in PRINT command. In this
case, you would replace the PRINT
statement with the following:
PRINT PF(#TIME), “ The temperature
is: “, $T, “ degrees Celsius”, NEWLINE;
Reading a frequency input
Reading a frequency rather than a
voltage is just as easy. Script 3 shows
the details.
In this case, after initalising the frequency input and setting the PRINT
PRECISION to 3 decimal places, the
main loop begins executing and logging the frequency on that pin in Hz,
every five seconds.
Note that the frequency can be anywhere between 0.1Hz and 192kHz. To
cover this wide range, three different
modes are used – LOW, MEDIUM and
HIGH frequency – and the mode will
be changed automatically by the firmware to suit the frequency (to achieve
the best accuracy).
For example, for frequencies below about 1kHz, a special LOW
January 2011 41
How The USB Data Logger Functions
This USB Data Logger is different to most other data loggers, as it incorporates
support for a scripting language. It is supplied with its own compiler and virtual
machine (VM) engine.
A virtual machine is basically a software implementation of a “real” machine. In this
case we are referring to a “processing machine”, ie, a processor that can execute instructions to add and subtract numbers, branch on a certain condition and call subroutines,
among others. An example of a well known VM is the PICAXE, which runs on a PIC.
This virtual machine can execute its own custom machine code but unlike a microcontroller, it is implemented in software. In this case, the firmware in the PIC18F27J53
microcontroller implements the VM and the Windows PC host implements both the
VM and the compiler for this language. The source code is compiled into machine
code and stored on a file on the memory card.
The VM engine is capable of multitasking, which means more than one custom
script can run at a time. It also incorporates a virtual memory engine as well (refer to
the PDF file on the SILICON CHIP website for further details).
This means that, unlike a PICAXE, the RAM (random access memory) and program
space available to each running script is much bigger than the few kilobytes available
on the PIC itself. It is cached to the memory card and only a small amount is present in
the microcontroller’s memory at any time. Any accesses outside the microcontroller’s
memory cause a “cache miss” and go to disk (ie, to the memory card). This will be
explained in more detail in Pt.3 next month.
FREQUENCY mode is used, whereas
above around 12kHz a special HIGH
FREQUENCY mode is used instead.
Reading a counter input
Reading a 32-bit counter value is
just as easy as logging a frequency
input. In this case, simply replace the
<at><at>openFrequency(0); statement by
either a “<at><at>openRisingCounter” or
“<at><at>openFallingCounter” statement
(selecting to increment the count on
a rising or falling edge). In addition,
replace the <at><at>readFrequency(0);
statement by a “<at><at>readCounter(0)”
statement (of course, you should
change the PRINT statement to suit
your needs).
Note that for counters, the value
is cleared (set to 0) whenever it is
opened. So the counter can be “re
opened” to clear it.
Reading an I2C sensor
Let’s now take a look at how to read
from a digital temperature sensor using
the I2C bus.
For this example, we are going
to use the Analog Devices AD7414
temperature sensor. This is a 10-bit
temperature-to-digital converter, using
the I2C bus.
The one we are using comes in a
SOT-23 6-pin package. Two pins are
for the supply voltage which is 3.3V,
meaning that it can be powered direct42 Silicon Chip
ly from the USB Data Logger. Another
two pins, AS and ALERT, are the input
and output respectively. The AS input
can be used to choose one of three I2C
addresses (to potentially use more than
one of these on the same bus). These
three addresses are chosen by a high,
low or floating pin. We’ve configured
ours so that the I2C address is 0x92
(hexadecimal).
The ALERT output pin will change,
if configured, when the temperature
exceeds the set limits. We are not using
this feature in this example but you
could write your own custom script
to do just this.
The remaining two pins are the SCL
(clock) and SDA (data) lines of the
I2C interface. These are connected to
digital inputs D0 and D1 respectively
on the USB Data Logger.
Reading the datasheet of the AD7414
sensor tells you how to read the temperature value. This particular sensor
is used by reading and writing to four
internal 8-bit registers. One register (at
address 0) holds the most significant
eight bits of the value (you can just
read this for a good approximation,
or you can read the second register as
well to retrieve the extra two bits for
full resolution).
The “Configuration Register” is at
address 1 and this holds the extra
two bits of temperature information.
It also holds extra bits to control the
power to the sensor (you can put it in
standby to save power and set the alert
function mentioned above). The other
two registers hold the minimum and
maximum temperatures for the alert
function, which we don’t use in this
example.
The address writing works as follows: the first write sets the address
of the next write. For example, if we
want to write to the register at address
2, we first write 2 and then the value
to write to this address.
Similarly, for reading, you first write
the address, then read from the sensor.
So to read the value at address 1, for
example, you first write 1 and then
read from the device (one byte).
A program to read the temperature
from the AD7414 is shown in Script 4.
In this script, we first define a constant #I2C_ADDRESS in the header (it
can also be defined in the script). We
then use it in all places of the code that
take the I2C address of the sensor as
an argument. This is a good technique
since if we later want to change the I2C
address, we only need to change one
value (the 0x92) rather than change all
places where it is used.
Defined constants always start with
a “#” character, which is supposed
to be reminiscent of the “#define”
preprocessor directive in C. Note that
these define constants can be redefined
but the compiler will warn you if this
happens.
We first open the I2C bus and set it to
run at 400kHz, using the built-in global
function <at><at>openI2C. We then set the
precision to three decimal places and
enter the main loop.
We declare a local variable called
$RESULT which takes the value returned by the built-in global function
<at><at>putI2CByte. The latter takes two
arguments. The first is the I2C address,
while the second is the single byte to
write to the I2C bus. In this case, we
simply write 0 since we interested in
reading addresses 0 and 1.
The <at><at>putI2CByte function returns a value of 1 if the command succeeded or 0 otherwise. For example, if
there is no sensor connected, the function will fail. We check for this using
the built-in command IF(){ } ELSE { }
which executes the first block of code
if the condition evaluates to non-zero
or the last command block otherwise.
If the function returns 0, it logs an
“ERROR” message and goes to sleep
for 30 seconds before retrying.
siliconchip.com.au
Script 4: AD7414 Digital Temperature Sensor
HEADER myI2CHeader
{
// Basic Script Showing How To Read and Log a Temperature from an:
// AD7414 digital I2C sensor, by Mauro Grassi.
// Define a Constant which is the sensor’s I2C Address
#I2C_ADDRESS=0x92;
}
This view shows the completed USB
Data Logger with the memory card
plugged in.
We read from the sensor using the
built-in global function <at><at>getI2C.
This function takes two arguments.
The first is the address and the second
is the number of bytes to read.
Note that the address register inside
the sensor itself will automatically
increment on each read, so we use this
function to read the bytes at addresses
0 and 1. Again, it returns 1 if successful
or 0 otherwise.
If successful, the data is written to
an internal buffer which is a global
variable $$I2C. Global variables are
defined for all scripts and their names
always start with two “$” characters,
as opposed to local variables. In this
case, we use the round brackets “( )” to
specify offsets of 0 and 1 to the buffer.
This reads the data as a byte, whereas
using square “[ ]” brackets reads it as
a 32-bit floating-point number.
In this case, $$I2C(0) represents the
eight MSBs (most significant bits) of
the 10-bit temperature, while the two
MSBs (most significant bits) of <at><at>
I2C(1) represent the two LSBs of the
10-bit temperature. The temperature
siliconchip.com.au
SCRIPT myI2CScript
{
// Open the I2C bus, running at 400kHz...
<at><at>openI2C(400);
PRECISION(3);
WHILE(1)
{
// Write the Address Register
$RESULT=<at><at>putI2CByte(#I2C_ADDRESS, 0);
IF($RESULT)
{
// Read Two Bytes From The Sensor (the address increments automatically)
$RESULT=<at><at>getI2C(#I2C_ADDRESS, 2);
IF($RESULT)
{
// Compute the Temperature
$T=$$I2C(0)+(($$I2C(1) & 0xC0)/256.0);
PRINT “The Temperature is “, $T, “ degrees Celsius”, NEWLINE;
}
}
ELSE
{
PRINT “Error”, NEWLINE;
}
SLEEP(30);
}
}
is stored in the local variable $T. The
script then logs the value and ends up
at the SLEEP(30); command which
suspends execution for 30 seconds,
before the cycle repeats.
Note that it’s possible to sleep for
a variable amount on each cycle. For
example, in the script presented above,
if the I2C temperature sensor read gives
an error, we could choose to retry in
three seconds, rather than 30. You
would simply move the SLEEP(30);
command inside the first block of the
IF statement and add a SLEEP(3);
command after the PRINT “Error”,
NEWLINE; command.
Conclusion
The general pattern in all these cases
is that each script begins by executing an initialisation sequence. It then
enters the main loop, executes some
code and then goes to sleep until the
next cycle begins.
Of course, what you do is up to you.
The VM (virtual machine) engine is
multitasking, so scripts are suspended
after a certain amount of time if they
don’t voluntarily go to sleep!
As stated, the ability to run custom
scripts from the memory card allows
the unit to interface to almost any
sensor you can think of, as well as to
do novel things, such as analyse the
data or monitor the sensors (ie, take
different actions on certain conditions
being met).
In next month’s final article, we will
run through the PC host program and
show you how to compile and run
custom scripts. More details and examples, including a “Tips & Tricks” section on how to use the custom scripting
language will be given as well.
SC
January 2011 43
|