/* Main code for LIGHTING UNIT for PIC16f628 chip written by Matěj Hybler.
 * BACHELOR'S THESIS VUT Brno. 
 * Using built-in LCD library from microC for PIC IDE. No need to include lcd.h here. 
 */

// #include "lcd.h"
#define REPEAT_DEL  200

#define BEEP_LEN    10
#define AL_LEN      500

//Pin connetions
#define RELAY       PORTA.B2
#define LIGHT       PORTA.B3
#define BUZZER      PORTA.B4
#define ENTER_KEY   PORTB.B7
#define MODE_KEY    PORTB.B6
#define DOWN_KEY    PORTB.B5
#define UP_KEY      PORTB.B4

// LCD module connections
sbit LCD_RS at RA0_bit;
sbit LCD_EN at RA1_bit;
sbit LCD_D4 at RB0_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D7 at RB3_bit;

sbit LCD_RS_Direction at TRISA0_bit;
sbit LCD_EN_Direction at TRISA1_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;

//boolean variables
bit actmin;
bit inc;
bit end;
bit mode;

int count = 0;
int x;
char sec = 0;
char minu = 0;
char del;
char time[] = "00:00";


//Function prototypes
void manual();
void autom();
void light_end();
void beep();


//Function for port, timer, LCD and interrupt initializatio
void hw_init()
{
  CM0_bit = 1;      //turn off internal comparators
  CM1_bit = 1;
  CM2_bit = 1;
  TRISB = 240;      //set pin <4:7> as input for buttons
  TRISA = 0;        //set whole PORTA as output
  PORTA = 0;
  BUZZER = 1;
  LIGHT = 1;

  RBIF_bit = 0;     //clear int on PORTB change flag
  RBIE_bit = 1;     //turn on interrupt on change on PORTB
  TMR1IE_bit = 1;   //turn on interrupt on TMR1 overflow
  TMR1CS_bit = 0;   //select clk source for TMR1
  T1CKPS0_bit = 1;  //set TMR1 prescaler to 1:8
  T1CKPS1_bit = 1;
  T1CON.B2 = 1;     //do not synch clk
  PEIE_bit = 1;

  TMR1H = 11;
  TMR1L = 220;

  OPTION_REG.B7 = 0;    //enable PORTB pull-ups

  Lcd_Init();                 // Initialize LCD
  Lcd_Cmd(_LCD_CLEAR);        // Clear display
  Lcd_Cmd(_LCD_CURSOR_OFF);   // Cursor off

  GIE_bit = 1;    //enable all interrupts
  actmin = 1;     //set active minutes
  mode = 1;       //set mode to auto
  beep();         //do startup beep
}

//makes beep on key pressed
void beep()
{
    BUZZER = 0;
    delay_ms(BEEP_LEN);
    BUZZER = 1;
}

//stores last time to EEPROM
void store_time()
{
  EEPROM_Write(0,minu);
  EEPROM_Write(1,sec);
}

//loads previously entered time from EEPROM
void load_time()
{
  minu = EEPROM_Read(0);
  sec = EEPROM_Read(1);
}

//print remaining time
void show_time()
{
    time[4] = sec%10 + 48;          //convert seconds to char
    time[3] = (sec/10)%10 + 48;     //convert tens of seconds to char
    time[1] = minu%10 + 48;         //convert minutes to char
    time[0] = (minu/10)%10 + 48;    //convert tens of minutes to char

    Lcd_Out(2,6,time);              //print time to LCD
}

//mode selection menu
void set_mode()
{
  Lcd_Cmd(_LCD_CLEAR);                // Clear display
  Lcd_Out(1,1,"Mod osvitu:");

  while(ENTER_KEY)                  //loop until enter key pressed
  {
    if(!MODE_KEY)                   //toggle between modes
    {
      mode = !mode;
      delay_ms(REPEAT_DEL);         //key repeat delay
    }

    if(mode)
    {
      Lcd_Out(2,6," AUTO ");
    }
    else
    {
      Lcd_Out(2,6,"MANUAL");
    }
  }
  delay_ms(REPEAT_DEL);
}

//time increase decrease function
void ch_time()
{
  if(actmin)            //check weather minutes or seconds selected
  {
    if(inc)
    {
      if(minu==99)      //make sure minutes never reach 100 when increment
        minu = 0;
      else
        minu++;
    }
   else
   {
     if(minu)           //make sure minutes never reach 100 when decrement
       minu--;
     else
       minu = 99;
   }
  }
  else
  {
    if(inc)
    {
      if(sec>=59)       //make sure ceconds never reach 60 when increment
        sec = 0;
      else
        sec++;
    }
    else
    {
      if(sec)           //make sure minutes never reach 60 when decrement
        sec--;
      else
        sec = 59;
    }
  }
}

//time set menu
void set_time()
{
  Lcd_Cmd(_LCD_CLEAR);                // Clear display
  Lcd_Out(1,1,"Doba osvitu:");
  load_time();

  while(ENTER_KEY)                  //loop until enter key pressed
  {
    if(!UP_KEY)                     //increment time
    {
      inc = 1;
      ch_time();
      delay_ms(REPEAT_DEL);
    }
    else if(!DOWN_KEY)              //decrement time
    {
      inc = 0;
      ch_time();
      delay_ms(REPEAT_DEL);
    }
    else if(!MODE_KEY)
    {
      actmin = !actmin;             //minutes or seconds entry
      delay_ms(REPEAT_DEL);
    }
    show_time();
  }
  store_time();                 //store last time value to EEPROM
}

//decrement time value by 1sec
void tmr_decrement()
{
  if(sec)
    sec--;
  else if(minu)
  {
    minu--;             //decrement by 1 sec
    sec = 59;
  }
  else
  {
    TMR1IE_bit = 0;       //time reached zero, disable decrementation
    TMR1ON_bit = 0;
    end = 1;
  }
}

//automatic mode
void autom()
{
  end = 0;
  set_time();

  Lcd_Cmd(_LCD_CLEAR);                // Clear display
  Lcd_Out(1,1,"Konec osvitu za:");
  TMR1IE_bit = 1;
  TMR1ON_bit = 1;
  delay_ms(200);
  RELAY = 1;
  delay_ms(200);

  while(!end)
  {
     show_time();
  }
  light_end();
}

//manual mode
void manual()
{
  Lcd_Cmd(_LCD_CLEAR);                // Clear display
  while(MODE_KEY)
  {
    if(!ENTER_KEY)
    {
      RELAY = !RELAY;
      delay_ms(REPEAT_DEL);
    }

    if(RELAY)
    {
      Lcd_Out(1,1,"OSVIT ZAPNUT");
    }
    else
    {
      Lcd_Out(1,1,"OSVIT VYPNUT");
    }
  }
}

//end lighting and start alarm
void light_end()
{
   char al;
   
   TMR1IE_bit = 0;
   TMR1ON_bit = 0;
   RELAY = 0;
   Lcd_Cmd(_LCD_CLEAR);                // Clear display
   Lcd_Out(1,2,"OSVIT UKONCEN!");      //ALARM!!!                            //disable relay
   for(al=0;al<3;al++)
   {
     delay_ms(AL_LEN);
     LIGHT = 1;
     BUZZER = 0;
     delay_ms(AL_LEN);
     LIGHT = 0;
     BUZZER = 1;
   }
   LIGHT = 1;
   BUZZER = 1;
   while(PORTB<240);
}

//main program
void main()
{

  hw_init();                        //intialize hardware


  Lcd_Out(1,1,"Osvit. jednotka");  //display splash screen
  Lcd_Out(2,1,"pro vyrobu DPS");

  delay_ms(2000);

START:
  RELAY = 0;
  set_mode();           //select mode
  if(mode)
  {
    autom();            //start automatical mode
  }
  else
  {
    manual();           //start manual mode
  }
  BUZZER = 1;
  goto START;
}

//timer overflow and key press ISR
void interrupt(void)
{
   if(TMR1IF_bit)             //timer overflow
   {
     TMR1IF_bit = 0;          //clear TMR1 ovf interrupt flag

     TMR1H = 11;
     TMR1L = 220;

     del = !del;
     if(del)                //additional prescaler 1:2
       tmr_decrement();     //decrement time
   }

   else if(RBIF_bit)        //key pressed ISR
   {
     delay_ms(20);
     if(PORTB<240)          //beep if key pressed
     {
       beep();
     }
     PORTB = PORTB;         //read port to update missmatch on PORTB
     RBIF_bit = 0;          //clear PORTB change interrupt flag
   }
}
