AT24C256 EEPROM write failed

Hi, I'm currently working on a code to write Mifare Classic 1K card dumps to AT24C256 via arduino and python.

Each Mifare Classic 1K card, as it's name implied, contains 1024 bytes of data.
An AT24C256, according to it's data sheet, has 32768 bytes of storage.

Theoretically I can store 32 Mifare cards in an AT24C256, right?

So I:

  1. Wrote a python code to read card dump and write to serial.
  2. Wrote an arduino sketch to read bytes written to serial by python code.
  3. Store bytes into AT24C256.

But actually none of above worked.

I'm using codes from here and the example worked nicely, so it's not a problem of my AT24C256.
It's just somehow when I used it my way it totally won't work.

My python code and R/W leds on arduino board shows that there actually are some serial R/W happening. But whenever I read from AT24C256, the result ain't what it should be.

I'm wondering if I am writing my code in the wrong way from the very beginning. All the examples I can find won't deal with reading and writing so much data by serial. Since I never wrote code working on serial before, I added a lot of controlling conditions and it just gone out of my control and made it super difficult for me to debug.

I'm guessing it's the eeprom R/W part of my code causing problem so I posted here. Attachments are my codes and example card dump, hope some one can help me with this. :cry: :cry:
I'm not a professional programmer so my code may look very crappy, please forgive me.

Another question about AT24C256 which I haven't really understand is it's page write function.
I know that AT24C256 have a page size of 64 bytes and writing a page is as fast as writing a single byte,
so I was thinking to page write each mifare card block (16 bytes) to make writes faster.

But what will happen to the other 48 bytes in same page?

If I wrote a for loop like this (assuming there are 512 pages):

for(int pageAddr=0 ; pageAddr < 512; pageAddr++){

   write_page(16_bytes_to_write, pageAddr);

}

Will it just write those data to first 16 bytes of every pages and waste 48x512 bytes, or use the spaces "smartly"?

WriteCardToEEP.ino (3.14 KB)

TestCard.txt (1 KB)

attachments are my codes and example card dump, hope some one can help me with this.

Many people, including me, will not download a .zip file.

Post the code inline, or attach your code as a .ino

If your data breaks nicely into 16 byte chunks, then that page boundary issue is easy to manage. Just write the chunk and increment the address by 16 each time. The important part is to not do a block write across a page boundary.

Here's an example program showing the 16 byte block writes.

//possible page boundary issue unless all writes at 16 bytes
#include <Wire.h>
#define ADDRESS 0x50 //Address of EEPROM

unsigned int memAddress = 0;//starting eeprom address
int totalBytes = 720;//total byte number multiple of 16
const byte blockLength = 16; //will avoid page boundary in eeprom
const byte numberOfBlocks = totalBytes/blockLength;

//test data visually to confirm pattern
byte fileData[][blockLength] =
{
  {0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0},
};

void setup() {
  Serial.begin(115200);
  Wire.begin();

  for (int i = 0; i <= numberOfBlocks - 1; i++)
  {
    writeBytes( ADDRESS, memAddress, fileData[i], blockLength);
    memAddress += blockLength;
  }

  memAddress = 0;//reset address for read

  for (int i = 0; i <= numberOfBlocks - 1; i++)
  {
    readBytes(ADDRESS, memAddress, blockLength);
    memAddress += blockLength;
  }
}

void loop() {}

void writeBytes(int device, unsigned int Address, byte* data, byte len)
{
  Wire.beginTransmission(device);
  Wire.write(Address >> 8 ); //MSB
  Wire.write(Address & 0xFF); // LSB
  Wire.write(data, len);
  Wire.endTransmission();
  delay(5);//small delay for eeprom to save data
}

void readBytes(int device, unsigned int Address, byte len )
{
  byte readBuffer[len];
  Wire.beginTransmission(device); // I2C address
  Wire.write(Address >> 8); // bit shift for high byte of pointer address
  Wire.write(Address & 0xFF); // mask for the low byte
  Wire.endTransmission();
  Wire.requestFrom(device, len);
  Wire.readBytes(readBuffer, len);
  for (byte j = 0; j < 16; j++)
  {
    Serial.print(readBuffer[j], HEX);
    Serial.print(" ");
  }
  Serial.println();
}

@cattledog

Thank you for your kind reply and example code! I re-uploaded my codes as request. But the card dump is a actually a hex file instead of a txt, I had to change that to upload successfully.

Below is my python code:

import binascii
import os
import serial

ser = serial.Serial('/dev/ttyUSB0',9600, timeout=1)
recv = -1
counter = 0
totalcount = 0


with open('TestCard.txt', 'rb') as f:
	hexdata = binascii.hexlify(f.read())
	hexlist = map(''.join, zip(*[iter(hexdata)]*2))

f.close

while(1):
	

	#Wait for arduino to ask for address
	if(ser.readline()=="address\n"): 
		ser.write("0")
		print "written 0 \n" #Give arduino address I wanted to write
		break
	
for a in hexlist:

	#Check if arduino is good to revcieve data
	while(ser.readline()!="OK\n"):
		ser.write("sendreq\n")

	#Send data
	if(counter != 16 ):
		ser.write(a.encode())
		print "written " + a.encode()
		counter = counter + 1
		totalcount = totalcount + 1
	else:
		while(ser.readline()!="Next Block\n" and ser.readline()!="Done\n"):
			continue
	counter = 0


ser.write("EndOfData\n")
ser.close()

and below is my sketch:

#include <Wire.h>
#include <SoftwareSerial.h>

byte buff[16];
int addr=-1;
int count=0;
int readed=-1;
byte recv;
bool ended = false;


void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
    int rdata = data;
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.write(rdata);
    Wire.endTransmission();
    Serial.write("done");
}

// WARNING: address is a page address, 6-bit end will wrap around
// also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes
void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddresspage >> 8)); // MSB
    Wire.write((int)(eeaddresspage & 0xFF)); // LSB
    byte c;
    for ( c = 0; c < length; c++)
        Wire.write(data[c]);
    Wire.endTransmission();
    
}

byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
    byte rdata = 0xFF;
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,1);
    if (Wire.available()) rdata = Wire.read();
    return rdata;
}

// maybe let's not read more than 30 or 32 bytes at a time!
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,length);
    int c = 0;
    for ( c = 0; c < length; c++ )
        if (Wire.available()) buffer[c] = Wire.read();
}



void setup() {
  Wire.begin();
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:

      if(ended == true){
        delay(1000000);
      }

      
      while(addr==-1&& ended != true){
        Serial.print("address\n");
        addr = Serial.read();
        delay(500);
      }

      

      while(1){
            if(Serial.read()!="sendreq\n"){
              Serial.write("OK\n");
            }else if(Serial.read()!="EndOfData\n"){
               ended = true;
            }
            delay(50);
      }
      
      while(readed == -1 && ended != true){
          readed = Serial.read();
          recv = readed;
          delay(50);
          
          
      }
      readed = -1;

      
      /*Serial.print(recv < 0x10 ? " 0" : " ");
      Serial.print(recv,HEX);
      Serial.println();*/

      if(recv!=-1 && ended != true){
        buff[count] = recv;
        count++;
      }
      

      if(count == 16 && ended != true){
          for(int i=0 ; i<16 ; i++){
              addr += i;
              //i2c_eeprom_read_byte(0x50,addr)!= buff[i] ? i2c_eeprom_write_byte(0x50,addr,buff[i]) : delay(1);
              delay(15);
        }
          //i2c_eeprom_write_page(0x50, addr, buff, sizeof(buff));
          //delay(10);
          count = 0;
          Serial.write("Next Block\n");
      }

      
        

      
        
   
}

Let's break you problem into three pieces.
Python sending block of 16 bytes
Arduino reading blocks of 16 bytes
Arduino writing blocks of 16 bytes to an external eeprom

I am not at all familiar with Python and can not comment on that code at all.

There are some things in your Arduino code that don't look correct to me.

I strongly recommend that you look at this tutorial by Robin2 on techniques for receiving serial data.
http://forum.arduino.cc/index.php?topic=396450

I think that example 6 which is binary data with start and end markers is particularly relevant.

I am certain that if you can get a block of 16 bytes into a buffer on the Arduino, the code I posted will get it correctly into the eeprom without page issues. The only issue is managing the addresses and saving a last known address (perhaps in the onboard eeprom) if all the data is not transferred in one go and the Arduino loses power.

After some research on the information @cattledog gave me, I think I found what I did wrong.

Serial.read() reads only one byte at a time. So the control condition in my sketch

while(1){
            if(Serial.read()!="sendreq\n"){
              Serial.write("OK\n");
            }else if(Serial.read()!="EndOfData\n"){
               ended = true;
            }
            delay(50);
      }

just won't work as I expected.

I'm trying to fix this now, hope it turns well !

After some hard work to fix it, I thought I got everything right but still not working. Maybe even worse.
The serial transmission now stops randomly before the python script can even finish running :confused:

I guess I am doing something really wrong with those serial transmissions but I just can't figure out where went wrong......

Python script:

import binascii
import os
import serial
import time

ser = serial.Serial('/dev/ttyUSB0',9600)
recv = -1
counter = 0
totalcount = 0



with open('TestCard.txt', 'rb') as f:
    hexdata = binascii.hexlify(f.read())
    hexlist = map(''.join, zip(*[iter(hexdata)]*2))

f.close

while(1):


    #Wait for arduino to ask for address
    if(ser.readline()=="address\n"): 
        ser.write("0")
        print "written 0 \n" #Give arduino address I wanted to write
        break

for a in hexlist:

    ser.write("srq\n")
    #Check if arduino is good to revcieve data

    while(ser.readline() != "OK\n"):
        if(ser.readline() == "write\n"):
            print "write\n"
        ser.write("srq\n")
        print "srq\n"

    while(ser.readline() != "read\n"):
        if(ser.readline() == "read\n"):
            print "sok\n"
            ser.write("SOK\n")
            break

    print "sok\n"       
    ser.write("SOK\n")
    #Send data

    ser.write(a.encode())
    print "written " + a.encode()
    counter = counter + 1
    totalcount = totalcount + 1

    #else:
        #while(ser.readline()!="Next Block\n" and ser.readline()!="Done\n"):
            #continue
    if(counter == 16 ):
        print "\n\n16 bytes\n\n" 
        counter = 0


ser.write("EOF\n")
print "\nEOF\n"
ser.close()

Arduino sketch:

#include <Wire.h>
#include <SoftwareSerial.h>

byte buff[16];
int addr=-1;
int count=0;
char readed[5];
char srq[4] = "srq\n";
char eof[4] = "EOF\n";
char sok[4] = "SOK\n";
byte recv;
bool ended = false;


void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
    int rdata = data;
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.write(rdata);
    Wire.endTransmission();
}

// WARNING: address is a page address, 6-bit end will wrap around
// also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes
void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddresspage >> 8)); // MSB
    Wire.write((int)(eeaddresspage & 0xFF)); // LSB
    byte c;
    for ( c = 0; c < length; c++)
        Wire.write(data[c]);
    Wire.endTransmission();

}

byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
    byte rdata = 0xFF;
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,1);
    if (Wire.available()) rdata = Wire.read();
    return rdata;
}

// maybe let's not read more than 30 or 32 bytes at a time!
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,length);
    int c = 0;
    for ( c = 0; c < length; c++ )
        if (Wire.available()) buffer[c] = Wire.read();
}

void LOL(char* arr)
{
  strcpy(arr, "LOL\n");
}  

bool checksrq(){
  bool same = false;
  for(int i=0 ; i<4; i++){
        readed[i] == srq[i] ? same = true : same = false;
    }

   return same;
}

bool checkEOF(){
  bool same = false;
  for(int i=0 ; i<4; i++){
        readed[i] == eof[i] ? same = true : same = false;
    }

   return same;
}

bool checkSOK(){
  bool same = false;
  for(int i=0 ; i<4; i++){
        readed[i] == sok[i] ? same = true : same = false;
    }

   return same;
}

void setup() {
  Wire.begin();
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:

      if(ended == true){
        delay(1000000);
      }

      while(addr==-1&& ended != true){
        Serial.print("address\n");
        addr = Serial.read();
        delay(500);
      }

      while(1){

          for(int i=0 ; i<4; i++){

            readed[i] = Serial.read();

          }

            if(checksrq()){
              delay(5);
              Serial.write("OK\n");
              LOL(readed);
              break;
            }else if(checkEOF()){
               ended = true;
            }
            LOL(readed);
            delay(1);
      }

      Serial.write("read\n");
      while(checkSOK == false){
        delay(1);
        Serial.write("read\n");
      }
      for(int i=0; i<16 ; i++){
        buff[i] = Serial.read();
        count++;
      }

      LOL(readed);

      if(count == 16 && ended != true){
          for(int i=0 ; i<16 ; i++){

              addr += i;
              //i2c_eeprom_read_byte(0x50,addr)!= buff[i] ? i2c_eeprom_write_byte(0x50,addr,buff[i]) :     delay(1);
              delay(5);
              Serial.write("write\n");

        }
          //i2c_eeprom_write_page(0x50, addr, buff, sizeof(buff));
          //delay(10);
          count = 0;
          Serial.write("Next Block\n");
      }
}

Here's a few thoughts on how to simplify things.

  1. Let the arduino manage the eeprom addresses, and eliminate the sending an receiving of addr.

  2. Can you get for the python sketch to send a single character start and end marker instead of the strings? Are there any bytes which can not be data?

  3. Code like this can try to read before the bytes are available to read. You can easily read faster than data arrives.

for(int i=0 ; i<4; i++){

            readed[i] = Serial.read();

          }
  1. Serial.available() will return the number of bytes in the read buffer, but you do not use this. You might be able to use blocking code which waits for bytes to be available.

  2. Take a look at Robin 2's code on python to Arduino. Demo of PC-Arduino comms using Python - #5 by Robin2 - Interfacing w/ Software on the Computer - Arduino Forum

In particular see ComArduino2.py and ArduinoPC2.ino. He receives data using the methods of the Serial tutorial.