I did a bunch of testing today with my TMP36 temperature sensor to try and get to the bottom of why the readings are so wacky. This is a continuation of the Odd analog readings thread, but I felt you might want to find this information without reading 55 replies
How to get accurate analog readings:
Hook your analog sensor to the 3V3* pin and GND. The 3V3* pin has a low pass filter on it and is the same node that is connected to the Analog to Digital convertorās reference voltage (VDDA).
Keep your wires as short as possible, and avoid crossing over the top of the Spark Core and the Wifi antenna if you can.
If your analog sensor has a high impedance output, you are going to need to place a 0.01uF (usually marked 103) capacitor from the analog input pin (A0-A7) to GND. This effectively lowers the impedance of the input, and allows the ADC to convert the voltage to a digital value properly. This is by far the BEST thing you can do to help your readings stabilize (thus the double bullet). More on this later.
If you analog sensor has a high impedance output, AND you donāt want to install a capacitor to help lower the impedanceā¦ you might try to delay your analogRead() calls by as much as once per second to help get readings closer to where they should be. You will still see values wildly swinging around, but this helps in a pinch.
If you are using resistance or voltage values in your equations, take a multimeter and measure the value of these items and put the exact values in your equations where possible. Your 3V3* pin most likely does not output exactly 3.30V. This may affect your results slightly.
If your sensor is a temperature sensor, be sure to mount it to a separate breadboard or on the end of a short cable away from the Core. The reason for this, is after a while runningā¦ the Spark Core Wifi module and 3.3V regulator warm up to over 100Ā°F. This temperature is thermally coupled into all 24 of the pins that are pressed into the breadboard, which in turn heats up the metal pins in each rail. Even if you donāt plug your temp sensor directly into these pins, you can be sure the entire bread board is heating up. My TMP36 was running 11Ā°F higher than ambient because of this. As soon as I moved it off to another breadboard, the temperature immediately dropped down to the correct value.
Many sensors benefit from adding a decoupling capacitor across their power and gnd inputs, to help reject common mode noise from entering the sensor and affecting the output. Typical values here are 0.1uF. Keep the leads short as possible.
Code can easily go bad fast, so if you are having problemsā¦ start by measuring the voltage at the analog input, and calculating what your analog reading should be. See if that is the value you are getting before it goes through your conversions. Break up each step of your conversion to find out where a problem may exist. Follow along with a calculator and see if you are getting numbers that are too big or too small for the types of variables are using.
Have you done all of the above and you still get bouncy readings? You might just have a noisy sensor, or a noisy environmentā¦ or maybe the thing you are sensing is just fluctuating. If you donāt want to set up a low pass filter in hardware, you can try to create a software filter. [I like to use this dilution filter]2 but there are a ton of different ways to do it, pros and cons for many of the ways.
Ok on with the hours of testing I did today with the TMP36:
So one long standing question is whether or not the ADC is setup correctly. I never tried changing the ADC_SAMPLING_TIME
found in spark_wiring.h
until today. Currently itās set to ADC_SampleTime_1Cycles5
and you can see a good write up of why I think thatās bad here.
So I tried 3 different Sample Times and 4 different levels of capacitance on the input pin. Keep in mind I have my TMP36 on a separate breadboard, and Iām using the A7 input so I can have a GND pin close by for my caps. In addition to measuring the temp, I also measured how long the A to D conversion was taking (Conv. Time). Notes: 10000pF = 0.01uF, and my room thermostat was set to 70Ā°F, These are averages of 100 readings taken 100ms apart.
You can see that with zero capacitance at the sample time of 1Cycles5 (1.5), the situation is pretty bad. Temperature is averaging 21Ā°F higher than normal. And was regularly spiking up to 100Ā°F. Adding just 100pF of capacitance brings the average reading down to within 8Ā°F. Not bad. 470pF improves this even more, and 0.01uF is spot on. The readings barely fluctuated more than 0.3Ā°F with the 0.1uF cap. Conversion times are hella fast, 5us.
sample time of 41.5 and 239.5 effectively increase the allowable input impedance for our circuit, and as you can see even with zero capacitance the readings on average are pretty good! Adding capacitance doesnāt change the average much, but it does improve the variability between readings. Conversion time respectively increases to 8.5us and 25us. These are still hella fast.
To keep the conversion time fast, but also help to improve the readings for users that have no idea they should add a capacitor to the analog pin, Iām recommending changing the ADC Sample Time to 41.5.
Check out the variability in the graphs! Be sure to pay attention to the change in temperature scale between the graphs.
Hereās my Spark Core test code:
#include <application.h>
uint16_t temperature = 0;
float voltage = 0.0;
float t1 = 0.0;
bool s = 1;
char tempStr[20];
uint32_t start,end;
uint16_t sample = 0;
void setup()
{
pinMode(D7, OUTPUT);
Serial.begin(115200);
RGB.control( true );
RGB.brightness(255);
while(!Serial.available()) {
// Run some test code so we know the core is running!
s = !s; // toggle the state
if(s) {
RGB.color(255,255,255);
delay(10); // makes it blippy
} else {
RGB.color(0,0,0);
delay(50);
}
}
RGB.brightness(64);
RGB.control( false );
Serial.println("SAMPLE, CONV TIME, TEMP C, TEMP F");
}
void loop()
{
start = micros();
temperature = analogRead(A7);
end = micros();
voltage = (temperature * 3.3)/4095.0;
t1 = (voltage - 0.5) * 100.0;
sprintf(tempStr,"%d, %d, %.2f, %.2f",++sample,end-start,t1,(t1*1.8+32));
Serial.println(tempStr);
// Run some test code so we know the core is running!
//digitalWrite(D7,s);
//s = !s; // toggle the state
delay(100); // makes it blinky
}
Comments? questions?