I'm trying to get a decent signal out of the TMP36 temperature sensor that came with the Arduino Starter Kit, with limited results :-).
The first thing I tried was connecting one pin to 5V, one to GND and one to A0. In code I also calculate a moving average to smooth out the signal. The results of this can be found in basictmp36.png. There seems to be quite a lot of noise on this signal.
Then I tried to use the AREF pin, so I connected the 5V pin to the Arduino's 3.3V pin instead, and this same pin to AREF pin. Also In code I declared analogReference(EXTERNAL). The result can be found in tmp36_aref.png.
This is big improvement, but there still seem to be peaks quite often. These are smoothed out by the moving average but I think they should not be there at all.
Then I tried to power the Arduino itself using the DC jack instead of USB (12V / 3A DC adapter). The result in tmp36_aref_dcpower.png shows a small improvement but still the same peaks as before.
Is it a limitation of the TMP36 device itself?
Does it matter that when I power the Arduino via DC that USB is still connected? (I use Putty to write the serial data to file)
Are there other things I can do to improve the signal?
And my code is as below (aref_voltage value and the analogReference() call depend on situation of course):
#define aref_voltage 3.3 // Connect 3.3V to ARef
const int RunningAverageCount = 8;
double RunningAverageBuffer[RunningAverageCount];
int NextRunningAverage = 0;
//TMP36 Pin Variables
int tempPin = A0; //the analog pin the TMP36's Vout (sense) pin is connected to
//the resolution is 10 mV / degree centigrade with a
//500 mV offset to allow for negative temperatures
int tempReading; // the analog reading from the sensor
void setup(void)
{
Serial.begin(9600);
analogReference(EXTERNAL); // If you want to set the aref to something other than 5v
}
void loop(void) {
tempReading = analogRead(tempPin);
// converting that reading to voltage, which is based off the reference voltage
double voltage = tempReading * aref_voltage;
voltage /= 1024.0;
// now print out the temperature
double temperatureC = (voltage - 0.5) * 100 ; //converting from 10 mv per degree with 500 mV offset
//to degrees ((volatge - 500mV) times 100)
RunningAverageBuffer[NextRunningAverage++] = temperatureC;
if (NextRunningAverage >= RunningAverageCount)
{
NextRunningAverage = 0;
}
double RunningAverageTemperature = 0;
for (int i = 0; i < RunningAverageCount; i++)
{
RunningAverageTemperature += RunningAverageBuffer[i];
}
double filterTemp = RunningAverageTemperature / RunningAverageCount;
Serial.print(temperatureC);
Serial.print(",");
Serial.println(filterTemp);
delay(20);
}
What you are seeing is the result of using insufficient ADC resolution for your signal. With a 5v span (your first case) the 10-bit ADC of the Uno (? what Arduino are you using?) will give roughly 5 mV resolution. With the sensor, that translates to a half degree difference. Notice how your raw output signal varies between only four numbers -- that is a red flag. You are really beyond the limits of what the ADC of that Arduino can do. The noise which is typical in the ADC's LSB is getting translated to a whopping half degree of output. That's not real.
To fix this, you have several of options. One is to amplify the signal to spread its output over the full 5v range of the ADC. Or you can use a lower Vref, set to the highest expected voltage output of the sensor. Or you can use an Arduino with a better ADC. Some of the Nanos have 12 bit ADCs. This is the resolution that I currently use for most of the temperature monitoring that I do, and it is at the lower end of what is acceptable (IMO). Finally, you can use the Arduino to operate an external ADC which has better resolution.
The moving average is not the best filter to filter high frequency electronic noise.
If you would take the average of 100 analogRead() instead of just one analogRead() will reduce the noise a lot. It is even possible to get more resolution than the 10-bits of the Arduino Uno. See my averageRead.ino.
How often do you want to read the temperature ? 50 times per second or just once per second ?
If you would read the temperature once per second, then a moving average can be helpful, but also a low-pass filter will work.
The best option is to do what we all do. Test a TMP36 sensor and see that it works (but not accurate), then buy a DS18B20 and forget about the TMP36
Power the sensor from the 3V3 which I have found to be very stable.
The sensor is NOT ratiometric, so you need to use a stable reference - the INTERNAL (1100mV) reference will do very well, giving you a range of -40 to +70C.
You can reduce the noise and improve precision by taking a set of readings from the ADC and finding the average.
My plan is to create a PID controller to keep an isolated box at a constant temperature for fermentation (beer brewing). Given that, reading the temperature sensor a bunch of times and running the control loop at 1Hz suits my needs even if the output is not perfect.
The figure below shows the PID response (first attempt, with only setting the Kp parameter) and it already works quite well :).
I'm going to play around with the PID parameters to fine tune!