/*
 * timer_light_v2.c
 */


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

#define PB1    PORTAbits.RA3
#define PB2    PORTAbits.RA2
#define LIGHT  LATBbits.LATB0
#define SIGNAL LATBbits.LATB5

#define TIMER_VAL   -31250

void timer_setup(void)
{
  T0CONbits.TMR0ON = 0; // stop the timer
  T0CONbits.T08BIT = 0; // timer configured as 16-bit
  T0CONbits.T0CS = 0; // use FOSC
  T0CONbits.PSA = 0; // use prescaler
  T0CONbits.T0PS = 0b111; // prescaler 1:256
  TMR0 = TIMER_VAL;
  T0CONbits.TMR0ON = 1; // start the timer

  INTCONbits.T0IF = 0; // reset timer interrupt flag
  INTCONbits.T0IE = 1; // enable timer interrupts

  RCONbits.IPEN = 0; // no priorities
  INTCONbits.PEIE = 1; // enable peripheral interrupts
  INTCONbits.GIE = 1; // enable interrupts globally
}

void uart_setup(void)
{
  TRISCbits.TRISC6 = 0; // TX as output
  TRISCbits.TRISC7 = 1; // RX as input

  TXSTA1bits.SYNC = 0; // Async operation
  TXSTA1bits.TX9 = 0; // No tx of 9th bit
  TXSTA1bits.TXEN = 1; // Enable transmitter

  RCSTA1bits.RX9 = 0; // No rx of 9th bit
  RCSTA1bits.CREN = 1; // Enable receiver
  RCSTA1bits.SPEN = 1; // Enable serial port

  // Setting for 19200 BPS
  BAUDCON1bits.BRG16 = 0; // Divisor at 8 bit
  TXSTA1bits.BRGH = 0; // No high-speed baudrate
  SPBRG1 = 51; // divisor value for 19200
}


unsigned char light_status, tick_count; // 8 bit vars
unsigned char off_timer = 20, signal_timer = 10;

#define ON    0
#define OFF   1

void interrupt isr(void)
{
  if (INTCONbits.T0IF == 1) {
    TMR0 = TIMER_VAL;
    if (light_status == 0) {
        LIGHT = OFF;
        SIGNAL = OFF;
        tick_count = 0;
    }
    else { // light_status == 1
      LIGHT = ON;
      tick_count++;
      if (tick_count >= off_timer) {
        light_status = 0;
        LIGHT = OFF;
        SIGNAL = OFF;
      }
      else if (tick_count >= signal_timer) {
        SIGNAL = !SIGNAL; // flashing
      }
    }
    INTCONbits.T0IF = 0;
  }
}

char read_char(void)
{
  while (PIR1bits.RC1IF == 0) { // wait for char
    if (RCSTA1bits.OERR == 1) {
      RCSTA1bits.OERR = 0; // clear overrun if it occurs
      RCSTA1bits.CREN = 0;
      RCSTA1bits.CREN = 1;
    }
  }
  return RCREG1;
}

int nb_read_char(char * c_ptr) // non blocking version
{
  if (RCSTA1bits.OERR == 1) { // check for errors in anycase
    RCSTA1bits.OERR = 0; // clear overrun if it occurs
    RCSTA1bits.CREN = 0;
    RCSTA1bits.CREN = 1;
  }
  if (PIR1bits.RC1IF == 0) {
    return 0; // no char available
  }
  else { // get char
    *c_ptr = RCREG1;
    return 1;
  }
}

void putch(char c) {
  // wait the end of transmission
  while (TXSTA1bits.TRMT == 0) {};
  TXREG1 = c; // send the new byte
}

void read_line(char * s)
{
    for (;;) {
        char c = read_char();
        if (c < ' ') { // it is a control char
          if (c == 0x0d) { // CR
            putch(13);
            *s = 0;
            return;
          }
        }
        else { // it is a printable char
            putch(c);
            *s = c;
            ++s;
        }
    }
}

void enter_config(void)
{
    char command[40];
    printf("CONFIG>");
    for (;;) {
        read_line(command);
        if (strcmp(command,"end") == 0) {
            printf("END OF CONFIGURATION\n");
            return;
        }
        else if (strncmp(command,"timeout",7) == 0)
            off_timer = atoi(command+7) * 2;
        else if (strncmp(command,"signal",6) == 0)
            signal_timer = atoi(command+6) * 2;
        else
            printf("Invalid command\n");
    }
}

int main(void) {
  int asterisk_count = 0;

  TRISAbits.TRISA3 = 1; // input
  TRISAbits.TRISA2 = 1; // input
  TRISBbits.TRISB0 = 0; // output
  TRISBbits.TRISB5 = 0; // output

  light_status = 0;
  tick_count = 0;

  uart_setup();
  timer_setup();

  for (;;) {
    char c;
    if (PB1 == ON) {  // turn on the light and re-arm timer
      light_status = 1;
      tick_count = 0;
    }
    if (PB2 == ON) { // turn off the light
      light_status = 0;
    }
    if (nb_read_char(&c) == 1) { // a char is got
      if (c == '*') {
        ++asterisk_count;
        if (asterisk_count == 3) {
          enter_config();
          asterisk_count = 0;
        }
      }
      else
        asterisk_count = 0;
    }
  }
  return 0;
}
