//
// Title        : Quick and dirty IR Message analyzer
// Author       : Lars Pontoppidan Larsen
// Date         : February, 2006
// Version      : 1.00
// Target MCU   : ATMega88
//
// DESCRIPTION:
// This is the implementation of the IR message analyzer. Remote control 
// messages are analyzed and transmitted over UART. 
//
// Timer1 is used to measure timing and to determine when message has stopped.
//
// HARDWARE CONNECTIONS:
// External interrupt 0 (PD0) should be connected a IR reciever device such as 
// SFH5110 that performs amplification and bandpass filtering of the carrier 
// frequency.
//
// The UART is used at 9600 to transmit message analysis. Sending an 'r' or 'd' 
// character instructs the program if it should transmit raw message or decoded 
// message.
//
// An LED is flashed with PB1 
//
// 
// DISCLAIMER:
// The author is in no way responsible for any problems or damage caused by
// using this code. Use at your own risk.
//
// LICENSE:
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//


#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "uart.h"
#include "util.h"


/* LED abstraction */
#define SET_LED_DDR()  DDRB |= 1
#define LED_ON()       PORTB &= ~1
#define LED_OFF()      PORTB |= 1


/* Timer abstraction */
#define TIMER_VALUE TCNT1

#define MSG_THRESH  1400    // The threshold seperates long from short pulses
#define MSG_MAX  4000       // Going above this threshold ends message 

/* IR message buffer */
#define IR_BUFFER_SIZE 100

unsigned char ir_buffer_size=0;

struct {
  unsigned short hi_time;
  unsigned short lo_time;
} ir_buffer[IR_BUFFER_SIZE]; // The raw message 

unsigned char ir_msg[8]; // The decoded message.

unsigned char ir_bit; // Progress in the message


#define IR_IDLE 0
#define IR_RECIEVING 1
#define IR_MSG_IN_BUFFER 2

volatile unsigned char ir_status;

// External interrupt 0
ISR(INT0_vect)
{
  // Save pin status and timer value now
  unsigned char c;
  unsigned short tvalue;
  c = (PIND & (1<<2));
  tvalue = TIMER_VALUE;
  
  if (ir_status == IR_IDLE) {
    tvalue = 0;
    TIMER_VALUE = 0;
    ir_buffer_size = 0;
    ir_status = IR_RECIEVING;
    // Clear msg
    unsigned char i;
    for (i=0; i<8; i++)
      ir_msg[i]=0;
      
    ir_bit=0;
  }
  
  if (ir_status == IR_RECIEVING) {
    
    if (ir_buffer_size < IR_BUFFER_SIZE) {
      if (!c) {
        // Hi -> Lo
        LED_ON();
        
        // Raw message buffer
        ir_buffer[ir_buffer_size].hi_time = tvalue;
        
      }
      else {
        // Lo -> Hi.
        LED_OFF();
        
        // Raw message buffer
        ir_buffer[ir_buffer_size].lo_time = tvalue - ir_buffer[ir_buffer_size].hi_time;
        ir_buffer_size++;
        
        TIMER_VALUE = 0;
        
        // Assemble message. Ignore first two bits and overflow pulses
        if ((ir_buffer_size > 2) && (tvalue < MSG_MAX)) {
          
          ir_msg[(ir_bit>>3)] = (ir_msg[(ir_bit>>3)] << 1) | (tvalue > MSG_THRESH ? 1 : 0);
            
          if (++ir_bit > 63) {
            ir_bit = 0; // Just prevent havoc, message has overflowed
          }
          
        }
        
      }
      
    } 
    
  }
  
}

// Timer1 overflow
ISR(TIMER1_OVF_vect)
{
  if (ir_status == IR_RECIEVING) {
    // Message have finished
    ir_status = IR_MSG_IN_BUFFER;
  }
}

void print_nl(void)
{
  uart_putc('\r');
  uart_putc('\n');
}  

// Sends a binary long as ascii characters.
//
// Prints as x.xxx.xxx.xxx, leading zeros will be printed as spaces.
//
void printlong(unsigned long l)
{
  char buffer[13];
  
  long2ascii(buffer, l);
  
  uart_printlen(buffer,13);
  
}


void print_msg(void)
{
  unsigned char i;
  unsigned long sum=0;

  uart_printprog(PSTR("New msg.\r\n"));

  for (i=0; i<ir_buffer_size; i++) {
    printlong(ir_buffer[i].hi_time);
    printlong(ir_buffer[i].lo_time);
    
    printlong(ir_buffer[i].lo_time + ir_buffer[i].hi_time);
    sum += ir_buffer[i].lo_time + ir_buffer[i].hi_time;
    print_nl();

//     uart_putc(ir_buffer[i] + 'A');
  }

  print_nl();
  
  printlong(ir_buffer_size);
  uart_printprog(PSTR(" <- length\r\n"));

  printlong(sum);
  uart_printprog(PSTR(" <- time total\r\n\r\n"));
}

int main(void)
{
  unsigned char raw = TRUE;
  unsigned char c;
  
  /* Initialization */
  uart_init();
  LED_OFF();
  SET_LED_DDR();
  
  /* Setup ext interrupt 0 */
  DDRD &= ~(1<<2);
  PORTD &= ~(1<<2); // No pullup
 
  EICRA = (1<<ISC00);
  EIMSK = (1<<INT0);
  
  /* Setup timer1 to count 1/100000 seconds */
  TCCR1A = 0;
  TCCR1B = (1<<CS11);
  TIMSK1 = (1<<TOIE1);
  TIMER_VALUE = 0;
  
  ir_buffer_size = 0;
  ir_status = IR_IDLE;

  asm("sei");
  
  uart_printprog(PSTR("IR message decoder.\r\nPress 'r' to show raw message, 'd' to show decoded message\r\n"));
  
  LED_ON();
  ms_spin(200);
  LED_OFF();
  
  while(TRUE) {
    if (!uart_buffer_empty()) {
      switch(uart_getc()) {
      case 'r':
        raw = TRUE;
        break;
      case 'd':
        raw = FALSE;
        break;
      } 
    }
    
    if (ir_status == IR_MSG_IN_BUFFER) {
      
      if (raw) {
        print_msg();
      }
      
      for (c=0; c<8; c++)
        uart_printchar(ir_msg[c]);
        
      print_nl();  
    
      ir_status = IR_IDLE;
    }
    
  }
  
}

