Troubles with analog temp sensors

Hi

I’m scratching my head on this. I’m observing similar results over different analog sensors (i.e. different model numbers AND different devices) and different boards. Which makes me think that there is something wrong either with my board design or my code.

Basically the readings I am getting are always in the low mV. translating to a high temperature (100+C) - which considering i’m sitting in my kitchen with two jumpers and shivering slightly is an unlikely result.

the code is as follows

#include <Arduino.h>
#include <util/crc16.h>
#include <avr/pgmspace.h>
#include <util/parity.h>

void calcTemp();
int nearest(float x);



const byte tempPowerPin = A2;
const byte tempSensorPin = A3;


int curTemperature;
bool hasChanged;
const int mvs[201] = {1299, 1294, 1289, 1284, 1278, 1273, 1268, 1263, 1257, 1252, 1247, 1242, 1236, 1231, 1226, 1221, 1215, 1210, 1205, 1200, 1194, 1189, 1184, 1178, 1173, 1168, 1162, 1157, 1152, 1146, 1141, 1136, 1130, 1125, 1120, 1114, 1109, 1104, 1098, 1093, 1088, 1082, 1077, 1072, 1066, 1061, 1055, 1050, 1044, 1039, 1034, 1028, 1023, 1017, 1012, 1007, 1001, 996, 990, 985, 980, 974, 969, 963, 958, 952, 947, 941, 936, 931, 925, 920, 914, 909, 903, 898, 892, 887, 882, 876, 871, 865, 860, 854, 849, 843, 838, 832, 827, 821, 816, 810, 804, 799, 793, 788, 782, 777, 771, 766, 760, 754, 749, 743, 738, 732, 726, 721, 715, 710, 704, 698, 693, 687, 681, 676, 670, 664, 659, 653, 647, 642, 636, 630, 625, 619, 613, 608, 602, 596, 591, 585, 579, 574, 568, 562, 557, 551, 545, 539, 534, 528, 522, 517, 511, 505, 499, 494, 488, 482, 476, 471, 465, 459, 453, 448, 442, 436, 430, 425, 419, 413, 407, 401, 396, 390, 384, 378, 372, 367, 361, 355, 349, 343, 337, 332, 326, 320, 314, 308, 302, 296, 291, 285, 279, 273, 267, 261, 255, 249, 243, 237, 231, 225, 219, 213, 207, 201, 195, 189, 183};

void calcTemp() {
  long reading=0;
  int _reading = 0;
  float mv;
  float finalReading;
  for (byte i = 0; i <= 15; i++) {
    _reading = analogRead(tempSensorPin);
    if (i > 0) {
      reading = _reading + reading;
    }
  }
  finalReading = (reading/15.0);
  mv = (finalReading/1023.0) * 1100.0;
  int index = nearest(mv);
  int _curTemperature = index - 50;
  if (_curTemperature != curTemperature) {
    curTemperature = _curTemperature;
    hasChanged = true;
  }
}

int nearest(float x) {
  int idx = 0; // by default near first element
  int distance = abs(mvs[idx] - x);
  for (int i = 1; i < (sizeof(mvs) / sizeof(mvs[0])); i++) {
    int d = abs(mvs[i] - x);
    if (d < distance)
    {
      idx = i;
      distance = d;
    }
  }
  return idx;
}


void setup() {
  // put your setup code here, to run once:
  pinMode(tempPowerPin, OUTPUT);
  digitalWrite(tempPowerPin, HIGH);
  pinMode(tempSensorPin, INPUT);
  analogReference(INTERNAL);
}

void loop(){
	calcTemp();
	//delay(10000);
}

the hardware design is very simple - an atmega328p with 10nF decoupling caps and 10kpullup on reset.
A2 connected as a power pin and A3 to the output of the analog temp sensor. When this bits gets sorted out I will add a radio to the board and pop it in a box. There will be 20 of them.

the eagle files are here: https://1drv.ms/f/s!Aob6wYJSSDO0i_ppDmovt2RL5fSs-g

images of them for easy reference are here:

and a photo of the board I am actually working on is here:

(i have tested five in this state, some with LMT84 and some with LM94022 (both in the 5 pin SC-70 package).

can anyone shed any light on the consistently erroneous readings please? Just for the avoidance of doubt, the readings got on the uC are broadly consistent with direct voltmeter readings on the output pin of the temp sensors (albeit they are still connected to the uC at that time).

I’m feeding the board five volts at the moment. Although this is within tolerance, in production they will be running off two AAA cells. The board will be in sleep mode most of the time and i will need to hardwire the power to the temp sensor ultimately as the latency is too slow (10 secs).

thanks in advance,
Justin

Ummm… not what you asked… but why are you using an analog sensor? The simple DS18B20s are very popular because they are cheap, easy to use, and dependable!

What kind of ADC results are you getting from A3 at room temperature? Looks like 25C should be around 835, 20C around 860.

Why using a lookup table? It’s easier (and probably more accurate) to simply calculate the value in code. Saves a lot of memory, too: that table is 402 bytes out of your 2048 available. It’s also highly linear, making it even stranger to use a lookup table rather than a formula.

You use INTERNAL for ADC reference, are you sure about that part?

void calcTemp() {
  long reading=0;
  int _reading = 0;
  float mv;
  float finalReading;
  for (byte i = 0; i <= 15; i++) {
    _reading = analogRead(tempSensorPin);
    if (i > 0) {
      reading = _reading + reading;
    }
  }
  finalReading = (reading/15.0);
  mv = (finalReading/1023.0) * 1100.0;
  int index = nearest(mv);

There’s a lot wrong here.
Take 16 samples instead of 15, and you can replace your floating point calculation by a simple bitwise operator.
And why that i>0 test? You don’t want the first one?

So let’s fix that part:

  for (byte i = 0; i <= 16; i++) {
    _reading = analogRead(tempSensorPin);
    if (i > 0) { // Dump the first reading; record the other 16.
      reading = _reading + reading;
    }
  }
  finalReading = reading >> 4; // right-shift four bits is the same as dividing by 16, just a lot faster and much smaller code.

Then the temperature calculation.

  mv = (finalReading/1023.0) * 1100.0;

Having to do this means you didn’t calculate your lookup table properly. Based on the numbers in your sketch you should get the reading by this simple calculation:

float temp = reading * -0.19364 + 183.92;

Putting this all together you can reduce your sketch to this:

#include <Arduino.h>
#include <util/crc16.h>
#include <avr/pgmspace.h>
#include <util/parity.h>

const byte tempPowerPin = A2;
const byte tempSensorPin = A3;

int curTemperature;
bool hasChanged;

void calcTemp() {
  long totalReading = 0;
  hasChanged = false;
  for (byte i = 0; i <= 16; i++) {
    int reading = analogRead(tempSensorPin);
    if (i > 0) {
      totalReading += reading;
    }
  }
  int temp = (totalReading >> 4) * -0.19364 + 183.92;
  if (temp != curTemperature) {
    curTemperature = temp;
    hasChanged = true;
  }
}

void setup() {
  pinMode(tempPowerPin, OUTPUT);
  digitalWrite(tempPowerPin, HIGH);
  pinMode(tempSensorPin, INPUT);
  analogReference(INTERNAL);
}

void loop(){
  calcTemp();
  //delay(10000);
}

You can see significant savings in size as well. Your sketch:

Sketch uses 2668 bytes (8%) of program storage space. Maximum is 30720 bytes.
Global variables use 416 bytes (20%) of dynamic memory, leaving 1632 bytes for local variables. Maximum is 2048 bytes.

My version:

Sketch uses 1798 bytes (5%) of program storage space. Maximum is 30720 bytes.
Global variables use 14 bytes (0%) of dynamic memory, leaving 2034 bytes for local variables. Maximum is 2048 bytes.

33% smaller size, 97% less memory for global variables.

Thanks for the replies.

@tkbyd I'm using an analog sensor because they can work at very low voltage, have very low quiescent current and are very cheap. i could move to a digital variant but would need one that sat flush to the board as there is no head height in the enclosure. Obviously i'd need to redesign the board too!

@outsider the ADC is reporting around 290-300 per measurement. consistently across boards and sensors.

@wvmarle lots of things to reply to. here goes:

why use a transfer table? Why not? i've got lots of memory headroom and for debugging it eliminates all maths errors (as well as avoiding too much floating math)

using the 1.1v reference - yes. I'm sure. i know the limits that the sensor will work in and the better resolution of the internal reference is desirable.

number of samples: i am taking 16 samples and discarding the first. it's a hangover from the initial code where the analog reference gets changed and you need to wait for stability.

good point on the bitmath. thanks.

it is possible that your math on the lookup table gives the right answer. But remember that the lookup table may appear as if the sensor response is linear, but it is not. It is parabolic. The lookup table is accurate (according to the datasheet).

In any event, your code versus mine produces much the same result - and this is just debug code to test the sensors, not remotely production code.

So, chaps, the questions still beg as to what would cause this board and code to read consistently incorrectly across power supplies, boards, sensors and sensor types? Apparently its 130C in my kitchen.

thanks Justin

jpadie: why use a transfer table? Why not? i've got lots of memory headroom and for debugging it eliminates all maths errors (as well as avoiding too much floating math)

My solution probably has less floating point maths. You still have to do a   mv = (finalReading/1023.0) * 1100.0; calculation. I basically replaced that one with the complete conversion.

it is possible that your math on the lookup table gives the right answer. But remember that the lookup table may appear as if the sensor response is linear, but it is not. It is parabolic. The lookup table is accurate (according to the datasheet).

I noticed at one end of the table the steps are 5-6, at the other end the steps are 6. So it may be a parabolic reading, over this part of the readings it's very close to linear. Plotting the readings also gives a near-perfectly straight line. Errors from a linear approximation are almost certainly far less than the errors of the sensor itself. Actually the spec sheet itself mentions it's a "selectable linear negative temperature coefficient".

In any event, your code versus mine produces much the same result - and this is just debug code to test the sensors, not remotely production code.

Well, that proves my code is functional the same as yours :) It should produce the same output.

Hold the sensor in your hands, see what temperature you get (should be around 33°C if you have normally warm hands). Put it in the fridge, see what temperature you get (should be around 4°C). You should see the calculated temperature value drop. Also good for debugging is to have the actual ADC value you have measured printed on the Serial console. You should get values in the 800-range according to the spec sheet.

Yup. I'm not seeing sensible values as I mentioned. But they are consistent. 2-300mV.

Just hooked one up directly to two aaa cells and took some voltage readings. 180mv after 60 seconds.

I'm wondering whether I'm killing them all with the soldering process (hot air for a couple of seconds but I've also tried a crude reflow oven too).

Killing a part would normally result in no output, but at least you should see a clear difference in different temperature regions.

It makes me wonder: is your board correct?
Did you not by chance mix up top view/bottom view when designing the footprints, something like that?

It’s a good thought, but no. The wiring to the sensors is correct according to the data sheets.

I will try some readings in different t temperature zones and see what comes up.

Did you try the same circuit built up using breakout boards and so of the sensors?

I don't see anything strange in your posted schematic, but that doesn't necessarily mean it's correct.

nope. damn difficult to solder those tiny packages on perfboard.

i will try to make up a couple more sensor only boards tomorrow night and see whether there is any difference.

thanks for the advice so far justin

You can't expect the ADC to work at all without decoupling on AREF. 100nF to ground on the AREF pin is a requirement for accurate readings.

I'm a bit sceptical about running the sensor itself without decoupling, an Arduino pin set to HIGH is not a very stiff supply, its about 40 ohms or so, but given its a micropower sensor that doesn't seem too bad. But there's always the risk of parasitics when you power things from something that isn't stable. If the sensor really only does take a few uA supply, no point making it switchable really, just power it direct.

Check the sensors outputs with a multimeter when powered from a genuine supply voltage, compare to that in-circuit. Should narrow down the field.

You say several sensors (several types?) were tested - but same problem? Odd. Genuine parts?

If using the internal reference, be aware there's a big spread between chips and individual calibration is needed.

MarkT: If using the internal reference, be aware there's a big spread between chips and individual calibration is needed.

OP is getting a reading of about 300 on what should be an 800 mV signal with 1.1V reference.

So either the signal is indeed at 330 mV, or the reference voltage is at about 3V. That's way too far off either way to be explained by normal variation.

What kind of ADC reading are you getting with 100C in your kitchen? Are you sure you have programmed for a 1.1V AREF? Which Arduino? 1.1V / 5V = 0.22 * 100C = 22C Did you miss the TMP36, only 0.5 microAmps in shutdown mode and its linear, -40 to 125C TMP36

@outsider
i am sure about my code stipulating the 1.1v internal reference. i’m not using an arduino but an atmega328p.
i’ve tried a few analog sensors. i’m just getting garbage from all of them.

i’ve wired a few more up on the boards, directly powering Vin with AA cells (2) and having no other components on the board. still getting garbage on the output (sometimes 300mVish and sometimes 150mVish). it’s almost as if there is nothing coming out at all and the multimeter is just displaying arbitrary rubbish as it might without the probes touching anything.

ive also wired a lmt94 up without a board. same rubbish.

very minor variations with temp changes (fridge, hands, heatgun) (i.e. less than 50mv).

so either it happens that every sensor i’ve got from mouser (lmt94 and the 94022s) are bust or by soldering or reflowing I’m killing them.

I’m giving up on this. I’ll investigate digital sensors and redesign the board if I find something suitable. the tmp36 needs more voltage than i have planned for at the low end. these will run on 2aaa bats and i’d like them to keep going until under 2.5v. something like a tmp102 should be ok.

Another simple solution would be a thermistor. That works at any voltage. Use Vcc as reference voltage for the ADC and it's independent of actual voltages. Use a 100k nominal version for lower current.

Running the ATmega at that low voltage is another issue, though. You'll have to bring down the oscillator frequency to 1 MHz (but it does save a lot on power that way).

i think at 2.5v i can eke out 8Mhz. I may be able to get away with a lower frequency but that may interact negatively with the SPI comms with the radio. I will have a look. In any event, the plan is that at 2.7v the device sends out a lo-batt warning and then slows down its sampling rate even further; and adds hysteresis before sending the new value across the radio.

thermistor may also be an idea. I can't remember now why I decided not to use them originally. Some research to do over the weekend.

thanks again Justin

For the speed you're right. Just checked the data sheet, it's rated 10 MHz at 2.7V so 8 MHz at 2.5V sounds reasonable (minimum voltage 1.8V is still good for 4 MHz). I think I mixed it up with the ATtiny.