Silicon ChipDigital/Analog USB Data Logger, Pt.2 - January 2011 SILICON CHIP
  1. Outer Front Cover
  2. Contents
  3. Publisher's Letter: Gas-fired trigeneration is a worthwhile concept
  4. Feature: VAST: Australia’s New Digital TV Satellite Service by Garry Cratt
  5. Review: Tekway DST1102B 100MHz DSO by Jim Rowe
  6. Project: Cheap-N-Easy 433MHz Sniffer by Stan Swan
  7. Project: Cranial Electrical Stimulation Unit by Robert Scott
  8. Project: Digital/Analog USB Data Logger, Pt.2 by Mauro Grassi
  9. Feature: A Cheap High-Current Bench Power Supply by Nicholas VInen
  10. Project: Hearing Loop Signal Conditioner by John Clarke
  11. Vintage Radio: Portable HF transceivers used in Victorian forests by Rodney Champness
  12. Book Store
  13. Advertising Index
  14. Outer Back Cover

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":
  • 433MHz Sniffer PCB [06101111] (AUD $7.50)
  • Cheap-N-Easy 433MHz Sniffer PCB pattern (PDF download) [06101111] (Free)
Items relevant to "Cranial Electrical Stimulation Unit":
  • Cranial Electrical Stimulation Unit PCB [99101111] (AUD $20.00)
  • Cranial Electrical Stimulation Unit PCB pattern (PDF download) [99101111] (Free)
  • Cranial Electrical Stimulation Unit front panel artwork (PDF download) (Free)
Items relevant to "Digital/Analog USB Data Logger, Pt.2":
  • PIC18F27J53-I/SP programmed for the Universal USB Data Logger [0411210A.HEX] (Programmed Microcontroller, AUD $20.00)
  • Universal USB Data Logger Software [0411210A.HEX] (Free)
  • Universal USB Data Logger User Manual (PDF download) (Software, Free)
  • USB Data Logger panel artwork (PDF download) (Free)
Articles in this series:
  • Digital/Analog USB Data Logger (December 2010)
  • Digital/Analog USB Data Logger (December 2010)
  • Digital/Analog USB Data Logger, Pt.2 (January 2011)
  • Digital/Analog USB Data Logger, Pt.2 (January 2011)
  • Digital/Analog USB Data Logger, Pt.3 (February 2011)
  • Digital/Analog USB Data Logger, Pt.3 (February 2011)
Items relevant to "Hearing Loop Signal Conditioner":
  • Hearing Loop Signal Conditioner PCB [01101111] (AUD $20.00)
  • Hearing Loop Signal Conditioner PCB pattern (PDF download) [01101111] (Free)
  • Hearing Loop Signal Conditioner front & rear panel artwork (PDF download) (Free)

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