/*[ Copyright & GPL ]*********************************************************

 Copyright (C) 2001 Neil Cherry (ncherry@comcast.net)

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at
 your option) any later version.

 This program is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
 USA.

*****************************************************************************/
                                                                              
/**[ X10 Sniffer ]************************************************************

 Target:  PIC16F877

       Baud: 9600
       Bits: 8
     Parity: none
 Stop bits : 1

 Warning: Clock frequency dependant

 The X10 sniffer is a very simple device, It monitors the ZC, when a ZC occurs
 it  samples the Rx ~400ms later and then outputs either an ASCII 1 or a 0 to
 the RS232  port. If we received 6 0's then we output a <CR><LF> and wait until
 we get another 1 bit before we start outputting more characters.

 ----------------------------------------------------------------------------
 Date Who Comments
 ---- --- -------------------------------------------------------------------
01/00 njc Well this is the first go around with the C2C compiler. It has a
          idiosynchronousys of it own. You have to be careful with if
          statements and compares. But it does keep track of the variables.
          I've removed much of the assemly langauge (except for a few debug
          sections of code).
          Currently the RS232 code is working and so is the External Interrupt
          code. I'm having a great deal of trouble with the Timer0 code and
          I'll need to find an example of how it works and copy it.
01/00 njc I have all the code working but I have the X10 Rx input on PortD
          for now. I'm having some kind of trouble with PortA. It's probably
          the A/D converter. I'll check this later.
01/00 njc Here is a sample output for a On P3
06/01 njc I finally have it outputting an even number of 1's & 0's. This C
          compiler can be a great pain sometimes!
[ X10 Sniffer ]

-- on p3

1:1.1:0.1:0.1:0.0:1.0:1.0:1.0:1.1:0.0:1.0:1.1:1.1:0.1:0.1:0.0:1.0:1.0:1.0:1.1:0.0:1.0:1.0:0.0:0.0:0.
1.1:1.0:1.0:1.0:0.1:0.1:0.1:0.1:1.0:0.1:1.0:1.1:1.0:1.0:1.0:0.1:0.1:0.1:0.1:1.0:0.1:1.0:0.0:0.0:0.

         1:1.1:0.               - SOH
         1:0.1:0.0:1.0:1.       - House Code P (1100)
         0:1.0:1.1:0.0:1.0:1.   - Number Code 3 (00100)
         1:1.1:0.               - SOH
         1:0.1:0.0:1.0:1.       - House Code P (1100)
         0:1.0:1.1:0.0:1.0:1.   - Number Code 3 (00100)
         0:0.0:0.0:0.           - Pause

         1110           - SOH
         10100101       - House Code P (1100)
         0101100110     - Function Code On (00101)
         1110           - SOH
         10100101       - House Code P (1100)
         0101100110     - Function Code On (00101)
         00000          - Pause

-- dim P3

1.1:1.0:1.0:1.0:0.1:0.1:0.1:0.1:1.0:0.1:0.1:1.1:1.0:1.0:1.0:0.1:0.1:0.1:0.1:1.0:0.1:0.1:0.0:0.0:0.0:
1.1:1.0:1.0:1.0:0.1:0.1:0.1:1.0:0.1:0.1:1.0:1.1:1.0:1.0:1.0:0.1:0.1:0.1:1.0:0.1:0.1:1.0:0.0:0.0:0.

         1:1.1:0.               - SOH
         1:0.1:0.0:1.0:1.       - House Code P (1100)
         0:1.0:1.1:0.0:1.0:1.   - Number Code 3 (00100)
                                  :
         1:1.1:0.               - SOH
         1:0.1:0.0:1.0:1.       - House Code P (1100)
         0.1:1.0:0.1:0.1:1.0    - Function Code Dim (01001)

-- Command sent from Dan's x10 'x10 u9v32preset' (Extended command u9v32t49A24)

1.1:1.0:0.1:1.0:1.0:0.1:0.1:1.0:1.0:1.0:1.0:0.1:1.0:1.0:1.0:0.1:0.1:1.0:0.1:0.1:
0.1:0.1:0.1:0.1:0.1:1.0:1.0:0.1:0.1:0.1:1.0:

1.1:1.0:0.1:1.0:1.0:0.1:0.1:1.0:1.0:1.0:1.0:0.1:1.0:1.0:1.0:0.1:0.1:1.0:0.1:0.1:
0.1:0.1:0.1:0.1:0.1:1.0:1.0:0.1:0.1:0.1:1.0:
0.0:0.0:0.

        1.1:1.0:                - SOH 
        0.1:1.0:1.0:0.1:        - I (0111)

procedure tmr0_rollover is
--------------------------------------------------------------------------
clear_watchdog   -- initialize timer0/prescaler for x_ms rollover bank_1
option = 1       -- set prescaler at 7:1/256 6:1/128 5:1/64 4:1/32 3:1/16
                 -- 2:1/8 1:1/4 0:1/2
bank_0
-- tmr0 = 256 - t*Fclk/4*prescaler; t=time in ms, Fclk=osc frequency in kHz
-- t = 1 ms
-- Fclk = 4000
-- tmr0 = 6
end procedure

******************************************************************************/

#define ICD	1                   // Turn on ICD reserved memory locations
#define SNIFFER	1

#include "x10.h"

//-----------------------------------------------------------------------------------
//                                    Start of MAIN
//-----------------------------------------------------------------------------------

char cnt;

void main(void)
{
  char BufIndex;

  chcount     = 0;
  BufferIndex = 0;
  zero_bits   = 7;
  cnt         = 0;

  Setup();                           /* Setup the PIC */

  BufIndex    = 0;
  
  SendChar( '*' );                /* Send a message to the terminal */
  SendChar( CR );
  SendChar( NL );
  
  while( 1 ) {
    while( chcount  ) {
      SendChar( RxFifo[ BufIndex++ ] );
      if (!(BufIndex < RX_BUFFER_SIZE)) {
        BufIndex = 0;
      }
      if(chcount) {
        chcount--;
      }
    }   
  }// end while 1
} // end of Main()                 

/***[ Interrupt routine ]****************************************************

 Three things can interrupt us:

 1) A Character from the serial port, we buffer that
 2) A Zero crossing, that starts the sample timer (Ignore Phases for now)
 3) A Smaple timer, we take a sample of the X10 Rx (from the power line)

 The interrupts and flags we need are:

 Timer0 - TOIF (flag, INTCON<2>) and T0IE (mask, INTCON<5>)
        - X10 Rx Sample timer
 RB0    - INTF (flag, INTCON<1>) and INTE (mask, INTCON<4>)
        - X10 Zero Crossing
 Rxd    - RCEF (flag,   PIR1<5>) and PIE1 (mask,   PIE1<5>)
        - RS232 Rxd

 ------------------------------------------------------------------------
            A           1                  A           1
  SOH |  Letter  :   Number   |  SOH |  Letter  :   Number   | (silence)
 1110 | 01101001 | 0110100101 | 1110 | 01101001 | 0110100101 | 000000

 Above is the first half of an X10 command, the same 1/2 command is sent
 twice. Then the second half is sent (again twice). This sniffer will send
 out the above (without the spaces or '|'s) and send a CR/NL after the 6
 silence 1/2 bits.

*****************************************************************************/

void interrupt(void)
{
  char i, flag;

  // -----------------------------------------------------------------------
  if((PIR1 & RCIF_MASK) != 0) {          // If USART RX Interrupt
    RxChars();                           // Process the received character
    clear_bit(INTCON, T0IF);             // Reset Timer0 interrupt flag
    clear_bit( PIR1, RCIF );             // Clear flag  
  }

  // -----------------------------------------------------------------------
  if((INTCON & INTF_MASK) != 0) {               // If RB0/INT - Zero Crossing
    asm:L2;
    asm nop ;

    clear_bit(INTCON, INTF);
    
    if( Opt == Opt1) {
      OPTION_REG = Opt2;                 // Set Option register 1100 0111
      Opt        = Opt2;
    } else {
      OPTION_REG = Opt1;                 // Set Option register 1000 0111
      Opt        = Opt1;
    }

    // Start sample timer (400ms)

    // Init_TRM0 = 256 - ((DELAY * Frequency)/(4 * Prescaler))
    // Init_TRM0 = 256 - ((x uS * 4 MHz)/(4 * 2))
    // uS cancels MHz, 4/4 = 1, - 3 for initial instruction delay

    // Set the time to 400 ms
    TMR0 = 59;                          // 59 = 256-((400/2)-3)

    clear_bit(INTCON, T0IF);            // Reset Timer0 interrupt flag
    enable_interrupt( T0IE );           // Enable Timer0 interrupt
    
  // -----------------------------------------------------------------------
  } else if((INTCON & TOIF_MASK) != 0) {       // Timer 0 - Sample X10 Rx
    clear_bit(INTCON, T0IF);            // Reset Timer0 interrupt flag
    disable_interrupt( T0IE );          // Disable Timer0 interrupt

    // When the timer goes off we sample
    //i = input_pin_port_a( Rx );         // Sample the pin

    asm {
      ; // i = input_pin_port_D( Rx );    // Sample the pin
      clrw ; //
      ;//bcf STATUS, RP0 ;
      ;//bcf STATUS, RP1 ;
      btfsc PORTD, D'0' ;
      movlw D'1' ;
      movwf _i_interrupt ;
    }

    if(zero_bits < 7)
      cnt++;
    
    // Then buffer either a 0 or 1
    
    if(i != 1) {                        // Bits are inverted
      // buffer an ASCII 1
      zero_bits = 0;
      
      buf_char('1');                    // Send '1'
      
    } else {
      // buffer an ASCII 0
      if(zero_bits < 6) {
        // Output a 0
        zero_bits++;

        buf_char('0');                  // Send '0'
        
      } else if(zero_bits == 6) {       // 6 0's, done with command
        // Output a <CR><NL>

        flag = cnt & 1;

        if(flag == 0) {   		// if odd #of char's then
          zero_bits++;

          buf_char(CR);                 // Send <CR><NL>
          buf_char(NL);
          cnt = 0;
        } else {
          buf_char('0');
        }
      } // else output nothing
    }
    clear_bit(INTCON, T0IF);
  }
  // -----------------------------------------------------------------------

// Return from Interrupt
}

/*****************************************************/ 
/* setup PIC16F877 options,ports,interrupts          */
/*****************************************************/
void Setup(void)
{
  INTCON = 0x00;                     // Disable all interrupts
  Opt        = Opt1;
  OPTION_REG = Opt1;                 // Set Option register 1000 0000
                                     // RBPU off (<7> = 1)
                                     // Prescaler = Timer0
                                     // TMR0 rate := 1:2
  // And what of ADCON0 ???
  ADCON0 = 0x00;                     // Turn off A/D
  ADCON1 = 0x06;                     // Disable ADC on Ports A & E

  TRISA = PortAConfig;
  asm clrf PORTA ;
  TRISB = PortBConfig;
  asm clrf PORTB ;
  TRISC = PortCConfig;    
  asm clrf PORTC ;
  TRISD = PortDConfig;
  asm clrf PORTD ;
  TRISE = PortEConfig;
  asm clrf PORTE ;
 
  PIR1 = 0;
  
  ConfigureComms();                  // Configure USART for Asyncronous

  //INTCON = (GIE_MASK | PEIE_MASK | INTE_MASK | ~T0IE_MASK); 1101 0000
  INTCON = 0xD0;                     // 1101 0000
                                     // Enable Global Interrupts
                                     // Enable all Peripheral Interrupts
                                     // Enable External Interrupt on RB0
}
 
/*******************************************************/
/* Configure USART for communications                  */
/*                                                     */
/* Asynchronous mode                                   */
/* 9,600 Baud   ( With 4.00 Mhz Clock )                */
/* 8 data bits  ( For other rates see PIC16F8XX Data ) */
/* 1 stop bits                                         */
/* No Parity                                           */
/*                                                     */
/*******************************************************/
void ConfigureComms(void)
{
    set_bit( RCSTA, SPEN );    // Enable Serial port
    clear_bit( RCSTA, RX9 );   // 8 bit receive mode
        
    clear_bit( TXSTA, TX9 );   // 8 bit transmit mode    
  
    SPBRG = 25;                // SPBRG = 22 ( Set Baud rate   9,600 )             

    set_bit( TXSTA, BRGH );    // RRGH = 1   ( High speed mode )
    clear_bit( TXSTA, SYNC );  // Asyncronous mode;
    
    set_bit( TXSTA, TXEN );    // Enable Transmitter
    
    set_bit( PIE1, RCIE );     // Enable Receive Interrupt
    
    set_bit( RCSTA, CREN );    // Enable continuous receive
    clear_bit( PIR1, RCIF );   // Clear Receive Interrupt flag

    set_bit( INTCON, PEIE );   // Enable all Peripheral Interrupts
    set_bit( INTCON, GIE );    // Enable Global Interrupts    
}

/*****************************************************/
/* Send a character over the RS232 Port              */
/*                                                   */
/*                                                   */
/*****************************************************/
void SendChar(char ch)
{
  while ((TXSTA & TRMT_MASK) == 0); // Wait for TX Empty

  TXREG = ch;                      // Load the TXREG
}

/*****************************************************/
/* Receive a character over the RS232 Port           */
/*                                                   */
/* Called from Interrupt service routine             */
/*                                                   */
/* Returns the char received                         */
/* and saves it in the buffer                        */
/*                                                   */
/*****************************************************/
char RxChars(void)
{
  if ( ( RCSTA & 6 ) == 0 )     // Then if no errors              
  {                             // Process received character
    buf_char( RCREG );          // Save the data

    set_bit( RCSTA, CREN );     // Enable receiver.
  }
  else
  {
//          process any errors here
//          Beware, we are in the Interrupt routine.
           
//          ...
           
    clear_bit( RCSTA, CREN );   // Clear any errors   
    set_bit( RCSTA, CREN );     // Enable receiver.
  }  
        
  return RCREG;    
}

void buf_char( char c ) 
{
  if (!(BufferIndex < RX_BUFFER_SIZE)) {
    BufferIndex = 0;
  }  

  RxFifo[ BufferIndex++] = c; // Save the data        
  if(!(chcount == RX_BUFFER_SIZE))
    chcount++;
}
