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;
}

Comments

Popular posts from this blog

ERROR_SUCCESS

ERROR_SUCCESS. This macro would be familiar to all those who have done some programming in WIN32. It is the output of the GetLastError() function to check the thread's last error state when no error has occurred. Weird, isn't it? I mean, if it is a success, then why is it marked as an error in the macro? This is one example of a badly made API. APIs are considered bad when programming in them becomes non-intuitive. Software is said to be bad (or said to suck) when it seems counter-intuitive to the user. There is one very simple example of this. Start notepad. Type in any text. Click on close. The message that you see is: This makes no sense to me as a user. Of course, the programmer follows the approach that he creates a temporary file called Untitled , and in that file he allows the user to make all his changes. But how am I, as a user to understand that? A similar disconnect occurs even between two different programmers. That is why it takes a whole lot of effort to make

On Harry Potter and why I dislike the series

There could not be a better time for this post. There could not have been a worse time for this post. Now that the penultimate movie of the series is out, and my facebook wall filled with people who loved the movie. But this is something I really wanted to say, and I shall say it anyway. Harry Potter is pathetic literature. Now, you must be wondering why I say that. There are many reasons. Firstly, the storyline itself is flawed. When a writer sits down to write anything, he/she must set up some essential rules about what is happening. These rules must remain constant irrespective of how many times he/she changes his/her mind. This is so that the readers are allowed to have some sensibility in what they are reading. In the fourth book, Rowling goes ahead and kills Cedric. Then, at the end of the book, the horseless carriages are there again. Nothing special. We all knew that they are horseless. But then comes the fifth book, and BAM, the horses are actually winged beasts that only thos

Elements of a Story: The Whispers

I'm compelled to begin each post with a meta. That way, my blog posts seem less like essays or dissertations, and more like diary entries, or web logs. So here goes... I started this blog a little over a year ago. The main purpose of this blog was to experiment with styles of writing, and find an effective outlet for all the subjects I wish to rant about; saving my classmates the agony of having to listen to them. As I wrote this blog, I've experimented with so many styles, and have received comments claiming that my work is a shameless copy greatly inspired by so-and-so author/work. Fact is that I simply chanced upon that style. I read, so obviously, my work shall reflect the styles of those I admire, but I've worked out so many styles without even knowing that they exist, only to be informed of them later. Recently, I've been struck with the seeming absence of whispers as an element of a story. The more I've thought of the subject, the more I've been convince