I2C_eeprom and saving struct

I'm a strong believer in books.

A start can be the old C bible: Kernighan and Ritchie's The C Programming Language 2nd edition

It's only about C, not C++; more than enough to learn the basics. I can't advise on C++ as I don't have anything myself. I have a few more books in my library but all of them are old.

I have a 1987 version of C Primer Plus by Waite, Prata and Martin

For the below, I could not quickly find a link. You can look for them on the second hand market or PDFs on the web Advanced C: techniques and applications by G.E. Sobelman (dutch title "C voor gevorderden") C programmer's library by J.J. Purdun (dutch title "C programma bibliotheek")

All are pure C.

Hi

Tanks for the reply...

Any thing to be aware of ? I've read the eeprom have pages. Do this read/write functions handle this ?

Anything to pay attension to in the read/write-procedures I've used ? Do they handle the bank/pages in the memorychip ? or ?....

Sincerly

Maybe this will help.

Hi Nick

Thanks. I’ll give it some attension…

Sincerly
Steen

Hi guys, I too was looking for a way to store a struct in an 24LC256 and this thread really helped a lot. :slight_smile:

I have just one question.

Can you think of a practical way of going around the ~30 byte limitation of the I2C buffer?
My structures take up a lot more space than that and I can’t think of a way to do this.

I’m referring to this function:

// 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]);
      delay(5); //add a small delay
    }
  Wire.endTransmission();
}

Thanks in advance for any help you can provide.

Stonefire: Where do I find some kind of a tutorial for newbies about this ?

Beginning C for Arduino is a true beginner's book and has two chapters on pointers that might help you. It also has a "very light" introduction to C++ designed so you can understand how most Arduino libraries are written. (Make sure it's the 2nd edition.) Amazon carries it and you can click on the "Look Inside" banner to examine the Table of Contents.

Dimdim: ... ... Can you think of a practical way of going around the ~30 byte limitation of the I2C buffer? My structures take up a lot more space than that and I can't think of a way to do this.

I'm referring to this function: ...

No idea where that function comes from (some library I assume); post a link to the library please. I would think that a library would present a function to the user that hides the limitation (e.g. void i2c_eeprom_write(....)) but that might not be the case.

I think this would be my approach if there is a limitation. If 'ptr' is a pointer to a byte in memory, ptr+30 is a pointer to a byte in memory 30 bytes further. So you can write the first 30 of a struct by using ptr (and write 30 bytes), the second 30 bytes of the struct by using ptr+30 (and write 30 bytes), the third 30 bytes by using ptr+60 etc. You can use a loop (for, while) to write 30 bytes at a time till the remainder is less than 30 and next do a final write to write the remaining bytes.

PS: I'm not familiar with pages in eeproms so the approach might not be correct.

Can you think of a practical way of going around the ~30 byte limitation of the I2C buffer?
My structures take up a lot more space than that and I can’t think of a way to do this.

This is how Jack Christensen handles the issue of pages and wire library buffer length in his excellent library extEEPROM.h which is available through the library manager.

byte extEEPROM::write(unsigned long addr, byte *values, unsigned int nBytes)
{
    uint8_t ctrlByte;       //control byte (I2C device address & chip/block select bits)
    uint8_t txStatus = 0;   //transmit status
    uint16_t nWrite;        //number of bytes to write
    uint16_t nPage;         //number of bytes remaining on current page, starting at addr

    if (addr + nBytes > _totalCapacity) {   //will this write go past the top of the EEPROM?
        return EEPROM_ADDR_ERR;             //yes, tell the caller
    }

    while (nBytes > 0) {
        nPage = _pageSize - ( addr & (_pageSize - 1) );
        //find min(nBytes, nPage, BUFFER_LENGTH) -- BUFFER_LENGTH is defined in the Wire library.
        nWrite = nBytes < nPage ? nBytes : nPage;
        nWrite = BUFFER_LENGTH - _nAddrBytes < nWrite ? BUFFER_LENGTH - _nAddrBytes : nWrite;
        ctrlByte = _eepromAddr | (byte) (addr >> _csShift);
        Wire.beginTransmission(ctrlByte);
        if (_nAddrBytes == 2) Wire.write( (byte) (addr >> 8) );   //high addr byte
        Wire.write( (byte) addr );                                //low addr byte
        Wire.write(values, nWrite);
        txStatus = Wire.endTransmission();
        if (txStatus != 0) return txStatus;

        //wait up to 50ms for the write to complete
        for (uint8_t i=100; i; --i) {
            delayMicroseconds(500);                     //no point in waiting too fast
            Wire.beginTransmission(ctrlByte);
            if (_nAddrBytes == 2) Wire.write((byte)0);        //high addr byte
            Wire.write((byte)0);                              //low addr byte
            txStatus = Wire.endTransmission();
            if (txStatus == 0) break;
        }
        if (txStatus != 0) return txStatus;

        addr += nWrite;         //increment the EEPROM address
        values += nWrite;       //increment the input data pointer
        nBytes -= nWrite;       //decrement the number of bytes left to write
    }
    return txStatus;
}

I solved the problem by using the extEEPROM library. Great work by Jack Christensen.

Thank you guys for helping. :)

Hi,

I tried to write a below sketch using struct and union that worked perfectly and hope it may help others.

/*
 Purpose:-
    This is a trial program made to test the typedef union 
    with various data types to see if it works with 
    in data writing and reading from DS1307 or similar NVRAM 
 Author:-
    Pradip Khare
 
 Courtsey:-
    Respective owner of the libraries writers, who make our programming easier

 Results:-
    it works perfectly check with Nano and RTC chip DS1307
    Also, it is 50 times faster than writtign to EEPROM of hte processor 
    for the same data bytes written and read 
*/

#include <Wire.h>
#include "RTClib.h" // Courtsey Jeelabs- https://github.com/adafruit/RTClib


RTC_DS1307 rtc;   // i2c address 0x68

struct myStruct{
  boolean wasDataSaved;
  char  lastMode;
  float VlimitVal;
  float VsetVal;
  float AlimitVal;
  boolean wasOEnabled;
  int lastCurPos;
};

typedef union{
  myStruct inSide;
  byte bytes[17];
} UNION_t;

long lM1,lM2;
void setup() {
  Serial.begin(9600);
  rtc.begin();
  
  UNION_t oldSet;
  UNION_t newSet;
  
  oldSet.inSide.wasDataSaved  = false;
  oldSet.inSide.lastMode      = 'A'; 
  oldSet.inSide.VlimitVal     = 20.00;
  oldSet.inSide.VsetVal       = 10.00;
  oldSet.inSide.AlimitVal     =  0.50;
  oldSet.inSide.wasOEnabled   = true;
  oldSet.inSide.lastCurPos    = 3;

  // below values of dummies to check the difference after NVPROM read
  newSet.inSide.wasDataSaved  = true;
  newSet.inSide.lastMode      = 0; 
  newSet.inSide.VlimitVal     = 0.0;
  newSet.inSide.VsetVal       = 0.0;
  newSet.inSide.AlimitVal     = 0.0;
  newSet.inSide.wasOEnabled   = false;
  newSet.inSide.lastCurPos    = 0;

  Serial.print("\nSize oldSet=");Serial.print(sizeof(oldSet));
  Serial.print("\nOld Bytes"); 
  for(int i=0;i<17;i++) {  
    Serial.print(" ");Serial.print(oldSet.bytes[i]); // Print the hex representation of the float
  }
  lM1=millis();
  rtc.writenvram(0, oldSet.bytes, 17);
  lM2=millis();
  Serial.print("\nTook ");Serial.print(lM1-lM2);Serial.print(" milliSeconds to Write.");
  Serial.print("\n\nBefore Read:-");
  Serial.print("\nwasDataSaved="); Serial.print(newSet.inSide.wasDataSaved);
  Serial.print("\nlastMode    ="); Serial.print(newSet.inSide.lastMode);
  Serial.print("\nVlimitVal   ="); Serial.print(newSet.inSide.VlimitVal);
  Serial.print("\nVsetVal     ="); Serial.print(newSet.inSide.VsetVal);
  Serial.print("\nAlimitVal   ="); Serial.print(newSet.inSide.AlimitVal);
  Serial.print("\nwasOEnabled ="); Serial.print(newSet.inSide.wasOEnabled);
  Serial.print("\nlastCurPos  ="); Serial.print(newSet.inSide.lastCurPos);

  lM1=millis();
  rtc.readnvram (newSet.bytes,17, 0);
  lM2=millis();
  Serial.print("\nTook ");Serial.print(lM1-lM2);Serial.print(" milliSeconds to Read.");
  Serial.print("\nNew Bytes");
  for(int i=0;i<17;i++) {  
    Serial.print(" ");Serial.print(newSet.bytes[i]); // Print the hex representation of the float
  }
  Serial.print("\n\nFrom NVRAM:-");
  Serial.print("\nwasDataSaved="); Serial.print(newSet.inSide.wasDataSaved);
  Serial.print("\nlastMode    ="); Serial.print(newSet.inSide.lastMode);
  Serial.print("\nVlimitVal   ="); Serial.print(newSet.inSide.VlimitVal);
  Serial.print("\nVsetVal     ="); Serial.print(newSet.inSide.VsetVal);
  Serial.print("\nAlimitVal   ="); Serial.print(newSet.inSide.AlimitVal);
  Serial.print("\nwasOEnabled ="); Serial.print(newSet.inSide.wasOEnabled);
  Serial.print("\nlastCurPos  ="); Serial.print(newSet.inSide.lastCurPos);
}

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

Results are as follows:-

Size oldSet=17
Old Bytes 0 65 0 0 160 65 0 0 32 65 0 0 0 63 1 3 0
Took -2 milliSeconds to Write.

Before Read:-
wasDataSaved=1
lastMode =
VlimitVal =0.00
VsetVal =0.00
AlimitVal =0.00
wasOEnabled =0
lastCurPos =0
Took -2 milliSeconds to Read.
New Bytes 0 65 0 0 160 65 0 0 32 65 0 0 0 63 1 3 0

From NVRAM:-
wasDataSaved=0
lastMode =A
VlimitVal =20.00
VsetVal =10.00
AlimitVal =0.50
wasOEnabled =1
lastCurPos =3