How to address an eeprom

I know this is about eeproms but it is a software question.
I am using the AT24c256 library and the example that comes with it.

/**
 * This sketch shows examples on how to use all the features of this library
 * 
 * It can also be used as a test  to verify that you have your eprom configured
 * propery to your Arduino as it prints out the results so you can see if everything works
 */

#include <at24c256.h>

// Create a eprom object configured at address 0
// Sketch assumes that there is an eprom present at this address
AT24C256 eprom(AT24C_ADDRESS_0);
// Create another eprom object configured att address 2
// Sketch assumes that there is NO eprom present at this address
AT24C256 badEprom(AT24C_ADDRESS_2);

void setup() {
  Serial.begin(115200);
  Serial.println("Starting up");

  // Initialize the i2c library
  Wire.begin();

  /** Write and read an integer */
  int foo = 42;
  // Write the integer foo to the eprom starting at address 0
  
  eprom.put(0, foo);
  int foo_in;
  // Read the integer foo_in from eprom starting at address 0
  eprom.get(0, foo_in);
  Serial.println(foo_in);

  /** Write and read a double */
  double pi = 3.141593;
  // Write the double pi to the eprom starting at address 0
  eprom.put(0, pi);
  double pi_in;
  // Read the double pi_in from eprom starting at address 0
  eprom.get(0, pi_in);
  // Create a buffer and convert pi_in to a string to be able to print it
  char buffer[10];
  dtostrf(pi_in, 9, 6, buffer); // Converts a double to a string
  Serial.println(buffer);

  /** Write and read a struct */
  // Declare the struct "Point"
  struct Point {
    int x;
    int y;
  };
  Point point = {17, 42};
  // Write the struct point to the eprom starting at address 0
  eprom.put(0, point);
  Point point_in;
  // Read the struct point_in from eprom starting at address 0
  eprom.get(0, point_in);
  Serial.println(point_in.x);
  Serial.println(point_in.y);

  /** Write and read a single byte */
  // Write the value 77 to the eprom byte at address 0
  eprom.write(0, 77);
  // Read the value of the byte at address 0
  int value = eprom.read(0);
  Serial.println(value);

  /** Write and read a byte buffer */
  uint8_t out[15] = "Test of buffer";
  // Write the 15 bytes long buffer "out" to eprom starting at address 0
  eprom.writeBuffer(0, out, 15);
  uint8_t in[15];
  // Read 15 bytes from eprom starting at address 0 into the buffer "in".
  eprom.readBuffer(0, in, 15);
  Serial.println((char*)in);

  /** Error handling on write, no error */
  // Read a byte from address 0, this should not result in an error
  eprom.write(0, 77);
  // Get the last error code, it should be 0 since there was no error
  int lastError = eprom.getLastError();
  Serial.print("last status on write: ");
  Serial.println(lastError);

  /** Error handling on write, using a eprom address without eprom */
  // Write the value 77 to an eprom that is not connected - this will fail
  badEprom.write(0, 77);
  // Get the last error code, it should not be zero, but 2 which means that there were no response from the eprom
  int lastError2 = badEprom.getLastError();
  Serial.print("last error on write: ");
  Serial.println(lastError2);  

  /** Error handling on read, using a eprom address without eprom */
  // Read the value from address 0 from an eprom that is not connected (and print it)
  badEprom.read(0);
  // Get the last error code, it should not be zero, but 2 which means that there were no response from the eprom
  int lastError3 = badEprom.getLastError();
  Serial.print("last error on read: ");
  Serial.println(lastError3);  

  // Read the size of the eprom and print it
  Serial.println(eprom.length());

  /** Write and read a long byte buffer, >32 which is TwoWire's internal buffer size and 
    * >64 which is the at24c256 page size) */
  uint8_t out2[80] = "Writing a really long message, testing some of several buffer limits on the way";
  // Write the long buffer to the eprom starting at address 0 and check how many bytes were actually written
  int written = eprom.writeBuffer(0, out2, 80);
  Serial.print(written);
  Serial.print(" bytes written, last error on write: ");
  // Get the last error and print it
  Serial.println(eprom.getLastError());
  uint8_t in2[80];
  // Read 80 bytes from eprom starting at address 0 and store it in the in2 buffer. Check how many bytes were actually read
  int readBytes = eprom.readBuffer(0, in2, 80);
  Serial.print(readBytes);
  Serial.print(" bytes read, last error on read: ");
  // Get the last error and print it
  Serial.println(eprom.getLastError()); 
  in2[79] = '\0';
  Serial.println((char*)in2);
}

// This test program has no loop, it just runs once
void loop() {
}

I have cut it down to

/**
 * This sketch shows examples on how to use all the features of this library
 * 
 * It can also be used as a test  to verify that you have your eprom configured
 * propery to your Arduino as it prints out the results so you can see if everything works
 */

#include <at24c256.h>

// Create a eprom object configured at address 0
// Sketch assumes that there is an eprom present at this address
AT24C256 eprom(AT24C_ADDRESS_0);
// Create another eprom object configured att address 2
// Sketch assumes that there is NO eprom present at this address
AT24C256 badEprom(AT24C_ADDRESS_2);

void setup() {
  Serial.begin(115200);
  Serial.println("Starting up");

  // Initialize the i2c library
  Wire.begin();
  /** Write and read a byte buffer */
  uint8_t outa[15] = "Test of buffer";
  // Write the 15 bytes long buffer "out" to eprom starting at address 0
  eprom.writeBuffer(0, outa, 15);
  uint8_t outb[15] = "Tess of buffer";
  // Write the 15 bytes long buffer "out" to eprom starting at address 0
  eprom.writeBuffer(8, outb, 15);
 
  // Read 15 bytes from eprom starting at address 0 into the buffer "in".
   uint8_t ina[15];
  eprom.readBuffer(0, ina, 15);
  Serial.println((char*)ina);
   uint8_t inb[15];
  eprom.readBuffer(8, inb, 15);
  Serial.println((char*)inb);
}

// This test program has no loop, it just runs once
void loop() {
}

This should now just load the line
'Test of Buffer'
into address 0 and
'Tess of buffer'
into address 8
I thought I would get
'Test of Tess of
Tess of buffer'
since the second one over writes the first but I get
'Test of Tess of buffer
Tess of buffer'
Should it not just give me the first 15 letters on the first line since I am only asking for ina to read the first 15 addresses? why does it give me 24?

I suspect the following:

Because you partially overwrite the first text in the EEPROM, ina will be no longer contain a '\0'.

The compiler is free to re-organise your code and might do

uint8_t ina[15];
uint8_t inb[15];
eprom.readBuffer(0, ina, 15);
eprom.readBuffer(8, inb, 15);
Serial.println((char*)ina);
Serial.println((char*)inb);

So now in your RAM you will have read both and the are consecutive.
Printing ina will not find a terminating '\0' till it finds the '\0' at the end of inb and hence it prints what you see for ina.

3 Likes

You wrote 15 bytes at address zero, then 15 bytes at address 8, which woukd overwrite the last 7 characters of the first thing you wrote.

I think.

a7

I don't quite understand the existence of the /0? Are you saying sending the information into the eeprom adds a /0 that the read command looks for?
I thought eprom.readbuffer(0, ina, 15); would just read the 15 bits starting at bit 0 into a variable called ina and then print it?
In fact in my attempts to find the problem I commented the second half (writing and reading to inb) and I got just Test of Tess of
which I expected (the first half of the first message and the first half of the second message which was already in there from before. When I put the second half back in (the writing and reading from inb), I got
Test of Tess of
Tess of buffer
which I expected? I don't know why I was getting 24 bits before but I can't reproduce it?
I seed to have solved the problem but I don't know how?

The \0 is added to the end of the string when it is defined so does not need to be added to what is written. It is the end of string marker and is what functions such as Serial.print() look for when printing a string so they know when they have reached the end of the string when reading it from memory

OK but if I ask it to read 15 bits , surely it should give me 15 bits?
I found what I had done to clear the problem, I changed
uint8_t ina[15]; to
uint8_t ina[8];
when I changed it back the problem returned.
If I change write and read of inb and outb to 16, inb, 15
i.e. don't overwrite the first write then the reads both work correctly so it looks as if the problem is that the reads are reading up to the /0 and ignoring the fact that I am only asking it to read 15 bits each time? Does that sound right?

bits vs bytes explained

1 Like
  uint8_t outa[15] = "Test of buffer";
  eprom.writeBuffer(0, outa, 15);

The \0 is at address 14 in the EEPROM
Then you do this

  uint8_t outb[15] = "Tess of buffer";
  // Write the 15 bytes long buffer "out" to eprom starting at address 0
  eprom.writeBuffer(8, outb, 15);

The comment is wrong. You are not writing to address 0 but address 8
You have now overwritten the \0 at address 14 but there is now a \0 at address 23

   uint8_t ina[15];
  eprom.readBuffer(0, ina, 15);
  Serial.println((char*)ina);

Then you read 15 bytes onto the array. None of them are \0 because you have overwritten it
When you print the array all characters read from memory will be printed until the next \0 is found, whether or not they are in the array

C++ is quite happy to let you read and write outside of array bounds. It is up to you to stop it happening

2 Likes

I keep getting 0x16. Should I worry? :expressionless:


You can do it and C++ in return can basically do whatever it feels like. Accessing array elements out of bounds invokes undefined behaviour, about which I know enough to avoid (or suspect).

a7

Not quite. Why did you make outa 15 bytes and not 14 bytes?

You're use so-called c-strings; the text is stored including a terminating '\0' (not '/0') and hence you need one more byte storage space in the outa array (as you did).

The below code demonstrates what it looks like in memory

void setup()
{
  Serial.begin(115200);
  uint8_t outa[] = "Test of buffer";
  Serial.print(F("The size of outa is "));
  Serial.print(sizeof(outa));
  Serial.println(F(" bytes with content"));

  for (uint8_t cnt = 0; cnt < sizeof(outa); cnt++)
  {
    // print hex value of bytes
    if (outa[cnt] < 0x10) Serial.print("0");
    Serial.print(outa[cnt], HEX);
    // print byte as character
    Serial.print(F("("));
    Serial.write(outa[cnt]);
    Serial.print(F(")"));

    Serial.print(" ");
  }
  Serial.println();
}

void loop()
{
}

and the output is

image

The printout contains the hex value of the bytes and between () the character that the byte represents (see e.g. https://www.asciitable.com/). Note the 00 at the end which is the so-called null terminator ('\0'); the little square block between the () indicates that it's a non-printable character.

Note:
I used outa[] instead of outa[15] to be able to demonstrate the actual size of the array after assigning a text.


In the below I have simulated the EEPROM in RAM. It stores the text "Test of buffer" at location 0 and the text "!!Hello world!!" at location 8.

void setup()
{
  Serial.begin(115200);
  uint8_t outa[] = "Test of buffer";

  // simulated EEPROM
  uint8_t eeprom[30];
  // store outa in EEPROM
  memcpy(eeprom, outa, sizeof(outa));

  // print EEPROM content
  for (uint8_t cnt = 0; cnt < sizeof(eeprom); cnt++)
  {
    // print hex value of bytes
    if (eeprom[cnt] < 0x10) Serial.print("0");
    Serial.print(eeprom[cnt], HEX);
    // print byte as character
    Serial.print(F("("));
    Serial.write(eeprom[cnt]);
    Serial.print(F(")"));

    Serial.print(" ");
  }
  Serial.println();
  Serial.println("                                                                                    ^");


  // second text
  uint8_t outb[] = "!!Hello world!!";
  // store outb in EEPROM
  memcpy(&eeprom[8], outb, sizeof(outb));

  // print EEPROM content
  for (uint8_t cnt = 0; cnt < sizeof(eeprom); cnt++)
  {
    // print hex value of bytes
    if (eeprom[cnt] < 0x10) Serial.print("0");
    Serial.print(eeprom[cnt], HEX);
    // print byte as character
    Serial.print(F("("));
    Serial.write(eeprom[cnt]);
    Serial.print(F(")"));

    Serial.print(" ");
  }
  Serial.println();
  Serial.println("                                                                                                                                          ^");
}

void loop()
{
}

The variable eeprom represents an EEPROM with 30 bytes storage space; the 30 was chosen so it would fit on the screen in serial monitor on my computer and is big enough to hold the combined text in the way you stored the two texts. The ^ indicates the location of the terminating '\0''.

The output will look like

image


You can apply a similar approach for your EEPROM. Write you first text to EEPROM address 0, read 30 bytes back from address 0 into a buffer of 30 bytes and print the 30 bytes. Next write the second text to EEPROM at address 8, read 30 bytes back from address 0 into a buffer of 30 bytes and print the 30 bytes.

If you did not clear the EEPROM first, after the write of the first text you will still find remains of the second text after the first print.

PS
I had to use screenshots of serial monitor because the little square block was dropped when pasting it to the forum

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.