I2C communication to external EEPROM via digital pins

There have been occasional questions about the possibility of using digital pins instead of analog pins for I2C communication. Also I myself had the need to save analog pins for other purposes and so I wrote a code which enables any two digital pins to take over I2C communication, in this example with a 24lc256 EEPROM. The sketch shows how to write and to read a number to and from any address of the eeprom.
I'm not a pro so there might be space for improvement. If someone out there wants to transform it into a proper library, please feel free. Now to the sketch. Hope you'll enjoy. Here we go...

/*Example code for using two digital pins to establish I2C communication 
 with a 24LC256 EEPROM
 Written by Martin Thalheimer, 2011.
*/

byte data = 4;  //digital pin 4 = data - connect to pin 4 of 24lc256
byte clock = 5;  //digital pin 5 = clock - connect to pin 5 of 24lc256
int address=160;  //address of the 24lc256 with all address pin tied to GND
int i2c_out;
int i;
int number=234;   //put here a number of your choice (0-255) to be stored in the external eeprom
int memory_address=23456; /*put here the address of your choice (0-32767) within the eeprom where
                            you want the number to be stored*/
int adr_hi;
int adr_low;

 void setup(){
 Serial.begin(9600);
 pinMode(clock, OUTPUT);
 pinMode(data, OUTPUT);
 }

 void loop() {
   Serial.println("writing...");
   dig_eeprom_write(); //this function writes the number into the external eeprom
   Serial.println(number);
   Serial.print("to eeprom address ");
   Serial.println(memory_address);
    
   Serial.println("reading...");
   dig_eeprom_read(); //this function reads the number from the external eeprom
   Serial.println(i2c_out);
   Serial.print("from eeprom address ");
   Serial.println(memory_address);
   Serial.println();
 
   delay(2000);   
   }
      
  void dig_eeprom_write() {
   common_routine(); 
   i2c_out=number; 
   putbyte();   //the number is being sent to the eeprom
   getack();   //get acknowledge
   Stop();
   delay (100); //
   }  
  
  void dig_eeprom_read() {
   common_routine(); 
   start();
   i2c_out=address+1; //address of 24lc256 in read mode
   putbyte();   //send byte
   getack();   //get acknowledge
   getbyte(); //retrieve content of the memory cell
   givenoack();   //no acknowledge
   Stop();
   delay(10); 
   }
   
  void common_routine () {  //this part is common to each writing or reading cycle
    start();    //start signal for eeprom to wake up
    i2c_out=address; //address of 24lc256 in write mode
    putbyte();   //send byte
    getack();   //get acknowledge
    adr_hi=memory_address/256;    //
    adr_low=memory_address % 256; //
    i2c_out=adr_hi; //send the highbyte of the memory-address
    putbyte();   //send byte
    getack();   //get acknowledge
    i2c_out=adr_low; //send the lowbyte of the memory-address
    putbyte();   //send byte
    getack();   //get acknowledge
    }    
            
   void start() {
       digitalWrite (clock, HIGH); 
       digitalWrite (data, HIGH); 
       digitalWrite (data, LOW); 
       digitalWrite (clock, LOW);
       }
    
    void putbyte() {
       for (i=7; i>=0; i--){
       if ((i2c_out &(1<<i))==0) {   
         digitalWrite (data, LOW); 
       }
        else {
          digitalWrite (data, HIGH); 
       }
       digitalWrite (clock, HIGH);    //pulse clock
       digitalWrite (clock, LOW); 
       }
       }
        
 void getbyte() {
       pinMode(data, INPUT);
       i2c_out=0;
       for (i=7; i>=0; i--){
       if (digitalRead(data)==HIGH) {   
       i2c_out=(i2c_out + (1<<i)); 
       delay(10);
       }
       digitalWrite (clock, HIGH);    //pulse clock
       digitalWrite (clock, LOW); 
       }     
       pinMode(data, OUTPUT);   
       }
    
  void getack() {
      digitalWrite (data, HIGH); 
      pinMode(data, INPUT);
      digitalWrite (clock, HIGH);
      if(digitalRead(data)==LOW) {
      digitalWrite (clock, HIGH); //acknowledge ack
      digitalWrite (clock, LOW);
      }
      pinMode(data, OUTPUT); 
      }
      
   void Stop() {  
      digitalWrite (data, LOW); 
      digitalWrite (clock, HIGH);  
      digitalWrite (data, HIGH); 
      }
   
  void givenoack() {
     digitalWrite (data, HIGH);
     digitalWrite (clock, HIGH);    //pulse clock
     digitalWrite (clock, LOW); 
     digitalWrite (data, LOW);
     }

I have also for some time now wondered why there isn't any Software I2C or SPI library. There is a Software library for UART (SoftSerial, NewSoftSerial) so why not for I2C and SPI???

I would really like to make a library, and your code is a good thing to start from, but I think i don't have the time to do that. Perhaps some time the next week.

Jan

OK, would be great!

Martin

Soft I2C => check this blog article => SoftI2CMaster Library Lets You Add I2C To Any Arduino Pin | Arduino Blog

As it is work in progress maybe you can add your energy to get this effort on a higher level iso starting a new one?

my 2 cnts,
Rob