External EEPROM fails to write String from Serial with put function

Hello,

I have wrote this program to read and write from/to an external EEPROM connected via I2C.
If I used the examples to write a String with put function everything works good.
Now I’m trying to write a string that start in a specific address with the Serial.
The format of the string will be for example: 0|Hello. In this way I say to put the string Hello starting from the 0 address of the EEPROM. The problem is that using the serial the output is invalid. Someone could help me to understand why? The write function is in the last else at bottom.

Thanks.

This is the code:

#include <SparkFun_External_EEPROM.h>
#include <Wire.h>

ExternalEEPROM myMem;

unsigned int addressMem = 0;
String inputString = ""; 

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

  if (myMem.begin() == false)
  {
    Serial.println("No memory detected. Freezing.");
    while (1)
      ;
  }  
}

void loop() {}

void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    Serial.println(inputString);
    
    //TODO- EVITARE CHE SE LA STRINGA CONTIENE IL DELIMITATORE SI BUGGA
    if(inChar == '|'){
      addressMem = inputString.toInt();
      Serial.println("IN | IF");
       Serial.println(inputString);
       Serial.println(addressMem);
      inputString = "";
    }

    // the incoming character is a newline
    if (inChar == '\n') {
      Serial.println("Return char");
        if(inputString == "EEPROM_R\n"){
         
        for (int k=0;k<112;k++){
          char myRead2[20];
           myMem.get(addressMem, myRead2); //location to read, thing to put data into
            Serial.println("Patch: "+String(k)+
                            " Value: "+ myRead2);
            addressMem += 20;
            delay(5);
         }
             Serial.print("STOP");
             addressMem = 0;
        } else {
            Serial.println("Address: " + (String)addressMem + " Patch: " + inputString);
            myMem.put(addressMem, inputString);
            addressMem = 0;
            delay(5);
        }
        Serial.println(inputString);
        // clear the string:
        inputString = "";
      }
  }
}

If I used the examples to write a String with put function everything works good.

I don't think this is true. Did you cycle power and then attempt to read before you decided that "everything works good"

A String object is a pointer to a memory location. You are not saving what is actually held in memory but rather the pointer address. You need to use .put() and .get() with the underlying character array.

Thanks for your reply.
Yes, sorry. You have right. I have done so many attemps that I got confused.
I’ve resolved copying the string into a char array but now I’ve another issue (if required, I can create another thread).
When I send a lot of data (I’m using a windows application written by my self in C#) (14 string of max 20 char) sometimes some char missed. I’ve read that I need to implement a protocol that check the integrity of the data, and if required, ask again to my application to send the data but I’ve no idea where to start. Could you suggest me some documentation or ready to use library?

This is the code to send data with my C# Application

 private void sendBtn_Click(object sender, EventArgs e)
        {
           
 TextBox[] myPatch = { patch1, patch2, patch3, patch4, patch5, patch6, 
patch7, patch8, patch9, patch10, patch11, patch12, patch13, patch14 };
            int bankIndex = sendBankComboBox.SelectedIndex;
            int startAddress = 0 + (bankIndex * 280);
            mySerialPort.PortName = cbPorts.SelectedItem.ToString();
            mySerialPort.BaudRate = baudrate;
            mySerialPort.Open();
            int index = 0;
            foreach (TextBox patch in myPatch)
            {
                if (String.IsNullOrWhiteSpace(patch.Text))
                {
                    Debug.WriteLine("if");
                    Debug.WriteLine(bankIndex);
                    Debug.WriteLine(startAddress);
                    Debug.WriteLine(startAddress + "|" + "EMPTY");
                    patchName[bankIndex, index] = "EMPTY";
                    mySerialPort.WriteLine(startAddress + "|" + "EMPTY");
                } else
                {
                    Debug.WriteLine("else");
                    Debug.WriteLine(bankIndex);
                    Debug.WriteLine(startAddress);
                    Debug.WriteLine(patch.Text);
                    patchName[bankIndex, index] = patch.Text;
                    mySerialPort.WriteLine(startAddress + "|" + patch.Text);
                }
                index++;
                startAddress += 20;
            }

            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 14; j++)
                {
                    Debug.WriteLine(patchName[i, j]);
                }
            }

            mySerialPort.Close();

            Debug.WriteLine("Sent");
        }

Thanks.

I've read that I need to implement a protocol that check the integrity of the data, and if required, ask again to my application to send the data but I've no idea where to start. Could you suggest me some documentation or ready to use library?

Yes, a simple 8 bit check sum based the sum of all bytes (%256) or on the XOR(^) of all the bytes in the message could be easily used.

For more robustness (and security if required) a CRC procedure could be used. There are 16 bit and 8 bit crc routines built into the AVR LIBC which is used by the Arduino software. https://www.nongnu.org/avr-libc/user-manual/group__util__crc.html#ga95371c87f25b0a2497d9cba13190847f

I would also advise that you avoid using the String object on the Arduino. They can create memory issues and unpredictable behaviour of the program. See The Evils of Arduino Strings

A good reference with robust techniques receiving Serial data without is here Serial Input Basics

Thanks a lot for the links! (I've already read some of them :D).

Sorry, if I continue to ask things but I'm doing some tests and I've check that the problem is always with the same addresses. I try to explain what I want to do. I need to write 14 string for 8 files. Each string have max 20 char. When I read from EEPROM I read all the banks (so 14*8 strings) and everythings is ok. When I want to write, I write just in 1 bank (so 14 strings) and I have problem always with the same strings. Moreover if I send short char array (2 char) everything works fine. So maybe instead of data loss, I'm doing something wrong with the addresses. Could you check the code above to see if my code do what I want. I have read something about page in the EEPROM but if I have understood well, with put() function this problem should not appear.

I have notice that the error is shown every 64 char so maybe it’s a page problem. This is the output of the serial:

Patch: 0 Value: AAAAAAAAAAAAAAAAAA
Patch: 1 Value: AAAAAAAAAAAAAAAAAA
Patch: 2 Value: AAAAAAAAAAAAAAAAAA
Patch: 3 Value: AAAA????????????????'
Patch: 4 Value: AAAAAAAAAAAAAAAAAA
Patch: 5 Value: AAAAAAAAAAAAAAAAAA
Patch: 6 Value: AAAAAAAA????????????'

The offset among the patches is 20. So The bug appear after the 64th char. If the string is < 4 the patch3 works correctly. So what do you think? :slight_smile:

The SparkFun library is supposed to take care of the page boundaries and block writes across them. Indeed the default page size is 64 bytes. What eeprom are you using, and what is the specified page size.

I think that one way you can test this is to manage all your writes of 20 characters to begin at addresses which begin at 0 and then are multiples of 32 bytes apart. That way, you will never be writing across a page boundary.

Organize the eeproms writes to be at address 0,32,64,96,etc.

I have used the write function and the problem disappear but now I have some lag so some char miss. So, as you said I've modified the len of the string from 20 to 32 to avoid the page problem and everything works fine:

Patch: 0 Value: EMPTY
Patch: 1 Value: EMPTY
Patch: 2 Value: EMPTY
Patch: 3 Value: EMPTY
Patch: 4 Value: EMPTY
Patch: 5 Value: EMPTY
Patch: 6 Value: EMPTY
Patch: 7 Value: EMPTY
Patch: 8 Value: EMPTY
Patch: 9 Value: EMPTY
Patch: 10 Value: EMPTY
Patch: 11 Value: EMPTY
Patch: 12 Value: EMPTY
Patch: 13 Value: EMPTY

The EEPROM is this:

https://www.amazon.co.uk/gp/product/B07XTBXGXW/ref=ppx_yo_dt_b_asin_title_o06_s00?ie=UTF8&psc=1

They used the same IC that Sparkfun sells so I think that is not the IC the problem.

There are also no char missed even with 14 strings of 20 char! The data are not important so I don't need to encrypt them but even if everything works fine I will try to learn about the parity or checksum and implement a check of data inconsistency.

Thanks a lot for your help, I hope to not have other issues! Do you think that it is worth it to open an issue on the Github page of the library?

Sounds like things are going well. Can you please post your latest Arduino code.

I will study the Spark Fun library to see how it handles the page boundaries with the use of .put() and .get() as well as the block writes. I have always used J. Christensen's extEEPROM.h but I don't think is expressly supported .put() and .get() but more the block writes from byte cast data.

Sure:

#include <SparkFun_External_EEPROM.h>
#include <Wire.h>

ExternalEEPROM myMem;

unsigned int addressMem = 0;
//Cercare di usare un array di char
String inputString = "";

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

  if (myMem.begin() == false)
  {
    Serial.println("No memory detected. Freezing.");
    while (1)
      ;
  }
}

void loop() {}

void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    //Serial.println(inputString);

    //TODO- EVITARE CHE SE LA STRINGA CONTIENE IL DELIMITATORE SI BUGGA
    if (inChar == '|') {
      addressMem = inputString.toInt();
      //Serial.println("IN | IF");
      //Serial.println(inputString);
      //Serial.println(addressMem);
      inputString = "";
    }

    // the incoming character is a newline
    if (inChar == '\n') {
      //Serial.println("Return char");
      if (inputString == "EEPROM_R\n") {

        for (int k = 0; k < 112; k++) {
          char myRead2[20];
          myMem.get(addressMem, myRead2); //location to read, thing to put data into
          Serial.println("Patch: " + String(k) +
                         " Value: " + myRead2);
          addressMem += 32;
          delay(5);
        }
        Serial.print("STOP");
        addressMem = 0;
      } else if (inputString == "EEPROM_Rx\n") {
        for (int k = addressMem; k < 14; k++) {
          char myRead2[32];
          myMem.get(addressMem, myRead2); //location to read, thing to put data into
          Serial.println("Patch: " + String(k) +
                         " Value: " + myRead2);
          addressMem += 32;
          delay(5);
        }
        Serial.print("STOP");
        addressMem = 0;
      } else {
        //Serial.println("Address: " + (String)addressMem + " Patch: " + inputString);
        char inputCharArray[20];
        inputString.toCharArray(inputCharArray, 20);
        inputCharArray[inputString.length() - 1] = '\0';
        myMem.put(addressMem, inputCharArray);
        addressMem = 0;
        delay(10);
      }
      //Serial.println(inputString);
      // clear the string:
      inputString = "";
    }
  }
}

I just need to use char array instead of String for inputString var.
If you found something wrong with the library, let me know. I’m curious :smiley:

Thanks again for your support!

I have looked closer at the Spark Fun library, and it does not support block writing across page boundaries.

I was mislead by this statement in the library documentation

All read and write restrictions associated with pages are taken care of. You can access the external memory as if it was contiguous.

But the actual code just prevents writing across the boundary. This is useful in that it prevents wrap around and overwriting of data on the page but it was not what I expected.

Other libraries I have used, do indeed manage seamless block writes across boundaries. Indeed, one of the main reasons to use a library instead of more basic i2c code is to manage the page boundaries.

There is actually a closed issue on the library GitHub page about the page boundary issue.