/*[ 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 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 flag = cnt & 1; if(flag == 0) { // if odd #of char's then zero_bits++; buf_char(CR); // Send 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++; }