Hello,
I was in need for a fast I2C snifer.
after buying and trying buspirate, I realized I had no choice writing one.
it works pretty well and was successfully snifing 400khz streams.
only software, no use of TWI. can snif on any pin.
this is my first C program so please appology for lack of presentation.
guess it is self explaining:
/*******************************************************
/********************* SNIFER I2C **********************
/*** author maxidcx / forum arduino ********************
/*******************************************************/
#define SNIF_SCL (PINC & B010000) // read port value and isolate SCL pin
#define SNIF_SDA (PINC & B100000) // read port value and isolate SDA pin
#define I2C_STOP -30000Â Â Â Â // this value will be stored in the circular buffer when receiving stop sequence
#define I2C_EOFÂ -30001Â Â Â Â // this value will be return if snif_GET is involved while buffer is empty
#define snif_word_size 3Â Â Â // this is the word size for the received data.
               // Used only to format printing and count.
               // 3 in this example which is appropriate for 24bits DSP.
               // should be 1 or 2 for most usage.
#define snif_buffer_size 800 // buffer size (2 bytes per entry) Be carefull to comply with ATMEGA available RAM
#define snif_buffer_print 700Â // will authorize printing if buffer reach 500 !
int snif_RX[snif_buffer_size]; // the buffer
int snif_pos_IN=0;Â Â Â Â Â Â // position where toputthe next value in the buffer
int snif_pos_OUT=0;Â Â Â Â Â Â // position where to take the next value from the buffer
int snif_qty=0;Â Â Â Â Â Â Â Â // total number of value in the buffer
long snif_last_time=0;Â Â Â Â // used to stamp when the last frame was arrived
int snif_frame_qty=0;Â Â Â Â Â // number of frames in the buffer
int snif_word_count;Â Â Â Â Â // used during printing/formating to identify words and separate them by char 0x20
int snif_frame_size=0;Â Â Â Â // used by print function to count number of caracters in the frame displayed
/*************Â PRINTING BUFFER *********************/
// print all the caracters from the buffer,
// if available , and at least x miliseconds
// after the last frame is received
// calling this function just print 1 caracter from the buffer
/***************************************************/
void snif_PRINT(word delay_min) {
int x;
int count_word;
if (snif_qty) { // will enter only if some characters are available in the buffer
 if ( ((millis()-snif_last_time) > delay_min) || // wait at least x ms, to avoid loosing frames
   (snif_qty>snif_buffer_print)) { // or buffer becoming full
  x=snif_GET();
  if (x >=0) { snif_frame_size++;
        if (x & 0x0200) { // this is an address
          if (x & 0x0001) { Serial.print("R @");} // READ
                else { Serial.print("W @");} // WRITE
          Serial.print((x & 0x00FE)>>1,HEX);   // print adress
          snif_word_count=-1; // initialize word counting
          } else { // this is a normal data
           if ((x & 0x00F0)==0) { Serial.print("0");} // add a "0" when value < 0x10)
           Serial.print(x & 0x00FF,HEX);
           }
        if (x & 0x0100) { Serial.print("^");} // NACK
              else {Serial.print("_");} // ACK
        if (x & 0x0200) { Serial.print(" "); } // separate adress and data by one 0x20
        snif_word_count++;
        if (snif_word_count==snif_word_size) {
          Serial.print(" "); // separate each word with a char 0x20 for good visibility
          snif_word_count=0;
          }
       }
   else {
    switch (x){
    case I2C_STOP: Serial.println(""); // next line
           Serial.print("<STOP ");
           Serial.print(snif_frame_size);
           Serial.print("b");
           count_word = snif_frame_size / snif_word_size;
           if (count_word) {
             Serial.print("=");
             Serial.print(count_word);
             Serial.print("w");
             if (snif_frame_size - snif_word_size * count_word) {
               Serial.print("+");
               Serial.print(snif_frame_size - snif_word_size*count_word);
               Serial.print("b");
               }
             }
           Serial.println(">");
           snif_frame_qty--; // one frame is now completely printed
           break;
    case I2C_EOF: Serial.println("<EOF>");break; // probably never happens !
    default:   Serial.println(""); // this correspond to a Start with time stamp
           Serial.print("<START ");Serial.print(-x);Serial.print("ms> ");
           snif_frame_size=-1; // reset frame size (counting without adress)
    }
   }
  }
 }
}
/************** CIRCULAR BUFFER HANDLING ****************/
void snif_PUT(int x) {
if (snif_qty >= snif_buffer_size) {return;}
snif_RX[snif_pos_IN++]=x;
if (snif_pos_IN>=snif_buffer_size) {snif_pos_IN=0;}
snif_qty++;
}
int snif_GET() {
int value;
 if (snif_qty<1) {return I2C_EOF;}
 value = snif_RX[snif_pos_OUT++];
 if (snif_pos_OUT>=snif_buffer_size) {snif_pos_OUT=0;}
 snif_qty--;
 return value;
}
boolean snif_available(){
if (snif_qty>0) { return true;} else {return false;}
}
/************* SENSITIVE PART *******************/
boolean snif_SCL_low(){ // return true if timeout or stop bit otherwise wait clk to go low
 byte SCL;
 byte SDA;
 byte old_SDA;
 byte i;
SDA=SNIF_SDA;
for (i=1; i<200; i++) { // 200 loop max for time out handling
 if (SCL=SNIF_SCL) { // SCL still high
  old_SDA=SDA;
  if (SDA=SNIF_SDA) {
   if (old_SDA==LOW) {return true;} // stop signal recognized
   }
  }
 else { return false;} // SCL is LOW
 }
return true; // too long / timeout
}
int snif_8bits(){ // read 8 bits data + ack/nack flag
int value;
byte SDA;
// when entering this function, the SCL pin is expected to be LOW
value=1; // after 8 loops, this bit0 will become bit8 and will stop the loop
do {
 while (SNIF_SCL==LOW) { } // wait SCL = HIGH
 if (SDA=SNIF_SDA) {value = (value <<1) + 1; } else {value = value <<1; }
 if (snif_SCL_low()) { return I2C_STOP;} // wait until SCL=LOW
} while (value<0x100); // until the bit0 comes in postion 8
while (SNIF_SCL==LOW) { } // wait SCL becomes HIGH
if (SNIF_SDA) { return value; } else { return value & 0xFEFF;} // NACK or ACK is stired in bit 8
// when leaving this function, the SCL is HIGH hopefully
}
/******** MAIN USER FUNCTION **********/
void snif_READ(int n){Â // check Start condition during n ms then read all bit until Stop condition
int value;Â
byte SCL,SDA,old_SCL,old_SDA;
long time_stamp;
while (n>0) {
 time_stamp=millis();
 cli(); // stop interupt to avoid loosing start bit condition, during about 1ms
tryagain:
 SCL=SNIF_SCL; SDA=SNIF_SDA;
 for (int i=0; i<1000; i++) { // 1000 seems to be around 1ms . not critical at all
   old_SCL=SCL;SCL=SNIF_SCL;
   old_SDA=SDA;SDA=SNIF_SDA;
   if (SDA==LOW) {
    if (old_SDA != LOW) {
     if ((old_SCL != LOW) && (SCL != LOW)) { goto startcondition; }
     }
    }
   }
 n--;
 sei(); // restore interuption
}
return; // end of n milisecond
startcondition:Â Â // start condition detected (falling SDA while clk=1)
snif_PUT(-((snif_last_time=time_stamp)& 16383)); // store value in buffer rounded to 16 seconds
snif_frame_qty++;Â Â Â Â Â // one more frame is coming
if (snif_SCL_low()) { goto endofstory;}Â // sorry for "goto" but still a good way of handling branching!
value = snif_8bits(); if (value<0){ goto endofstory;}
snif_PUT(value | 0x0200 ); // this is an adress, set bit 9 for further decoding during printing
frameloop:
 if (snif_SCL_low()) { goto endofstory;} // wait next SCL = 0
 value = snif_8bits();
 if (value<0) { goto endofstory; }
 snif_PUT(value); // store value in snif buffer
 goto frameloop;  // look for additional caracters
Â
endofstory:
snif_PUT(I2C_STOP);
goto tryagain; // that way we look for another start condition imediately, without releasing interupt...
}
void snif_INIT() {
snif_pos_IN=0;
snif_pos_OUT=0;
snif_qty=0;
snif_frame_qty=0;
snif_last_time=millis();
}
void setup() {
Â
Serial.begin(115200); // preferably high speed to reduce chnace of loosing I2C start bit
snif_INIT();
}
void loop() {
snif_READ(5);Â Â //Â look for start during 10ms ish
snif_PRINT(100);Â // each caractere will be printed one by one after a timeout period (1 sec )
         // you should adjust this value so the printing could occur between some I2C exchange otherwise buffer will get full !
// your other loop code goes here, like trigering events or so
}
enjoy it