Thermostat Controller with LCD

Thermostat Controller with LCD


Thermostat Controller with LCD


I wanted to share my project of modifying the temperature sensor project and turning it into a thermostat with ATmega168. I added a digital output to drive an LED to “warm” the temperature sensor when the current (actual) temperature falls below the desired temperature.

Two push buttons come in as digital inputs one to ramp the desired temperature up and the other to ramp it down.

The logic is simple bang-bang control to turn the LED on and off based on the relationship of actual temperature to desired temperature. It simulates a thermostat in heater mode. The LED is off when the current temperature is above the desired temperature and turns on once the current temperature falls below.

Thermostat Controller with LCD

My coding skills are rusty at best, so there is lots of room for improvement for anyone who wants to make this thermostat controller better.

One note, I tried it with a flash light bulb and two MOSFets in parallel to supply the required current for the bulb to provide more heat to the temperature sensor, but the electrical noise was impacting the temperature sensor readings, so I had to go back to the LED. This is another opportunity for improvement by those more experienced than me.

Thermostat c code

// tempsensor.c
// ATmega168

// Modified to create thermostat for "bang-bang" heat control
// I freely admit my programming skills are not what they could be so please do improve!

#define F_CPU 14745600

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

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PC0 -- temperature sensor analog input
// NOTE: (switches connects to GND when closed, otherwise
// the pin is internally pulled up through a pull-up resistor to VCC)

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);
  return result;

double sampleToFahrenheit(uint16_t sample) {
  // conversion ratio in DEGREES/STEP:
  // (4930 mV / 1024 steps) * (1 degree / 10mV)
  //	^^^^^^^^^^^		 ^^^^^^^^^^
  //     from ADC		  from LM34
  return sample * (4930.0 / 1024.0 / 10.0);  

int main() {
  // start up the LCD
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  // Set the 2 pins to input mode - Two Pushbuttons
  DDRC &= ~(1<<PC3); // set PC3 as input
  DDRC &= ~(1<<PC4); // set PC4 as input

  // Set the 1 pin to output mode - Light On/Off
  DDRC |=  (1<<PC2); // set PC2 as output to drive lightbulb
  // turn on the internal resistors for the pins

  PORTC |= (1<<PC3); // turn on internal pull up resistor for PA3
  PORTC |= (1<<PC4); // turn on internal pull up resistor for PA4
  // declare the variables to represent each pushbutton input
  uint8_t temp_up;
  uint8_t temp_down;

  // start up the Analog to Digital Converter

  // start up the serial port
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // holder variables for temperature data
  uint16_t last_sample = 0;
  double this_temp;
  double temp_avg;
  double desired_temp;
  uint16_t i;
  uint16_t j;
  uint16_t k;
  uint16_t l;
  uint16_t m;
  uint16_t n;
  desired_temp = 78.0;
  // Initally turn off light on PC2
  PORTC &= ~(1<<PC2);// set PC2 low
  while(1) {
    // take 100 samples and average them!
    temp_avg = 0.0;
	// (PINC & (1<<PA0)) returns 8 bit number, 0's except position PA0 which is
    // the bit we want
    // shift it back by PA0 to put the bit we want in the 0th position.
	temp_up = (PINC & (1<<PC3)) >> PC3;
	if (temp_up == 0)
		for(j=0;j<1000;++j); //debounce temperature up pushbutton
		if (temp_up == 0)
			desired_temp = desired_temp + 0.2; // if temp up pressed, increase desired temp by one degree
			if (desired_temp >= 99)  //set 99 degF as lhighest allowed temperature
			desired_temp = 99; 
	temp_down = (PINC & (1<<PC4)) >> PC4;
	if (temp_down == 0)
		for(k=0;k<1000;++k); //debounce temperature down pushbutton
		if (temp_down == 0)
			desired_temp = desired_temp - 0.2; // if temp down pressed, lower desired temp by one degree
			if (desired_temp <= 50) //set 50 degF as lowest allowed temperature
			desired_temp = 50;

    for(l=0; l<2000; l++) {
      last_sample = adc_read();
      this_temp = sampleToFahrenheit(last_sample);

      // add this contribution to the average
      temp_avg = temp_avg + this_temp/2000.0;
	// Compare actual and desired temperature. Turn on light to increase temperature
	if (temp_avg <= desired_temp)   //increase temperature by turning on light 
		for(m=0;m<2000;++m); //debounce so light doesn't switch on & off to frequently
		PORTC |= (1<<PC2); // Turn on LED light

	if (temp_avg > desired_temp + 1.0)   //decrease temperature by turning off light when actual one degree above desired
		for(n=0;n<2000;++n); //debounce so light doesn't switch on & off to frequently
		PORTC &= ~(1<<PC2);// Turn off LED light

    // write message to LCD
	lcd_write_string(PSTR("    Central Heat    "));
	lcd_write_string(PSTR("     Thermostat     "));
    fprintf_P(&lcd_stream, PSTR("Current Temp: %.1f"), temp_avg);
    lcd_write_data(0xdf); //degree symbol
    fprintf_P(&lcd_stream, PSTR("Desired Temp: %.1f"), desired_temp);
    lcd_write_data(0xdf);  //degree symbol

    // write message to serial port
    // printf_P(PSTR("%.2f degrees F\r\n"), temp_avg);

  return 0;

Comments are closed.