This page should really be called "Using the Arduino as a Really Inexpensive DAQ." This is where we dive into the limits of the using the Arduino as a data collector. Here you will find the information you need to know in order to determine when to move on to another board, or a dedicated DAQ. The photo shows the Arduino Mega setup used for testing - a piece of bent acrylic with a section cut out for mounting a serial LCD and a couple of breadboards glued in place. The Mega, using a 1280 chip, runs at the same speed as the Atmega based 328 Arduino boards, the only difference being some extra memory and more I/O ports, and of course a higher price tag ($37 to $39 on ebay). Also note that the Mega does not have a replaceable chip, so this is probably not the board to use for experimentation. That said, I am using the Mega (do as I say, not as I do), and the results are equally valid for any Atmel, 5 volt, 328 chip running at 16 MHz, i.e the common (for good reason) Arduino Uno.
Before you collect data, their are two things you need to know, sample rate, resolution, and responsiveness. I added responsiveness because of comments like this. Are these comments valid? We'll see. If you have a drive shaft capable of spinning at 10,000 rpm (167 revolutions per second), it doesn't do any good to hook it up to a collector that can only sample at 100 times a second. Likewise, suppose you have a sensor hooked up to that same shaft that measures the reflection from a thin white stripe drawn along the axis of the otherwise black background. Let's say the voltage variation due to the increased light reflection of the white stripe passing by the sensor drives the voltage of the sensor from 0 volts to .05 volts. We are merely wasting our time if our collector can only measure voltage changes of .1 volt; that's the importance of resolution. So what is an Arduino capable of?
First we'll look at the resolution issue. The analog to digital converters (ADC) in the Arduino are considered to have 10 bit resolution. The bits of resolution is an obfuscation that means 2^10, or 1024 divisions (0 to 1023), of the reference voltage, If someone tries to sell you a DAQ with with 4 bit resolution, you'll probably want to pass, since this would divide the reference voltage into 2^4, or 16 parts (0 to 15 steps). For an Arduino, the reference voltage is usually 5 volts, and that means the smallest detectable voltage variation is 5/1023 or .0049 volts (4.9 mV). That's not too bad, and there are some easy ways to adjust the system for even better resolution. The Arduino ADC ports are normally tied to a 5 volt reference, but this is adjustable. Let's say you have a sensor that outputs in the millivolt (mV) range and thus 0.001 volt is significant. We can easily use software to change the ADC port reference to 1.1 volts, and that gives us a resolution of 1.1/1023 or 0.0011. Close but still not quite enough. That same referenced page gives us another route, the AREF pin can be tied to and external voltage source, andl, let's say that source is 0.5 volts. Now we can detect a voltage of 0.5/1023, or .00049 volts (0.49 mV). Of course there are other issues involved such as circuit noise and stability, and I haven't really tried to measure down to these tolerances; but in theory, it should be possible, and note that this is closing in on 14 bit resolution at a non-adjustable 5 volt level.
You can use a voltage divider to set a non-standard (not 5, 3.3, or 1.1) voltage. The manual says to always place a voltage on the AREF pin using a 5K (5000) ohm, or better, resistance. With that in mind, lets use one of the many voltage divider calculators on the web with 5000 ohms for R1, 0.5 volts for Vout, and 5.0 volts for Vin. The calculator gives us
The moral here is to beware of running the board on USB power only.
Now, we tackle sample rates, and this gets a bit tricky. A DAQ does nothing but collect data at given time intervals. If we tell a DAQ to sample temperature at 500 samples a second, you get 500 voltage values every second, and you can be assured that each sample was taken precisely at 0.002 seconds (2 ms) spacing. There is no need to collect the time from a DAQ, since we know the time spacing between each sample. This timing certainty doesn't hold with the Arduino. The Arduino does not sample at precise intervals unless we force it to do so (with some difficulty). If the Arduino is looping, or calculating, there is a certain amount of CPU time being dedicated to those tasks. Data collection is not the primary focus of the Arduino, indeed it seems more like an afterthought. So, the easiest way to collect data with an Arduino is to just collect a time stamp and then the voltage signal. This is especially true if you are going to do calculations on the data.
To demonstrate Arduino samthe definitive tutorial on using this sensor with the Arduino, and I won't even try to improve on her fantastic writeup with one exception. Note the output of the sensor maxes out around 2 volts. To increase our resolution, we'll tie the AREF terminal to the 3.3 volt terminal using a 1K resistor (I know I said 5K before, but 1K works). A pushbutton switch has also been added that keeps digital pin 2 tied to ground until pushed and then it momentarily goes high. Physically, the temperature sensor looks like a transistor (same case), and the +5 vdc goes to the left hand pin when looking at the flat side of the case with leads extended downward (red wire in the picture where it is shown upside down).
A 1000 ohm resistor is used to limit current. The documentation talks about a 5K resistor, and I was surprise by the voltage drop across that value. I then used a 10K variable resistor and dialed it down, ad saw that AREF was definitely pulling current because there was a significant voltage drop across the resistor. With 1K, that drop was 0.12 volts which surprisingly enough pulled the 3.42 volt to 3.3v - just what we wanted using a commonly found resistor value..
Now we'll write some simple code that will send temperature results to the serial monitor running at the snail's pace of 9600 baud. You can leave out LED13 and the pushbutton, of course.
Here is a snippet of the output gathered using ttylog (ttylog -b 9600 -/dev/ttyUSB0 >> data.txt), there are similar programs in Windows like TeraTerm:
The first column is the time in milliseconds. Note the rate is 37 to 38 ms per iteration. That's a sample rate of 26 samples per second. Works for a slow moving temperature scan, but not much else. So lets try running the same thing at 115200 baud. At that speed the average time interval between readings is 4 ms, giving a sample rate of 250 samples per second. So speeding up the serial transfer rate to the maximum (and a bit unstable) speed of 115200 gives a 10 fold increase. Can we do better? How about if we strip it down to bare bones.
The output looks something like:
Now the time between reads is wavering between 1924 and 1932 microseconds, giving a sample rate per second of 517. Still painfully low. Let's bypass the serial bus and do a short burst in memory. This technique won't be suitable for continuous sampling because the 8K memory (or 2K on an Atmega 328) is going to fill up real fast (remember sram is where variables are kept not the 128K/32K flash which is program storage space), especially with an unsigned long integer, but here goes. This code will give us the maximum sample rate allowable without resorting to interrupts and assembly code (we may come back to the interrupts though). If we have to do that, it's time to look at a low cost DAQ.
The output for one iteration is:
Now, the time between samples is down to 120 microseconds, and that gives a sample rate of 8300 samples a second, being a decent rate for an inexpensive DAQ. The trick will be trying to get a continuous sample from the system at rates above 500 samples per second.
Another limitation that has not been discussed so far is the ability of the ADC to recover. Are we fooling ourselves in thinking that an 8K sampling rate is truly obtainable? The temperature sensor was hardly fluctuating, so it doesn't clue us in on the ADC limitations in terms of responsiveness. So, before going any further, the next test needs to be a sample of a fast fluctuating zero to five volt signal. We'll use a sine wave generator operating at 1000 Hz and 0 to 5 volts amplitude. If we truly have a responsive system at an approximate 8K sample rate, we should get eight representative samples along each sine cycle. This ought to be interesting. Here's the code:
And, here is a graph of the output and a snippet of the 100 samples where the first column is time in microseconds, the second column is the rawdata in the form of 0 to 1023 corresponding to 0 to 5.02 volts, and the last column was added in a spreadsheet, and it is the time in microseconds between entries Lines of data).
There doesn't appear to be a problem with ADC responsiveness at 8.33K samples per second. Also, note that the use of the FOR loop did not cause any slowdown in collection EXCEPT for the initial sample. So now, it's just a matter of figuring out the right tricks to use to make the Arduino an easily programmed inexpensive DAQ. So, we know that when using the Arduino IDE (instead of native assembly language), with a 16 MHz chip,we are limited to just over 8000 samples per second, and while we always have 10 bit resolution, that resolution can be adjusted to match the input voltage - a real plus over fixed reference voltages.
This section kicks off with this very interesting thread:
Hmmm, 77 kHz you say? Definitely going to look into this!
Well here are the results (only the first column is from the Arduino - the rest was calculated using gnumeric) of the samples collected from the analog port of a 1000 Hz signal. Not 77K, because that's theoretical, and the actual program has to collect and store samples in memory, but how's 56K? There is no such thing as a 56K sample per second commercial DAQ for under $100, but we now have one for less than $30!
Yep, you're correct. There are still a couple of problems to iron out. First, we've got to be able to move data out at the same rate it's being collected or we're limited to just a few hundred samples. Serial, even at 115,200 baud, is not going to do it, but once again the Arduino forums bail us out:
i2c can run at 400kbits, providing a transfer rate of 50K bytes per second. So, we can reduce the resolution and go with a map to convert integers to bytes and get a 50K sample per second stream, or be reduced to a 25K sample rate for full resolution. This setup is relatively painless, and there are memory chips that "talk" i2c. But there is an even better, if more complicated solution, and that is using the SPI communications interface which runs at a whooping 4 Mbits. This is what most SD/MMC card interfaces use, and it looks like the route to take. Information can be found here,
and with many other postings.
According to the main Arduino site there are soon to be official Megas with SD memory cards installed and officially supported. When these show up, we'll continue the chronicles. I'm betting we have an awesome, programmable DAQ with deep logging capabilities for about $60.
To be continued...
In the meantime, here is an interesting project that moves around a bunch of data, and of course, LadyAda is always in the mix...