Skip to main content

Making a simple temperature sensor

After a whole lot of posts written out of sheer frustration, here is something you can really do. In this post, I explain how to build a simple temperature sensor.

The temperature sensor is a LM35 chip. This is a easy to use 3 pin package. The pins are Vcc, Gnd and Vout. The output voltage (in millivolts) is the temperature in degrees centigrade * 10.

I am using an Atmel ATmega8 micro-controller. There really is no specific reason for choosing this micro-controller, except for easy availability (I had one) and previous experience.

The third and last major component is the LCD display for displaying the temperature.

Now for some quick calculations. My temperature shall not in general exceed 100C. A diode drops 0.6V when it is on. Putting two diodes in series, I get an almost constant voltage of 1.200V. I have a 10 bit ADC. Hence, the temperature in Celsius*10 is ADC*1200/1024, or (ADC*1200)>>10.

The final touch is a simple push-button for cycling through day average, day maximum and day minimum. However, a simple tact switch does not debounce the output, so it was my job to debounce the switch. The ATmega8, in the input mode with the pull up resistors gives around 10k pullup, so I simply added a 10uF capacitor at the INT0 pin. This gives a debouncing time of around 0.1s, good enough for our purpose.

Once these specs were complete, I sat to make the schematic. The completed schematic is shown below:

A PCB was made based on this schematic.

While programming the micro-controller, an important question was how to define a day. In the end, I decided to define a day as the previous 24 hours. Due to the limited number of pins on the ATmega8, I decided to ditch the crystal oscillator, after all, accuracy in timekeeping is hardly what the user wants. Instead, I used the internal 8MHz oscillator.

For the LCD interface, I used the libraries provided by Peter Fleury. To display the degree symbol correctly, I simply output the character 223. I shall not give any more information than this, because I don't really want to create a manual on a hardware kit that you can assemble. The fun really is in designing the thing yourself.

PCB made, micro-controller flashed, I set up this circuit for the first test run!

Here are some images of the temperature sensor in action:






The source code for the ATmega8 has been listed below. The LCD libraries are by Peter Fleury and can be found here.


#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include "lcd.h"

#define SetBit(x,b) (x)|=(b)
#define GetBit(x,b) ((x)&(b))
#define ToggleBit(x,b) (x)^=(b)
#define ResetBit(x,b) (x)&=~(b)
#define isLow(x,b) (!GetBit(x,b))
#define isHigh(x,b) GetBit(x,b)

#define DISPLAY_DAY_AVG 1
#define DISPLAY_DAY_MAX 2
#define DISPLAY_DAY_MIN 3

typedef struct
{
	uint16_t avg;
	uint16_t min;
	uint16_t max;
	uint8_t valid;
} hour_struct;

char buffer[10];

uint8_t display_wait = 0;
uint8_t current_display = 0;

uint16_t min_avg = 0;
uint8_t second_count = 0;
uint8_t min_count = 0;
uint8_t current_hour = 0;
uint16_t current_temp = 0;

uint16_t hour_avg;
uint16_t hour_max;
uint16_t hour_min;

hour_struct hours[23];

uint8_t is_first = 1;

uint16_t day_avg()
{
	uint8_t valid_hours = 1;
	uint32_t sum = hour_avg;
	for(uint8_t i=0; i<23; i++)
		if(hours[i].valid)
		{
			sum += hours[i].avg;
			valid_hours++;
		}
	return sum/valid_hours;
}

uint16_t day_min()
{
	uint16_t min = hour_min;
	for(uint8_t i=0; i<23; i++)
		if(hours[i].valid)
			if(hours[i].min < min)
				min = hours[i].min;
	return min;
}

uint16_t day_max()
{
	uint16_t max = hour_max;
	for(uint8_t i=0; i<23; i++)
		if(hours[i].valid)
			if(hours[i].max > max)
				max = hours[i].max;
	return max;
}

void temp_to_string(uint16_t temp, char* str_buffer)
{
	uint8_t strlen = 4;
	str_buffer[0] = 'C';
	str_buffer[1] = (char)(223);
	str_buffer[2] = (char)(temp%10)+'0';
	temp /= 10;
	str_buffer[3] = '.';
	while(temp)
	{
		str_buffer[strlen++] = (char)(temp%10)+'0';
		temp /= 10;
	}
	str_buffer[strlen] = '\0';
	uint8_t l = (strlen)/2-1;
	
	for(int i=0; i<=l; i++)
	{
		str_buffer[strlen-i-1] ^= str_buffer[i];
		str_buffer[i] ^= str_buffer[strlen-i-1];
		str_buffer[strlen-i-1] ^= str_buffer[i];
	}
}

ISR(TIMER1_COMPA_vect)
{
	current_temp = (uint32_t)(ADC)*1200/1024;
	uint16_t temp = min_avg*second_count;
	temp += current_temp;
	second_count++;
	min_avg = temp/second_count;
	SetBit(ADCSRA, _BV(ADSC));
	
	if(is_first)
	{
		is_first = 0;
		hour_min = hour_max = hour_avg = current_temp;
		SetBit(MCUCR, _BV(ISC01));
		SetBit(GICR, _BV(INT0));
	}
	
	if(second_count == 60)
	{
		second_count = 0;
		temp = hour_avg*min_count;
		temp += min_avg;
		min_count++;
		hour_avg = temp/min_count;	
		if(min_avg > hour_max)
			hour_max = min_avg;
		else if(min_avg < hour_min)
			hour_min = min_avg;
	
		if(min_count == 60)
		{
			min_count = 0;
			hours[current_hour].avg = hour_avg;
			hours[current_hour].min = hour_min;
			hours[current_hour].max = hour_max;
			hours[current_hour].valid = 1;
			current_hour++;
			if(current_hour == 23)
			{
				current_hour = 0;
			}
			hour_min = min_avg;
			hour_max = min_avg;
		}
	}
	if(display_wait)
		display_wait--;
	else
	{
		current_display = 0;
		lcd_clrscr();
		lcd_puts("Temperature\n");
		//itoa(current_temp, buffer, 10);
		temp_to_string(current_temp, buffer);
		lcd_puts(buffer);
	}
}

ISR(INT0_vect)
{
	display_wait = 5;
	current_display++;
	current_display &= 0x03;
	lcd_clrscr();
	if(current_display == DISPLAY_DAY_AVG)
	{
		lcd_puts("Day Average\n");
		temp_to_string(day_avg(), buffer);
		lcd_puts(buffer);
	}
	else if(current_display == DISPLAY_DAY_MAX)
	{
		lcd_puts("Day Maximum\n");
		temp_to_string(day_max(), buffer);
		lcd_puts(buffer);
	}
	else if(current_display == DISPLAY_DAY_MIN)
	{
		lcd_puts("Day Minimum\n");
		temp_to_string(day_min(), buffer);
		lcd_puts(buffer);
	}
	else
	{
		display_wait = 0;
		lcd_puts("Temperature\n");
		temp_to_string(current_temp, buffer);
		lcd_puts(buffer);
	}
}

int main(void)
{
	for(int i=0; i<23; i++)
		hours[i].valid = 0;
	
	DDRC = 0x00;                  //Set Port A as input(for ADC)
	PORTC = 0x00;                 //Disable internal pullups for Port A
	DDRD = 0x00;
	PORTD = 0xFF;
	ADCSRA = _BV(ADEN)|6;
	SetBit(ADCSRA, _BV(ADSC));
	while(isHigh(ADCSRA, _BV(ADSC)));
	SetBit(ADCSRA, _BV(ADSC));
	while(isHigh(ADCSRA, _BV(ADSC)));
	TCCR1B = _BV(WGM12)|_BV(CS12);
	OCR1A = 31250;
	TIMSK = _BV(OCIE1A);
	sei();
	lcd_init(LCD_DISP_ON);
	lcd_clrscr();
	lcd_puts("Temperature\nSensor");
	while(1);
	return 0;
}

Popular posts from this blog

Progressive Snapshot: Is it worth it?

I turned 25 last year, which in the highly mathematical and calculating eyes of the US insurance industry meant that I had suddenly matured into a much more responsible driver than I was at 24 years and 364 days of age. As a result, I expected my insurance rates to go down. Imagine my surprise when my insurance renewal notice from GEICO actually quoted a $50 increase in my insurance rates. To me, this was a clear signal that it was time to switch companies.Typically, I score really high on brand loyalty. I tend to stick with a brand for as long as possible, unless they really mess up. This qualified as a major mess up. As a result, I started shopping for insurance quotes.Two companies that quoted me significantly lower rates (30%–40% lower) were Progressive and Allstate. Both had an optional programme that could give me further discounts based on my consenting to the companies tracking my driving habits. Now, I am a careful driver – I hardly ever accelerate hard. I hate using the brak…

Cornell Graduate Students United: At What Cost?

On Monday and Tuesday, we graduate students at Cornell will be voting on whether or not we want to unionise. Actually, scratch that, only graduate students who hold a TA, RA, or GRA appointment can unionise.This is a shitty arrangement, and I will be voting against it.For those of you who are not aware of how graduate school works at Cornell, you could be on one of many appointments.FellowshipA graduate student on a fellowship gets a stipend and tuition paid without associated teaching or research opportunities. Graduate students on a fellowship typically work towards their own theses, but will be excluded from the unionGraduate research assistantshipsA GRA gives a graduate student stipend and tuition without teaching responsibilities. However, this money comes out of a specific project grant, and the students typically work on their own theses. Students on GRAs magically qualify to join the union, whereas there is virtually no difference between a GRA and a fellowship for the most pa…

De-addicting

I’m on a process to break away from the distractions of social media, primarily Facebook, and spend the time to pursue other interests. This is an interesting experiment, and it has required significant dedication and effort on my part. At the end, though, I’m not certain if I’ll be successful.I realised that I had a problem when Facebook became the most visited sites in my browser. I should have been working, not scrolling through Facebook like a, well, you know what Jerry Seinfeld has to say. I further noticed that I would tend to log on to Facebook every time I was stuck at work, so that my capacity to tackle problems was reduced to only those that involved cracking a walnut shell. Anything more complex, and I would procrastinate and end up on Facebook.What I needed was a swift kick in the arse to get off Facebook and into the real world. It came through the means of my latest project which required all my attention. At this time, I logged out of Facebook from my work computer and …