back to
www.audiodesignguide.com

To get more information contact me at: webmaster@audiodesignguide.com


Programmable Thermostat
December 27 st , 2009 


This project is not complete because I have found some limit in the Microcontroller used because the flash memory is not enough to contain all my firmware.
This Programmable Thermostat include an automatic charge for Ni-Mh battery and a very good clock using 2 quartz.
I would like that someone continue to develop the firmware, I can give you one pcb for free.

SCHEMATIC

PCB

PHOTO


Follows the rear side photo with the 2 correction to the pcb, RA4 pin need pull-up and you must cut the wrong line as show.

FIRMWARE

To program the 16F876A Microchip microcontroller I have used the PICKIT 2 USB Development Programmer/Debugger (cod. PG164120 or DV164121) with a cost about 40-50$

I love the C language because it is very simple if compared to assembler.

The my C source has been compiled with HI-TECH PICC-Lite Compiler.

HI-TECH Software has provided a  freeware HI-TECH PICC-Lite compiler as a tool for hobbyists and students, but the licence allows its use for commercial purposes as well. It is ideal as a teaching tool for an introduction into the 'C' language and embedded programing on a Microchip device.

Follows the source file to compile with this command:

picl -16F877A ProgrammableThermostat.c

#include  <pic.h>
#include  <string.h>
#include  <stdio.h>
#include  <stdlib.h>


#define PIC_CLK 20000000
#include "delay_alternative_enhanced_precision.h"


//__CONFIG(WDTDIS & XT & UNPROTECT & LVPDIS);	// freq. clock 4MHz
//__CONFIG(WDTDIS & HS & UNPROTECT & LVPDIS);	// freq. clock 20MHz
__CONFIG(WDTDIS & PWRTEN & HS & UNPROTECT & DEBUGDIS & BORDIS & LVPDIS); 
//__CONFIG(WDTDIS & XT & UNPROTECT & LVPDIS);

#define PORTBIT(adr, bit) ((unsigned)(&adr)*8+(bit))

static bit BAT_CH  @ PORTBIT(PORTC, 2);
static bit BAT_DI  @ PORTBIT(PORTC, 3);

static bit BUT_UP  @ PORTBIT(PORTB, 0);
static bit BUT_LF  @ PORTBIT(PORTB, 1);
static bit BUT_EN  @ PORTBIT(PORTB, 2);
static bit BUT_RG  @ PORTBIT(PORTB, 4);
static bit BUT_DW  @ PORTBIT(PORTB, 5);

static bit OUT_ON  @ PORTBIT(PORTA, 5);
static bit BUT_ON  @ PORTBIT(PORTA, 4);

static bit LCD_RS  @ PORTBIT(PORTC, 7);
static bit LCD_RW  @ PORTBIT(PORTC, 6);
static bit LCD_EN  @ PORTBIT(PORTC, 5);
static bit LCD_BK  @ PORTBIT(PORTC, 4);
static bit LCD_O1  @ PORTBIT(TRISC, 6);
static bit LCD_O2  @ PORTBIT(TRISC, 5);
static bit LCD_O3  @ PORTBIT(TRISC, 4);
#define LCD_DATA   PORTB
#define LCD_TRIS   TRISB

#define ACT_UP  (1)
#define ACT_DW  (2)
#define ACT_EN  (3)
#define ACT_LF  (4)
#define ACT_RG  (5)

//******************************************************************************
//* function declarationis & global variables
//******************************************************************************
void          DelayMs(int);
void          write_SPI(unsigned int, unsigned int);
void          LCD_init(void);
void          LCD_write_str(int, int, char *);
void          LCD_write_data(unsigned char);
void          LCD_write_cmd(unsigned char);
void          LCD_wait_busy(void);
void          LCD_clear_screen();
void          LCD_cursor_on();
//int           byteToInt(unsigned char, unsigned char) ;
//int           intToByte(int, unsigned char *, unsigned char *);
void          uitoa(unsigned int, char*, unsigned char);
void          DelayBigUs(unsigned int cnt);
void          DelayBigMs(unsigned int cnt) ;
unsigned long GetTemp();
unsigned long GetBattery();
unsigned long GetPowerSupply();
unsigned char GetButton();
void          ManageBacklight(unsigned char event);
void          DebugEvent(unsigned char event);
void          DisplayTime();
void          DisplayPower();
void menu2();
void menu3();
void menu4();
void menu5();
void menu6();

unsigned long valoreBat, valorePS, valoreTemp;
unsigned char byte1, byte2, byte3, byte4;
unsigned char tmp[17];

#define DOTS      ":"

#ifdef OLD
#define HELLO     "HELLO"
#define CHOICE	  "Scegli > up/down"
#define ROW       "1234567890123456"
#define GOON      "Accendi subito  "
#define GOOFF     "Spegni subito"
#define PROGC     "Cambia programma"
#define PROGV     "Visual programma"
#define PROGH     "Cambia orario"
#define PROGSV    "Visualizza stat"
#define PROGSR    "Resetta stat"
#define PROG1I    "Prog1 Ora inizio"
#define PROG1F    "Prog1 Ora fine"
#define PROG1S    "Prog1 giorno set"
#endif

int          ore = 0;
int          min = 0;
int          sec = 0;

unsigned char state = 0;

#define XTAL    20000000         // crystal frequency - 4MHz

//########################1234567890123456#########
char hello[]           = "Ciao ";
char arrow[]           = ">";
char space[]           = " ";
bank1 char choice[]    = "Scegli su o giu";
bank1 char switchon[]  = "Accendi";
bank1 char switchoff[] = "Spegni";
bank1 char now[]       = "subito";
bank1 char change[]    = "Cambio";
bank1 char prog[]      = "progr.";
bank1 char time[]      = "orario";
bank1 char enter[]     = "Invio conferma ";
//########################1234567890123456#########

void interrupt prv_int(void)
{
    if (TMR1IF)
    {
        TMR1H = 0x80;
      
        sec++;

	if (sec == 60)
	{
	    sec = 0;

	    min++;

	    if (min == 60)
	    {
                min = 0;
		sec = 0;
                ore++;

		if (ore == 24)
		{
	            min = 0;
		    sec = 0;
		    ore = 0;
		}
   	    }

	 }
         TMR1IF = 0;    
    }
}

main()
{
    unsigned char event;
	

    TRISA   = 0b00001111;  // All RA0-RA4 port A are input
    TRISC   = 0b00000011;  // only RC0 e RC1 are input for oscill

CMCON  = 0b00000111; // All comparator disabled

    //*****************************************************
    //* start tmr1 and secondary oscillator
    //*****************************************************
    T1OSCEN = 1;
    TMR1CS  = 1;

    T1CKPS0 = 0;
    T1CKPS1 = 0;

    TMR1IF  = 0; 
    TMR1L   = 0x80;
    TMR1H   = 0x80;
    TMR1IE  = 1;
    
    PEIE = 1;
    GIE  = 1;

    TMR1ON = 1;

    //*****************************************************
    //* start adc
    //*****************************************************
    ADCON1 = 0b10000100;  

    DelayMs(50);

    //##################################################
    //# display startup
    //##################################################
    LCD_init();

    LCD_write_str( 1, 1, hello);

    LCD_BK = 1;
    DelayMs(2000);
    LCD_BK = 2;

    //##################################################
    //# load menu
    //################################################## 	
    
    //##################################################
    //# main loop
    //################################################## 	
    while(1)
    {
	if (state == 0 || state == 1)
	{
	    DisplayTime();

	    valoreTemp = GetTemp();
	}

	//valoreBat = GetBattery();

	//valorePS = GetPowerSupply();

	//DisplayPower(valoreBat, valorePS);

	event = GetButton();

	ManageBacklight(event);

	if (event > 0)
	{
	    switch (state)
	    {
		case 0:
		    LCD_write_str( 2, 1, choice);
		    state = 1;
		    break;
		case 1:
		    menu2();
		    state = 2;
		    break;
		case 2:
		    if (event == ACT_EN)
		    {
			OUT_ON=1;
		    }
		    else if (event == ACT_DW)
		    {
			menu3();
			state=3;
		    }
		    break;
		case 3:
		    if (event == ACT_EN)
		    {
			OUT_ON=0;
		    }
		    else if (event == ACT_UP)
		    {
			menu2();
			state=2;
		    }
		    else if (event == ACT_DW)
		    {
			menu4();
			state=4;
		    }
		    break;
		case 4:
		    if (event == ACT_UP)
		    {
			menu3();
			state=3;
		    }
		    else if (event == ACT_DW)
		    {
			menu5();
			state=5;
		    }
		    break;
		case 5:
		    if (event == ACT_EN)
		    {
			menu6();
			state=6;
		    }
		    else if (event == ACT_UP)
		    {
			menu4();
			state=4;
		    }
	    }
	}
	if (state == 6)
	{
	    menu6();
	}
	//DebugEvent(event);

	DelayMs(500);
    }
}

void switchon_now()
{
    // >Accendi subito
    // 1234567890123456
    LCD_write_str( 1,  2, switchon);
    LCD_write_str( 1,  9, space);
    LCD_write_str( 1, 10, now);
    LCD_write_str( 1, 16, space);
}
void switchoff_now()
{
    //  Spegni subito
    // 1234567890123456
    LCD_write_str( 2,  2, switchoff);
    LCD_write_str( 2,  8, space);
    LCD_write_str( 2,  9, now);
    LCD_write_str( 2, 15, space);
    LCD_write_str( 2, 16, space);
}
void change_prog()
{
    // >Cambio progr.
    // 1234567890123456
    LCD_write_str( 1,  2, change);
    LCD_write_str( 1,  8, space);
    LCD_write_str( 1,  9, prog);
    LCD_write_str( 1, 15, space);
    LCD_write_str( 1, 16, space);
}
void change_time()
{
    //  Cambio orario
    // 1234567890123456
    LCD_write_str( 2,  2, change);
    LCD_write_str( 2,  8, space);
    LCD_write_str( 2,  9, time);
    LCD_write_str( 2, 15, space);
    LCD_write_str( 2, 16, space);
}
void menu2()
{
    LCD_write_str( 1,  1, arrow);
    switchon_now();
    LCD_write_str( 2,  1, space);
    switchoff_now();
}
void menu3()
{
    LCD_write_str( 1,  1, space);
    switchon_now();
    LCD_write_str( 2,  1, arrow);
    switchoff_now();
}
void menu4()
{
    LCD_write_str( 1,  1, arrow);
    change_prog();
    LCD_write_str( 2,  1, space);
    change_time();
}
void menu5()
{
    LCD_write_str( 1,  1, space);
    change_prog();
    LCD_write_str( 2,  1, arrow);
    change_time();
}
void menu6()
{
    LCD_write_cmd(0x01); // clear screen
    LCD_write_cmd(0x02); // cursor home position
    LCD_write_cmd(0xE);  // cursor on
    DisplayTime();
    //LCD_write_str( 2,  1, enter);
}

void DisplayTime()
{
    uitoa(ore, tmp, 2);
    LCD_write_str( 1, 1, tmp);

    strcpy(tmp, DOTS);
    LCD_write_str( 1, 3, tmp);

    uitoa(min, tmp, 2);
    LCD_write_str( 1, 4, tmp);

    strcpy(tmp, DOTS);
    LCD_write_str( 1, 6, tmp);

    uitoa(sec, tmp, 2);
    LCD_write_str( 1, 7, tmp);

    strcpy(tmp, "   ");
    LCD_write_str( 1, 9, tmp);

    return;
}

void ManageBacklight(unsigned char event)
{
    static unsigned long backTime = 0;

    if (event > 0)
    {
	LCD_BK = 1;
	backTime = 0;
    }
    else if (backTime > 20)
    {
	LCD_BK = 0;
	state = 0;
    }
    else
    {
	backTime++;
    }
}

unsigned char  GetButton()
{
    TRISB   = 0b11111111;  // All port A are input

    DelayMs(10);

    BUT_ON = 1;

    DelayMs(10);

    if (BUT_UP == 0)
    {
	BUT_ON = 0;
	return ACT_UP;
    }
    else if (BUT_DW == 0)
    {
	BUT_ON = 0;
	return ACT_DW;
    }
    else if (BUT_LF == 0)
    {
	BUT_ON = 0;
	return ACT_LF;
    }
    else if (BUT_RG == 0)
    {
	BUT_ON = 0;
	return ACT_RG;
    }
    else if (BUT_EN == 0)
    {
	BUT_ON = 0;
	return ACT_EN;
    }
    BUT_ON = 0;
    return 0;
}

void adc_enable(unsigned char channel) {
  DelayMs(5);

  ADCON0 = (channel << 3) + 0x81;  // ADC on, Fosc/32

  DelayMs(5);
}

void adc_read() {
  ADGO = 1;                        // start A/D-conversion 
  while(ADGO)                      // wait
    continue;
}

void DisplayPower()
{
    strcpy(tmp, "PS ");
    LCD_write_str( 2, 1, tmp);

    uitoa(valorePS/10, tmp, 2);
    LCD_write_str( 2, 4, tmp);

    strcpy(tmp, ",");
    LCD_write_str( 2, 6, tmp);

    uitoa(valorePS%10, tmp, 1);
    LCD_write_str( 2, 7, tmp);


    strcpy(tmp, "BAT ");
    LCD_write_str( 2, 9, tmp);

    uitoa(valoreBat/10, tmp, 2);
    LCD_write_str( 2, 13, tmp);

    strcpy(tmp, ",");
    LCD_write_str( 2, 15, tmp);

    uitoa(valoreBat%10, tmp, 1);
    LCD_write_str( 2, 16, tmp);

    return;
}


unsigned long GetPowerSupply()
{
    unsigned long valore;

    adc_enable(3);
    adc_read();
    
    valore = ADRESL+(ADRESH<<8); 

    //valore = (valore * (5000 / 1023)) / 7 ;
    valore = (valore * (5000 / 1023)) / 9 ;

    return valore;
}

unsigned long GetBattery()
{
    unsigned long valore;

    adc_enable(1);
    adc_read();
    
    valore = ADRESL+(ADRESH<<8); 

    //valore = valore * (5000 / 1023) / 7 ;
    valore = (valore * (5000 / 1023)) / 9 ;

    return valore;
}

unsigned long GetTemp()
{
    unsigned long valore;


    adc_enable(0);
    adc_read();
    
    valore = ADRESL+(ADRESH<<8); 

    valore = valore * (5000 / 1023) / 6;
    
    uitoa(valore/10, tmp, 2);
    LCD_write_str( 1, 12, tmp);

    strcpy(tmp, ",");
    LCD_write_str( 1, 14, tmp);

    uitoa(valore%10, tmp, 1);
    LCD_write_str( 1, 15, tmp);

    tmp[0] = 0b11011111;
    LCD_write_str( 1, 16, tmp);

    return valore;
}

//*********************************************************************
//* LCD read and write
//*********************************************************************
void LCD_init()
{
    char * ptr;
    char   LCDstr[] = { 0x3c, // 111100: 8bits, 2lines, 5x10dots
			0x01, // clear display
			0x0c, // 1100: display on, cursor off
			0x06, // 110: auto-increment
			0x00 };
    LCD_O1 = 0;
    LCD_O2 = 0;
    LCD_O3 = 0;

    LCD_TRIS = 0;
    LCD_DATA = 0;

    ptr = LCDstr;

    while(*ptr)
    {
        LCD_write_cmd(*ptr);

	ptr++;
    }
}


void LCD_wait_busy()
{
    unsigned char busy;
    int           retry = 1000;

    LCD_TRIS = 0xff;
    
    LCD_RS = 0;		
    LCD_RW = 1;		// read
    asm("nop");

    do {
	    LCD_EN = 1;
	    asm("nop");

	    busy = LCD_DATA & 0x80;
	    LCD_EN = 0;
    } while (busy && retry-- > 0);

    LCD_TRIS = 0x00;
}

void LCD_write_cmd(unsigned char c)
{
    LCD_wait_busy();

    LCD_RW = 0;
    LCD_RS = 0;		
    LCD_DATA = c;
    LCD_EN = 1;
    LCD_EN = 0;
}

void LCD_write_data(unsigned char c)
{
    LCD_wait_busy();

    LCD_RW = 0;
    LCD_RS = 1;		
    LCD_DATA = c;
    LCD_EN = 1;
    LCD_EN = 0;
}

void LCD_write_str(int line, int pos, unsigned char * str)
{
    unsigned char    curr;
    unsigned char    i;


    //*************************************************
    // set the posistion and line
    //*************************************************
    if (line == 1)
	curr = 0b10000000 + (pos - 1);
    else
	curr = 0b11000000 + (pos - 1);

    LCD_write_cmd(curr);

    //*************************************************
    // send the string
    //*************************************************
    for(i = 0; str[i] != '\0'; i++)
    {
        LCD_write_data(str[i]);
    }
}

void DelayMs(int cnt)
{
    unsigned char	i;

    do {
	i = 4;

	do {
		DelayUs(250);
	    } while(--i);
    } while(--cnt);
}

#ifdef OLD
int intToByte(int integer, unsigned char * byte1, unsigned char * byte2)
{
    *byte1 = (char)(integer >> 8);
    *byte2 = (char)(integer);

    return 0;
}

int byteToInt(unsigned char byte1, unsigned char byte2) 
{
    int integer;

    integer = byte1;
    integer <<= 8;
    integer |= byte2;

    return integer;
}

#endif
void uitoa(unsigned int value, char* string, unsigned char len)
{
    unsigned char index = len;

    string[0] = '0';

    do {
        string[--index] = '0' + (value % 10);

        value /= 10;
    } while (value != 0);

    string[len] = 0;
}