Advertisements

Archive for December 12th, 2012

Arduino Snippets: Temperature Measurement

Temperature seems an obvious thing to measure, so a bit of rummaging disgorged a classic LM335 temperature sensor that produce an output voltage directly calibrated in Kelvin at 10 mV/K: room temperature runs 296 K = 2.96 V. Nothing could be easier than this:

LM335 Temperature Sensor

The downside: a 1 °C temperature change corresponds to only 10 mV, which is barely two LSB of the Arduino ADC. In round numbers, a 1 °F change = 1 LSB, which doesn’t leave much room for measurement noise. I average five successive readings, which may be excessive, but the result seems stable enough:

const float AVREF = 4.94;                    // Arduino analog reference

#define TAVG 5

float ReadLM335(byte Pin) {
float Kelvin;

	Kelvin = (float)analogRead(Pin);
	for (byte i = 1; i < TAVG; i++)
		Kelvin += (float)analogRead(Pin);

	return Kelvin * (100.0 * AVREF) / (TAVG * 1024.0);
}

For better accuracy, you must measure VCC on the Arduino board and plug that into the AVREF constant, because the ADC reference voltage comes from the power supply. If you’re powering the Arduino from a USB port, then don’t bother worrying about analog conversion accuracy, because VCC depends on which PC you use, the USB cable length, what load current you draw from the regulator, and probably the phase of the moon.

The magic number 100.0 converts 10 mV/K to K.

The four character DL1414 LED display works well enough for the kind of temperatures you might find around a human being and, if you have an LED bargraph display, you may as well throw that into the mix, too.

LM335 Temperature Sensor – 19 C

The bargraph has RRYYGGYYRR LEDs, so I scaled the temperature at 5 °C/bar and put 0 °C on the bottom of the display, which means 15-19 and 20-24 °C occupy the green bars in the middle. Fingertip temperatures light up the two yellow bars and body heat gets you into the red, so it’s a reasonable display. Just to show it works, here’s a closer look (0 °C is on the right, but you can reverse that easily enough):

LM335 Temperature Sensor – 25 C

The Arduino source code:

// LM335 Temperature sensor sensor
// Ed Nisley - KE4ANU - November 2012

//#include <stdio.h>
//#include <math.h>

//----------
// Pin assignments

const byte PIN_TEMPERATURE = A1;			// Temperature sensor - LM335 = 10 mV/K

const byte PIN_MOSI = 8;			// data to shift reg
const byte PIN_SCK  = 6;			// shift clock to shift reg
const byte PIN_RCKB  = 7;			// latch clock for LED Bargraph
const byte PIN_RCKC  = 12;			// latch clock for LED character display

const byte PIN_HEARTBEAT = 13;				// DO - Arduino LED

//----------
// Constants

const int UPDATEMS = 1000;					// update LEDs only this many ms apart

const float AVREF = 4.94;					// Arduino analog reference
const float KTOC = -273.2;					// Kelvin to Centigrade offset

const float BARSCALE = 5.0;					// degrees per bar increment

#define TCCRxB 0x02							// Timer prescaler

#define LED_SIZE		4				// chars per LED
#define LED_DISPLAYS	1				// number of displays
#define LED_CHARS		(LED_DISPLAYS * LED_SIZE)

union DL1414_ {
	word ShiftWord;				// word overlay
	struct {					// bitfield sent to the display
	unsigned int Addr:2;
	unsigned int NotWrite:1;
	unsigned int Ctl3_7:5;			// unused bits
	unsigned int Data:7;
	unsigned int Data7:1;			// unused bit
	} ShiftBits;
};

//----------
// Globals

int Temperature, BaseTemperature;

word LEDBits;

char LEDCharBuffer[LED_CHARS + 1] = "HELO";		// raw char buffer, can be used as a string

unsigned long MillisNow;
unsigned long MillisThen;

//-- Helper routine for printf()

int s_putc(char c, FILE *t) {
  Serial.write(c);
}

//-- Send bits to LED bar driver register

void SetBarBits(word Pattern) {

	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,Pattern >> 8);
	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,Pattern & 0x00ff);

	digitalWrite(PIN_RCKB,HIGH);
	digitalWrite(PIN_RCKB,LOW);

}

void PulsePinHigh(byte PinID) {
	digitalWrite(PinID,HIGH);
	digitalWrite(PinID,LOW);
}

//-- Write single char to DL1414

void WriteLEDChar(char Char,char CharID) {

	union DL1414_ DL1414;

	DL1414.ShiftBits.Data = Char & 0x7F;
	DL1414.ShiftBits.Addr = ~CharID & 0x03;		// reverse order of chars

	DL1414.ShiftBits.NotWrite = 1;				// set up data and address

	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord >> 8);
	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord & 0x00ff);
	PulsePinHigh(PIN_RCKC);

	//	delay(1000);

	DL1414.ShiftBits.NotWrite = 0;				// write the character

	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord >> 8);
	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord & 0x00ff);
	digitalWrite(PIN_RCKC,HIGH);
	PulsePinHigh(PIN_RCKC);

	//	delay(1000);

	DL1414.ShiftBits.NotWrite = 1;				// disable write

	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord >> 8);
	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord & 0x00ff);
	PulsePinHigh(PIN_RCKC);

	//	delay(1000);

}

void WriteLEDString(char *pString) {

	for (byte i=0; (i < LED_CHARS) && *pString; ++i)
		WriteLEDChar(*pString++,i);

	return;
}

//-- Sample temperature with a dab of averaging

#define TAVG 5

float ReadLM335(byte Pin) {
float Kelvin;

	Kelvin = (float)analogRead(Pin);
	for (byte i = 1; i < TAVG; i++)
		Kelvin += (float)analogRead(Pin);

	return Kelvin * (100.0 * AVREF) / (TAVG * 1024.0);
}

//------------------
// Set things up

void setup() {
  pinMode(PIN_HEARTBEAT,OUTPUT);
  digitalWrite(PIN_HEARTBEAT,LOW);	// show we arrived

//  TCCR1B = TCCRxB;					// set frequency for PWM 9 & 10
//  TCCR2B = TCCRxB;					// set frequency for PWM 3 & 11

  pinMode(PIN_MOSI,OUTPUT);
  digitalWrite(PIN_MOSI,LOW);

  pinMode(PIN_SCK,OUTPUT);
  digitalWrite(PIN_SCK,LOW);

  pinMode(PIN_RCKB,OUTPUT);
  digitalWrite(PIN_RCKB,LOW);

  pinMode(PIN_RCKC,OUTPUT);
  digitalWrite(PIN_RCKB,LOW);

  Serial.begin(9600);
  fdevopen(&s_putc,0);				// set up serial output for printf()

  printf("Temperature sensor - LM335\r\nEd Nisley - KE4ZNU - November 2012\r\n");

  BaseTemperature = KTOC + ReadLM335(PIN_TEMPERATURE);

  WriteLEDString(LEDCharBuffer);

  LEDBits = 0x5555;
  SetBarBits(LEDBits);

  printf("Base Temperature: %d C\n",(int)BaseTemperature);

  delay(1000);

  MillisThen = millis();

}

//------------------
// Run the test loop

void loop() {

	MillisNow = millis();

	if ((MillisNow - MillisThen) > UPDATEMS) {
		digitalWrite(PIN_HEARTBEAT,HIGH);

		Temperature = KTOC + ReadLM335(PIN_TEMPERATURE);

		printf("Temperature: %d C\n",(int)Temperature);

		LEDBits = 0x0200 >> (1 + (int)(Temperature/BARSCALE));	// move upward on display!
		SetBarBits(LEDBits);

		sprintf(LEDCharBuffer,"%-3dC",(int)Temperature);
		WriteLEDString(LEDCharBuffer);

		digitalWrite(PIN_HEARTBEAT,LOW);

		MillisThen = MillisNow;
	}

}
Advertisements

13 Comments